精通JavaScript第五章:dom
一、遍历dom
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>introduction to the dom</title>
<script type="text/javascript">1:
2: window.onload = function() {3: document.getElementById("div_").innerHTML= document.documentElement.firstChild.nextSibling.firstChild.firstChild.nodeValue;4: }
5:
</script>
</head>
<body>
<h1>介绍dom</h1>
<p class="text">
这是一个dom的p标签测试
</p>
<ul>
<li id="everywhere">it can be found everywhere</li>
<li class="test">it is easy to user.</li>
<li class="test">it can help you to find what you want, really quickly.</li>
</ul>
<div id="div_"></div>
</body>
</html>
IE和其他的浏览器有个地方不同: 该文档下,body下如果使用childNodes.length那么得到的是4,而firefox得到的是9. 因为火狐更加严格,每两个标签之间如果有换行,那么算是一个#text节点。因此遍历节点有了困难。
比如说
document.documentElement.firstChild.nextSibling.firstChild
在ie得到是<h1>,而FF得到是body和h1之间的换行(\n,其nodeType为1)
为什么document.documentElement.firstChild.nextSibling能顺利找到body呢,请看FF下html标签内的innerHTML
可以清楚的看到, head和body之间没有换行符。
解决:判断node的nodeType
function preNode(elem) {
do { elem = elem.previousSibling; } while (elem && elem.nodeType != 1)
return elem;
}
function nexNode(elem) {
do { elem = elem.nextSibling; } while (elem && elem.nodeType != 1)
return elem;
}
/*找到第一个标签子节点 首先找到第一个子节点,判断其是不是标签;如果不是,则用
nexNode一直找到其不是文本的排在它后面的第一个兄弟节点*/
function firstChildNode(elem) {
elem = elem.firstChild; return elem && elem.nodeType == 3 ? nexNode(elem) : elem;
}
/*获得这个标签内的文本(非换行) 首先找到第一个子节点, 如果是文本标签而且不是换行,
那么直接返回nodeValue; 如果不是,则用nexNode一直找到其不是文本的排在它后面的第一个兄弟节点,
再递归调用本身*/
function nodeText(elem) {
elem = elem.firstChild;
return elem && elem.nodeType == 3 && elem.nodeValue != "\n" ?
elem.nodeValue : nodeText((nexNode(elem) || elem));
}
. function first(elem) {
elem = elem.firstChild;
return elem && elem.nodeType == 1 ? elem : nexNode(elem);
}
function last(elem) {
elem = elem.lastChild;
return elem && elem.nodeType == 1 ? elem : preNode(elem);
}
function tag(tagname, elem) {
return (elem || document).getElementsByTagName(tagname);
}
function hasClass(classname, tagname) {
var r = [];
var re = new RegExp("(^|\\s)" + name + "(\\s|$)");
var e = document.getElementsByTagName(tagname || "*");
for (var j = 0; j < e.length; j++) {
if (re.test(e[j])) r.push(e[j]);
}
return r;
}
通过这几个辅助方法,可以很方便地结合html dom 内置方法跨浏览器操作dom
(路过的知识点)HTMLElement
HTMLElement 对象表示 HTML 中的一个元素。
HTMLElement 对象的属性
一个 HTML 文档中的每个元素都有和元素的 HTML 属性对应的属性。这里列出了所有 HTML 标记都支持的属性。其他的属性,都特定于某种具体的 HTML 标记。HTMLElement 对象继承了 Node 和 Element 对象的标准属性
document.getElementById("div_") instanceof HTMLElement //output true
document.getElementById("div_").constructor //output HTMLDivElement
通过上面的代码, 我们可以得出html elements 元素都是HTMLElement类型的。
那么,我们直接扩展HTMLElement类型的prototype,是不是每个元素将可以直接使用我们的扩展方法呢?
答案肯定是yes。
. HTMLElement.prototype.next=function(){
do {
elem = elem.nextSibling;
} while (elem && elem.nodeType != 1)
return elem;
}
那么document.body.first().next()将很容易地获得P元素
书上是这么写的, 也说明只有FF,Safari,opera支持。(可惜,我用任何一个浏览器都没有作用 - -)
二、判断dom何时加载完毕
浏览器的渲染和操作顺序大致如下:
1、 html解析完毕
2、 外部脚本和样式表加载完毕
3、 html dom完全构造起来
4、 图片和外部内容加载
5、 网页加载完成
因为网页头部并且从外部文件加载的脚本会在html真正构造之前执行,还不能访问并不存在的dom,所以这有个问题需要解决。
一般是等待整个页面加载完成再执行,比如window.onload=function(){}.
部分加载
伪实现。 即把js代码写在html文档的最后。(因为html文档是流形式地加载)
监听dom加载状态(如下图解释)
. function isDOMReady() {
if (domReady.done)
return false;
if (document
&& document.getElementsByTagName
&& document.getElementById
&& document.body) //关键的if判断
{
domReady.timer = null;
for (var i = 0; i < domReady.ready.length; i++)
domReady.ready[i]();
domReady.ready = null;
domReady.done = true;
clearInterval(domReady.timer);
}
}
function domReady(f) {
if (domReady.done)
return f();
if (domReady.timer) {
domReady.ready.push(f);
} else {
domReady.ready = [f];
domReady.timer = setInterval("isDOMReady()", 13);
}
}
domReady(function() {
alert('dom is loaded!');
document.getElementsByTagName("h1")[0].style.border = '4px solid red';
});
三、获得元素的内容
预备知识:
null : 表示无值;
undefined : 表示一个未声明的变量已声明但没有赋值的变量一个并不存在的对象属性。使用if (!object){}两者就都包含了
==运算符将两者看作相等。typeof运算符类型相等。===运算符 全相等(值和类型都相等)。
. function hasAttribute(elem, attrName) {
return elem.getAttribute(attrName) != null;
}
/*读取,设置属性*/
function attr(elem, attrName, attrValue) {
if (!attrName || attrName.constructor != String) return "";
attrName = { "for": "htmlFor", "class": "className"}[attrName] || attrName;
if (typeof attrValue != undefined) {
elem[attrName] = attrValue;
if (elem.setAttribute)
elem.setAttribute(attrName, attrValue);
}
return elem[attrName] || elem.getAttribute(attrName, attrValue) || "";
}
//包装为dom节点数组
function checkElem(arr) {
var ret = [];
if (!arr)
arr = "";
if (arr.constructor != Array) arr = [arr];
for (var i = 0; i < arr.length; i++) {
if (arr[i].constructor == String) {
var tempdiv = document.createElement("div");
tempdiv.innerHTML = arr[i];
for (var j = 0; j < tempdiv.childNodes.length; j++)
ret.push(tempdiv.childNodes[j]);
}
else if (arr[i].length) {
for (var j = 0; j < arr[i].length; j++)
ret.push(arr[i][j])
} else
ret.push(arr[i]);
}
return ret;
}
/* 修改dom 扩展 insertBefore和append 两个方法*/
function before(parent, before, elem) {
if (typeof elem == undefined) {
parent.appendChild(before);
}
else {
var arr = checkElem(elem);
for (var j = arr.length; j >= 0; j--)
parent.insertBefore(arr[j], before);
}
}
function append(parent, elem) {
var arr = checkElem(elem);
for (var j = 0; j < arrlength; j++)
parent.appendChild(arr[j]);
}
/* 删除dom的两个方法 */
function remove(elem) {
if (elem) elem.parentNode.removeChild(elem);
}
function empty(elem) {
if (elem) {
while (elem.childNodes && elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
}
本人在长沙, 有工作可以加我QQ4658276