javascript权威指南-脚本化文档-上
作者:gcbeen
日期:2013年11月04日
一、DOM概览
文档对象模型(DOM)表示:和操作HTML和XML文档内容的基础API。
- 树形的根部是HTMLDocument节点,它代表整个文档。
- 代表HTML元素的节点是HTMLElement节点。
- 代表文本的节点是Text节点。
- 代表注释的节点是comment节点。
二、选取文档元素
选取文档的元素的方法。
- 用指定的id属性。
var section1 = document.getElementById("section1");
function foreach(aryEle, f) { var i, len = aryEle.length; for (i = 0; i < len; i += 1) { f.call(i); } } function getElements(/* ids */) { var elements = {}; foreach(arguments, function (i) { id = arguments[i]; ele = document.getElementById(id); if (ele == null) { throw new Error("No element with id: " + id); } elements[id] = ele; }); return elements; }
低于IE 8版本的浏览器中,getElementById()对匹配元素的ID不区分大小写,而且也返回匹配name属性的元素。
- 用指定的name属性。
name属性只在少数HTML元素中有效,包括表单、表单元素、<iframe>和<img>元素。
var radiobuttons = document.getElementsByName('favorite_color');
在IE中,getElementsByName()也返回id属性匹配指定的元素。为了兼容,不要将同样的字符串同时用作名字和ID。
// 针对<form name="shipping_address">元素,得到Element对象。 var form = document.shipping_address;
文档对象自动创建的属性可以查找到元素,最好还是显示调用getElementByName()来查找它们。
- 用指定的标签名字。
// 返回NodeList对象。 var spans = document.getElementsByTagName('span'); var firstPara = document.getElementByTagName('p')[0]; // 文档中所有的元素 var allEles = document.getElementByTagName('*'); var firstParaSpans = firstPara.getElementsByTagName('span');
HTMLDocument类定义一些快捷属性来访问各种各样的节点。如:images、forms和links等属性指向行为类似只读数组的img、form和a元素(包含href属性)的集合。这些属性指代HTMLCollection对象。它们可以用元素的ID或名字来索引。
document.shipping_address; document.forms.shipping_address;
HTMLDocument也定义embeds属性,它们都是HTMLCollection类型的
anchors是非标准属性,它指代有一个name属性的a元素而并不是一个href属性。
scripts在HTML5中是标准属性,它是HTMLCollection类型的script元素的集合。
document.body是一个HTML文档的body元素,document.head是head元素。这些属性中是会定义。Document类的documentElement属性指代文档的根元素,HTML文档中,它总是指代html元素。
节点列表和HTML集合
getElementsByName()和getElementsByTagName()都返回NodeList对象,document.images和document.forms的属性为HTMLCollection对象。这些对象都是只读的类数组对象。
for (var i = 0; i < document.images.length; i += 1) { document.images[i].style.display = 'none'; } var content = Array.prototype.map.call(document.getElementsByTagName('p'), function (e) { return e.innerHTML; });
NodeList和HTMLCollection对象不是历史文档状态的一个静态快照,而是通常是实时的,而且当文档变化是它们所包含的元素列表能随之变化。
通常,NodeList和HTMLCollection的实时性非常有用。但是,如果要在迭代一个NodeList对象生成一个静态的副本:
var snaphot = Array.prototype.slice.call(nodelist, 0);
- 用指定的css类。
var warnings = document.getElementByClassName("warning"); var log = document.getElementById("log"); var fatal = log.getElementByClassName("fatal error");
- 匹配指定的css选择器。
//css选择器 #nav div .warning p[lang='fr'] *[name='x'] span.fatal.error span[lang='fr'].warning #log span #log>span body>h1:first-child div, #log
document.querySelectorAll(); // 接受包含一个css选择器的字符串参数, // 返回一个表示文档中匹配选择器的所有元素的NodeList对象,并不是实时的。 // 如果没有匹配元素返回一个空的NodeList对象。 // 如果选择符非法,querySelector()将抛出一个异常。 document.querySelector(); // 返回第一个匹配的元素。没有匹配的元素返回null。
这两个方法在Element节点中也有定义,在元素上调用时,指定的选择器仍然在整个文档中匹配,然后过滤出结果集以便它只包含指定元素的后代元素。这看起来是违反常规,因为选择器字符串能包含元素的祖先而不仅仅是上述所匹配的元素。
document.all[]已经被标准的方法(getElementById()和getElementByTagName()所取代)现在不应该使用。
document.all[0]; // 文档的第一个元素 document.all["navbar"]; // id或name为“navbar”的元素 document.all.navbar; // id或name为“navbar”的元素 document.all.tags("div"); // 文档中所有的元素 document.all.tags("p")[0]; // 文档中第一个元素
三、文档结构和遍历
Node定义了一下重要的属性。
parentNode:给节点的父节点,或者针对类似Document对象应该是null,因为它没有父节点。childNodes:只读的类数组对象(NodeList对象),它是节点的字节点的实时表示。firstChild、lastChild:该节点的字节点中的第一个和最后一个,如果该节点没有字节点则为null。nextSibling、previoursSibling:该节点的兄弟节点的前一个和后一个。这两个属性将节点之间以双链表的形式连接起来。nodeType:给节点的类型。 9:代表Document节点,1:代表Element节点,3:代表Text节点,8:代表Comment节点,11:代表DocumentFragment节点。nodeValue:Text节点或Comment节点的文本内容。nodeName:元素的标签名,以大写形式表示。示例:
document.childNodes[0].childNodes[1]; document.firstChild.firstChild.nextSibling; // 对文档的变化及其敏感。 <html><head><title>Test</title></head><body>Hello Word</body></html> // 返回 BODY <html><head><title>Test</title></head><body>Hello Word</body></html> // 返回 HEAD四、作为元素树的文档
将主要兴趣集中在文档中的元素上,将文档看作是Element对象树,忽略部分文档:Text和Comment节点。
Element对象的children属性。只包含Element对象。
Element属性。
firstElementChild,lastElementChild nextElementSibling, previousElementSibling chileElementCount:字元素的数量。逐个元素的文档遍历的API并未完全标准化。可以通过可移植的遍历函数来实现。
function parent(e, n) { n = n || 1; while (n && e) { e = e.parentNode; n -= 1; } if (!e || e.nodeType !== 1) { return null; } return e; } function _sibling(e, nextFlag) { if (nextFlag) { e = e.nextElementSibling || _getEle(e, null, _nextSibling); } else { e = e.previousElementSibling || _getEle(e, null, _prevSibling); } } function sibling(e, n) { while (n > 0) { _sibling(e, true); n -= 1; } while (n < 0) { _sibling(e, false); n += 1; } return e; } function _getEle (e, f, g) { f && f.call(e); while (e && e.nodeType !== 1) { g.call(e); } return e; } function _firstChild(e) { e = e.firstChild; } function _nextSibling(e) { e = e.firstElementChild || _getEle(e, _firstChild, function () { e = e.nextSibling; }); } function _lastChild(e) { e = e.lastChild; } function _prevSibling(e) { e = e.lastElementChild || _getEle(e, _lastChild, function () { e = e.previoursSibling; }); } function child(e, n) { if (e.children) { if (n < 0) { n += e.children.length; } if (n < 0) { return null; } return e.children[n]; } if (n >= 0) { _nextSibling(e); return sibling(e, n); } else { _prevSibling(e); return sibling(e, n + 1); } }自定义Element的方法。
Element.prototype.next = function () { return this.nextElementSibling || (function () { var sib = this.nextSibling; while (sib && sib.nodeType !== 1) { sib = sib.nextSibling; } return sib; } ()); }在不包含children属性的非IE浏览器中模拟Element.children属性。
if (!document.documentElement.children) { Element.prototype.__defineGetter__("children", function () { var kids = []; for (var c = this.firstChild; c != null; c = c.nextSibling) { if (c.nodeType === 1) { kids.push(c); } } return kids; }); }
blog comments powered by Disqus