任何HTML或XML文档都可以用DOM表示为一个由节点构成的层级结构。
节点分很多类型,每种类型对应着文档中不同的信息和(或)标记,也都有自己不同的特性、数据和方法,而且与其他类型有某种关系。
这些关系构成了层级,让标记可以表示为一个以特定节点为根的树形结构。
以下面的HTML为例:
<html> <head> <title>Sample Page</title> </head> <body> <p>Hello World!</p> </body></html>
表示为层级结构:

document节点表示每个文档的根节点。
在这里,根节点的唯一子节点是元素,我们称之为文档元素(documentElement)。
文档元素是文档最外层的元素,所有其他元素都存在于这个元素之内。
每个文档只能有一个文档元素。
在HTML页面中,文档元素始终是元素。
在XML文档中,则没有这样预定义的元素,任何元素都可能成为文档元素。
HTML中的每段标记都可以表示为这个树形结构中的一个节点。
元素节点表示HTML元素,属性节点表示属性,文档类型节点表示文档类型,注释节点表示注释。
DOM中总共有12种节点类型,这些类型都继承一种基本类型。
14.1.1 Node类型
DOM Level 1描述了名为Node的接口,所有DOM节点类型都必须实现这个接口。
Node接口在JavaScript中被实现为Node类型,在除IE之外的所有浏览器中,都可以直接访问这个类型。
在JavaScript中,所有节点类型都继承Node类型,因此所有类型都共享相同的基本属性和方法。
每个节点都有nodeType属性,表示该节点的类型。
节点类型由定义在Node类型上的12个数值常量表示:
❑ Node.ELEMENT_NODE(1) ❑ Node.ATTRIBUTE_NODE(2)
❑ Node.TEXT_NODE(3) ❑ Node.CDATA_SECTION_NODE(4)
❑ Node.ENTITY_REFERENCE_NODE(5) ❑ Node.ENTITY_NODE(6)
❑ Node.PROCESSING_INSTRUCTION_NODE(7) ❑ Node.COMMENT_NODE(8)
❑ Node.DOCUMENT_NODE(9) ❑ Node.DOCUMENT_TYPE_NODE(10)
❑ Node.DOCUMENT_FRAGMENT_NODE(11) ❑ Node.NOTATION_NODE(12)
节点类型可通过与这些常量比较来确定
if (someNode.nodeType == Node.ELEMENT_NODE) { console.log('Node is an element.');}
先检查了节点是不是元素。如果是,则将其nodeName的值赋给一个变量。对元素而言,nodeName始终等于元素的标签名,而nodeValue则始终为null。
2.节点关系
文档中的所有节点都与其他节点有关系。
这些关系可以形容为家族关系,相当于把文档树比作家谱。
在HTML中,元素是元素的子元素,而元素则是元素的父元素。
元素是元素的同胞元素,因为它们有共同的父元素。
每个节点都有一个childNodes属性,其中包含一个NodeList的实例。
NodeList是一个类数组对象,用于存储可以按位置存取的有序节点。
注:NodeList并不是Array的实例,但可以使用中括号访问它的值,而且它也有length属性。
NodeList对象独特的地方在于,它其实是一个对DOM结构的查询,因此DOM结构的变化会自动地在NodeList中反映出来。
我们通常说NodeList是实时的活动对象,而不是第一次访问时所获得内容的快照。
例子展示了:如何使用中括号或使用item()方法访问NodeList中的元素:
let firstChild = someNode.childNodes[0];let secondChild = someNode.childNodes.item(1);let count = someNode.childNodes.length;
使用中括号或者item()方法都可以,但多数开发者倾向于使用中括号,因为它是一个类数组对象。
注:length属性表示那一时刻NodeList中节点的数量。
使用Array.prototype. slice()可以像前面介绍arguments时一样把NodeList对象转换为数组。
let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0);
使用ES6的Array.from()静态方法,可以替换这种笨拙的方式:
let arrayOfNodes = Array.from(someNode.childNodes);
每个节点都有一个parentNode属性,指向其DOM树中的父元素。
childNodes中的所有节点都有同一个父元素,因此它们的parentNode属性都指向同一个节点。
此外,childNodes列表中的每个节点都是同一列表中其他节点的同胞节点。使用previousSibling和nextSibling可以在这个列表的节点间导航。
这个列表中第一个节点的previousSibling属性是null,最后一个节点的nextSibling属性也是null,如下所示:
if (someNode.nextSibling === null) { console.log('父节点的最后一个子节点');} else if(someNode.previousSibling === null) { console.log('父节点的第一个子节点');}
如果childNodes中只有一个节点,则它的previousSibling和nextSibling属性都是null。
父节点和它的第一个及最后一个子节点也有专门属性:firstChild和lastChild分别指向childNodes中的第一个和最后一个子节点。
someNode.firstChild的值始终等于someNode. childNodes[0],someNode.lastChild的值始终等于someNode.childNodes[someNode. childNodes.length-1]。
如果只有一个子节点,则firstChild和lastChild指向同一个节点。
如果没有子节点,则firstChild和lastChild都是null。
上述这些节点之间的关系为在文档树的节点之间导航提供了方便。
有了这些关系,childNodes属性的作用远不止是必备属性那么简单了,原因:
利用这些关系指针,几乎可以访问到文档树中的任何节点,而这种便利性是childNodes的最大亮点。
方法:hasChildNodes(),若返回true则说明节点有一个或多个子节点。
相比查询childNodes的length属性,这个方法更方便。
最后还有一个所有节点都共享的关系。
ownerDocument属性,是一个指向代表整个文档的文档节点的指针。
所有节点都被创建它们(或自己所在)的文档所拥有,因为一个节点不可能同时存在于两个或者多个文档中。
这个属性为迅速访问文档节点提供了便利,因为无需在文档结构中逐层上溯了。
注意 虽然所有节点类型都继承了Node,但并非所有节点都有子节点。
3.操纵节点
因为所有关系指针都是只读的,所以DOM又提供了一些操纵节点的方法。
最常用的:appendChild(),用于在childNodes列表末尾添加节点。
添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点。
appendChild()方法返回新添加的节点
let returnedNode = someNode.appendChild(newNode);console.log(returnedNode == newNode); // appendChild()方法返回新添加的节点console.log(someNode.lastNode == newNode); // 添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点
如果把文档中已经存在的节点传给appendChild(),则这个节点会从之前的位置被转移到新位置。
即使DOM树通过各种关系指针维系,一个节点也不会在文档中同时出现在两个或更多个地方。
insertBefore()方法,把节点放到childNodes中的特定位置而不是末尾或开头。
接收两个参数:要插入的节点和参照节点。
调用这个方法后,要插入的节点会变成参照节点的前一个同胞节点,并被返回。
如果参照节点是null,则insertBefore()与appendChild()效果相同
// 作为最后一个子节点插入returnedNode = someNode.insertBefore(newNode, null);console.log(newNode == someNode.lastNode); //如果参照节点是null,则insertBefore()与appendChild()效果相同// 作为新的第一个子节点插入returnedNode = someNode.insertBefore(newNode, someNode.firstChild);console.log(newNode == someNode.firstChild); // 要插入的节点会变成参照节点的前一个同胞节点// 插入最后一个子节点前面returnedNode = someNode.insertBefore(newNode, someNode.lastNode);console.log(newNode == someNode.childNodes[someNode.childNodes.length - 2]); //
appendChild()和insertBefore()在插入节点时不会删除任何已有节点。
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。所有关系指针都会从被替换的节点复制过来。
要移除节点,使用removeChild()方法。这个方法接收一个参数,即要移除的节点。被移除的节点会被返回
上面介绍的4个方法都用于操纵某个节点的子元素,也就是说使用它们之前必须先取得父节点(使用前面介绍的parentNode属性)。并非所有节点类型都有子节点,如果在不支持子节点的节点上调用这些方法,则会导致抛出错误。
4.其他方法
所有节点类型还共享了两个方法。
第一个是cloneNode(),返回与调用它的节点一模一样的节点。
cloneNode()方法接收一个布尔值参数,表示是否深复制。
传入true参数时,会进行深复制,即复制节点及其整个子DOM树。
传入false,则只会复制调用该方法的节点。复制返回的节点属于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。
可以通过appendChild()、insertBefore()或replaceChild()方法把孤儿节点添加到文档中。
<ul> <li>item1</li> <li>item2</li> <li>item3</li></ul>
若myList保存着对这个
元素的引用,则下列代码展示了使用cloneNode()方法的两种方式:
let deepList = myList.cloneNode(true);console.log(deepList.childNodes.length); // 3(IE9之前的版本)或7(其他浏览器let shallowList = myList.cloneNode(false);console.log(shallowList.childNodes.length);// 0
deepList保存着myList的副本。
deepList有3个列表项,每个列表项又各自包含文本。
变量shallowList则保存着myList的浅副本,因此没有子节点。
注: cloneNode()方法不会复制添加到DOM节点的JavaScript属性,比如事件处理程序。
这个方法只复制HTML属性,以及可选地复制子节点。除此之外则一概不会复制。
IE在很长时间内会复制事件处理程序,这是一个bug,所以推荐在复制前先删除事件处理程序。
normalize(),这个方法唯一的任务是处理文档子树中的文本节点。
由于解析器实现的差异或DOM操作等原因,可能会出现并不包含文本的文本节点,或者文本节点之间互为同胞关系。
在节点上调用normalize()方法会检测这个节点的所有后代,从中搜索上述两种情形。
如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。
14.1.2 Document类型
Document类型是JavaScript中表示文档节点的类型。
在浏览器中,文档对象document是HTMLDocument的实例(HTMLDocument继承Document),表示整个HTML页面。
document是window对象的属性,因此是一个全局对象。
Document类型的节点有以下特征:
❑ nodeType等于9; ❑ nodeName值为”#document”;
❑ nodeValue值为null; ❑ parentNode值为null;
❑ ownerDocument值为null;
❑ 子节点可以是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment类型。
Document类型可以表示HTML页面或其他XML文档,但最常用的还是通过HTMLDocument的实例取得document对象。
document对象可用于获取关于页面的信息以及操纵其外观和底层结构。
1.文档子节点
虽然DOM规范规定Document节点的子节点可以是DocumentType、Element、Processing-Instruction或Comment,但也提供了两个访问子节点的快捷方式:
第一个是documentElement属性,始终指向HTML页面中的元素。
虽然document.childNodes中始终有元素,但使用documentElement属性可以更快更直接地访问该元素。
<html> <body> </body></html>
浏览器解析完这个页面之后,文档只有一个子节点,即元素。
这个元素既可以通过documentElement属性获取,也可以通过childNodes列表访问:
let html = document.documentElement; // 取得对<html>的引用console.log(html === document.childNodes[0]); // trueconsole.log(html === document.firstChild); // true
作为HTMLDocument的实例,document对象还有一个body属性,直接指向元素。
因为这个元素是开发者使用最多的元素,所以JavaScript代码中经常可以看到document.body
let body = document.body; // 取得对<body>的引用// 所有主流浏览器都支持document.documentElement和document.body
Document类型另一种可能的子节点是DocumentType。<! doctype>标签是文档中独立的部分,其信息可以通过doctype属性(在浏览器中是document.doctype)来访问
let doctype = document.doctype; // 取得对<! doctype>的引用
另外,严格来讲出现在元素外面的注释也是文档的子节点,它们的类型是Comment。
不过,由于浏览器实现不同,这些注释不一定能被识别,或者表现可能不一致
一般来说,appendChild()、removeChild()和replaceChild()方法不会用在document对象上。
因为文档类型(如果存在)是只读的,而且只能有一个Element类型的子节点(即,已经存在了)。
2.文档信息
document作为HTMLDocument的实例,还有一些标准Document对象上所没有的属性。
这些属性提供浏览器所加载网页的信息。
第一个属性是title,包含
元素中的文本,通常显示在浏览器窗口或标签页的标题栏。通过这个属性可以读写页面的标题,修改后的标题也会反映在浏览器标题栏上。<br />不过,修改title属性并不会改变<title>元素</p>
<pre><code class="lang-javascript">let originalTitle = document.title; // 读取文档标题
console.log(originalTitle);
document.title = 'New Title'; // 修改文档标题
console.log(document.title);
</code></pre>
<p>介绍<strong>3个属性:URL、domain和referrer</strong>。<br />URL包含当前页面的完整URL(地址栏中的URL)<br />domain包含页面的域名<br />referrer包含链接到当前页面的那个页面的URL。<br />如果当前页面没有来源,则referrer属性包含空字符串。<br />所有这些信息都可以在请求的HTTP头部信息中获取,只是在JavaScript中通过这几个属性暴露出来而已</p>
<pre><code class="lang-javascript">let url = document.URL; // 取得完整的URL
let domain = document.domain; // 取得域名
let referrer = document.referrer; // 取得来源
</code></pre>
<p>URL跟域名是相关的。<br />比如,如果document.URL是<a rel="nofollow" href="http://www.wrox.com/WileyCDA/">http://www.wrox.com/WileyCDA/</a>,则document.domain就是www.wrox.com。在这些属性中,<strong>只有domain属性可设置</strong>。<br />出于安全考虑,给domain属性设置的值是有限制的。如果URL包含子域名如p2p.wrox.com,则可以将domain设置为”wrox.com”(URL包含“www”时也一样,比如www.wrox.com)。<strong>不能给domain属性设置URL中不包含的值</strong></p>
<pre><code class="lang-javascript">// 页面来自p2p.wrox.com
document.domain = 'wrox.com'; // 成功
document.domain = 'nczonline.com'; // 出错!
</code></pre>
<p>当页面中包含来自某个不同子域的窗格(<frame>)或内嵌窗格(<iframe>)时,设置document.domain是有用的。<br />因为跨源通信存在安全隐患,所以不同子域的页面间无法通过JavaScript通信。<br />此时,在每个页面上把document.domain设置为相同的值,这些页面就可以访问对方的JavaScript对象了。<br />比如,一个加载自www.wrox.com的页面中包含一个内嵌窗格,其中的页面加载自p2p.wrox.com。这两个页面的document.domain包含不同的字符串,内部和外部页面相互之间不能访问对方的JavaScript对象。如果每个页面都把document.domain设置为wrox.com,那这两个页面之间就可以通信了。<br />浏览器对domain属性有一个限制,即<strong>domain属性一旦放松就不能再收紧</strong>。<br />比如,把document.domain设置为”wrox.com”之后,就不能再将其设置回”p2p.wrox.com”,后者会导致错误
<a name="DGM4h"></a></p>
<h3 id="bmxl35"><a name="bmxl35" class="reference-link"></a><span class="header-link octicon octicon-link"></span>3.定位元素</h3><p>使用DOM最常见的情形:获取某个或某组元素的引用,然后对它们执行某些操作。<br />document对象上暴露了一些方法,可以实现这些操作。<br /><strong>getElementById()</strong>和<strong>getElementsByTagName()</strong>是Document类型提供的两个方法。<br />● getElementById()方法接收一个参数,即要获取元素的ID。<br />如果找到了则返回这个元素,如果没找到则返回null。<br />参数ID必须跟元素在页面中的id属性值完全匹配,包括大小写。<br />如果页面中存在多个具有相同ID的元素,则getElementById()返回在文档中出现的第一个元素。<br />● getElementsByTagName()用来获取元素引用。接收一个参数,即要获取元素的标签名<br />返回包含零个或多个元素的NodeList。在HTML文档中,这个方法返回一个HTMLCollection对象。<br />考虑到二者都是“实时”列表,HTMLCollection与NodeList是很相似的。<br />例如,下面的代码会取得页面中所有的<img>元素并返回包含它们的HTMLCollection,返回的HTMLCollection对象保存在了变量images中。与NodeList对象一样,也可以使用中括号或item()方法从HTMLCollection取得特定的元素。而取得元素的数量同样可以通过length属性得知</p>
<pre><code class="lang-javascript">let images = document.getElementsByTagName('img');
console.log(images.length); // 图片数量
console.log(images[0].src); // 第一张图片的src属性
console.log(images.item(0).src); // 同上
</code></pre>
<p>HTMLCollection对象还有一个额外的方法namedItem(),可通过标签的name属性取得某一项的引用。<br />例如,假设页面中包含如下的<img>元素,可以用namedItem()从images中取得对这个<img>元素的引用,对于name属性的元素,还可以直接使用中括号来获取</p>
<pre><code class="lang-javascript"><img src="myImage.gif" name="myImage">
let images = document.getElementsByTagName('img');
let myImage = images.nameItem('myImage'); // 用namedItem()从images中取得对这个<img>元素的引用
let myImage = images['myInage']; // name属性的元素,还可直接使用中括号来获取
</code></pre>
<p>对HTMLCollection对象而言,中括号既可以接收数值索引,也可以接收字符串索引。而在后台,数值索引会调用item(),字符串索引会调用namedItem()。<br />要取得文档中的所有元素,可以给getElementsByTagName()传入*。在JavaScript和CSS中,*一般被认为是匹配一切的字符。<br />● 获取元素的第三个方法是<strong>getElementsByName()</strong><br />返回具有给定name属性的所有元素<br />最常用于单选按钮,因为同一字段的单选按钮必须具有相同的name属性才能确保把正确的值发送给服务器</p>
<pre><code class="lang-javascript"><input type="radio" value="red" name="color" id="colorRed">
<label for="colorRed">Red</label>
<input type="radio" name="green" name="color" id="colorGreen">
<label for="colorGreen">Green</label>
<input type="radio" name="blue" name="color" id="colorBlue">
<label for="colorBlue">Blue</label>
let radios = document.getElementsByName('color');
</code></pre>
<p>所有单选按钮都有名为”color”的name属性,但它们的ID都不一样。这是因为ID是为了匹配对应的<label>元素,而name相同是为了保证只将三个中的一个值发送给服务器。然后就可以getElementsByName()取得所有单选按钮<br />与getElementsByTagName()一样,getElementsByName()方法也返回HTMLCollection。不过在这种情况下,namedItem()方法只会取得第一项(因为所有项的name属性都一样)
<a name="LFBXu"></a></p>
<h3 id="g3m5bd"><a name="g3m5bd" class="reference-link"></a><span class="header-link octicon octicon-link"></span>4.特殊集合</h3><p>document对象上还暴露了几个特殊集合,也都是HTMLCollection的实例。<br />是访问文档中公共部分的快捷方式,列举如下:<br />❑ document.anchors包含文档中所有带name属性的<a>元素。<br />❑ document.applets包含文档中所有<applet>元素(因为<applet>元素已经不建议使用,所以这个集合已经废弃)。<br />❑ document.forms包含文档中所有<form>元素(与document.getElementsByTagName (“form”)返回的结果相同)。<br />❑ document.images包含文档中所有<img>元素(与document.getElementsByTagName (“img”)返回的结果相同)。<br />❑ document.links包含文档中所有带href属性的<a>元素。<br />这些特殊集合始终存在于HTMLDocument对象上,而且与所有HTMLCollection对象一样,其内容也会实时更新以符合当前文档的内容。
<a name="fbkA7"></a></p>
<h3 id="g0b30l"><a name="g0b30l" class="reference-link"></a><span class="header-link octicon octicon-link"></span>5.DOM兼容性检测</h3><p>由于DOM有多个Level和多个部分,因此确定浏览器实现了DOM的哪些部分是很必要的。document.implementation属性是一个对象,其中提供了与浏览器DOM实现相关的信息和能力。<br />DOM Level 1在document.implementation上只定义了一个方法,即hasFeature()<br />接收两个参数:特性名称和DOM版本。<br />如果浏览器支持指定的特性和版本,则hasFeature()方法返回true</p>
<pre><code class="lang-javascript">let hasXmlDom = document.implementation.hasFeature('XML', '1.0');
</code></pre>
<p>由于实现不一致,因此hasFeature()的返回值<strong>并不可靠</strong>。<br />目前这个方法已经被废弃,不再建议使用。为了向后兼容,目前主流浏览器仍然支持这个方法,但无论检测什么都一律返回true。
<a name="AWCyc"></a></p>
<h3 id="6sdiih"><a name="6sdiih" class="reference-link"></a><span class="header-link octicon octicon-link"></span>6.文档写入</h3><p>document对象可以向网页输出流中写入内容。<br />对应4个方法:<strong>write()、writeln()、open()和close()</strong><br />write()和writeln()方法都接收一个字符串参数,可以将这个字符串写入网页中。<br />write()简单地写入文本,而writeln()还会在字符串末尾追加一个换行符(\n)。<br />这两个方法可以用来在页面加载期间向页面中动态添加内容<br />write()和writeln()方法经常用于动态包含外部资源,如JavaScript文件。</p>
<pre><code class="lang-javascript">document.write("<script type=\"text/javascript\" src=\"file.js\">" + "</script>"); // 错误
document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>"); // 正确
</code></pre>
<p>在包含JavaScript文件时,不能像如下这样直接包含字符串”</script>“,因为这个字符串会被解释为脚本块的结尾,导致后面的代码不能执行;字符串”<\/script>”不会再匹配最外层的<script>标签,因此不会在页面中输出额外内容。<br />如果在页面加载完后再调用document.write(),则输出的内容会重写整个页面<br />使用了window.onload事件处理程序<br />open()和close()方法分别用于打开和关闭网页输出流。<br />在调用write()和writeln()时,这两个方法都不是必需的。
<a name="aH2Tm"></a></p>
<h2 id="g42eab"><a name="g42eab" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.3 Element类型</h2><p>除了Document类型,Element类型是Web开发中最常用的类型。<br />Element表示XML或HTML元素,对外暴露出访问元素标签名、子节点和属性的能力。<br />Element类型的节点具有以下特征:<br />❑ nodeType等于1; ❑ nodeName值为元素的标签名;<br />❑ nodeValue值为null; ❑ parentNode值为Document或Element对象;<br />❑ 子节点可以是Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference类型。<br />可以通过nodeName或tagName属性来获取元素的标签名。<br />这两个属性返回同样的值(添加后一个属性明显是为了不让人误会)。</p>
<pre><code class="lang-javascript">let div = document.getElementById('myDiv');
// 取得这个元素的标签名
console.log(div.tagName); // DIV
console.log(div.tagName == div.nodeName); // true
</code></pre>
<p>注:div.tagName实际上返回的是”DIV”而不是”div”。<br />在HTML中,<strong>元素标签名始终以全大写表示</strong>;<br />在XML(包括XHTML)中,标签名始终与源代码中的大小写一致。<br />如果不确定脚本是在HTML文档还是XML文档中运行,最好将标签名转换为小写形式
<a name="bTu4z"></a></p>
<h3 id="5zt1o5"><a name="5zt1o5" class="reference-link"></a><span class="header-link octicon octicon-link"></span>1.HTML元素</h3><p>所有HTML元素都通过HTMLElement类型表示,包括其直接实例和间接实例。<br />另外,HTMLElement直接继承Element并增加了一些属性。每个属性都对应下列属性之一,它们是所有HTML元素上都有的标准属性:<br />❑ id,元素在文档中的唯一标识符; <br />❑ title,包含元素的额外信息,通常以提示条形式展示;<br />❑ lang,元素内容的语言代码(很少用); <br />❑ dir,语言的书写方向(”ltr”表示从左到右,”rtl”表示从右到左,同样很少用);<br />❑ className,相当于class属性,用于指定元素的CSS类(因为class是ECMAScript关键字,所以不能直接用这个名字)。<br />所有这些都可以用来获取对应的属性值,也可以用来修改相应的值<br />并非所有这些属性的修改都会对页面产生影响。<br />比如,把id或lang改成其他值对用户是不可见的(假设没有基于这两个属性应用CSS样式),而修改title属性则只会在鼠标移到这个元素上时才会反映出来。修改dir会导致页面文本立即向左或向右对齐。修改className会立即反映应用到新类名的CSS样式(如果定义了不同的样式)。
<a name="IGOXQ"></a></p>
<h3 id="a1ugpv"><a name="a1ugpv" class="reference-link"></a><span class="header-link octicon octicon-link"></span>2.取得属性</h3><p>与属性相关的DOM方法主要有3个:<strong>getAttribute()</strong>、<strong>setAttribute()</strong>和<strong>removeAttribute()</strong>。<br />这些方法主要用于操纵属性,包括在HTMLElement类型上定义的属性。<br />注:传给getAttribute()的属性名与它们实际的属性名是一样的,要传”class”而非”className”(className是作为对象属性时才那么拼写的)。<br />如果给定的属性不存在,则getAttribute()返回null<br />getAttribute()方法也能取得不是HTML语言正式属性的自定义属性的值。<br />注:属性名不区分大小写,因此”ID”和”id”被认为是同一个属性。<br />根据HTML5规范的要求,<strong>自定义属性名应该前缀data-以方便验证</strong>。<br />元素的所有属性也可以通过相应DOM元素对象的属性来取得。<br />包括HTMLElement上定义的直接映射对应属性的5个属性,还有所有公认(非自定义)的属性也会被添加为DOM对象的属性。<br />开发者在进行DOM编程时,通常会放弃使用getAttribute(),而只使用对象属性。getAttribute()主要用于取得自定义属性的值。
<a name="m73Jb"></a></p>
<h3 id="92cj6"><a name="92cj6" class="reference-link"></a><span class="header-link octicon octicon-link"></span>3.设置属性</h3><p>与getAttribute()配套的方法是<strong>setAttribute()</strong><br />接收两个参数:要设置的属性名和属性的值。<br />如果属性已经存在,则setAttribute()会以指定的值替换原来的值;<br />如果属性不存在,则setAttribute()会以指定的值创建该属性。<br />适用于HTML属性,也适用于自定义属性。<br />另外,使用setAttribute()方法设置的属性名会规范为小写形式,因此”ID”会变成”id”。<br />因为元素属性也是DOM对象属性,所以直接给DOM对象的属性赋值也可以设置元素属性的值<br />注:在DOM对象上添加自定义属性,不会自动让它变成元素的属性。在多数浏览器中,这个属性不会自动变成元素属性</p>
<pre><code class="lang-javascript">div.mycolor = 'red';
console.log(div.getAttribute('myColor')); // null
</code></pre>
<p><strong>removeAttribute()</strong>用于从元素中删除属性。<br />不单单是清除属性的值,而是会把整个属性完全从元素中去掉<br />用得并不多,但在序列化DOM元素时可以通过它控制要包含的属性。<br />4.attributes属性<br />Element类型是唯一使用attributes属性的DOM节点类型。attributes属性包含一个NamedNodeMap实例,是一个类似NodeList的“实时”集合。<br />元素的每个属性都表示为一个Attr节点,并保存在这个NamedNodeMap对象中。<br />NamedNodeMap对象包含下列方法:<br />❑ getNamedItem(name),返回nodeName属性等于name的节点;<br />❑ removeNamedItem(name),删除nodeName属性等于name的节点;<br />❑ setNamedItem(node),向列表中添加node节点,以其nodeName为索引;<br />❑ item(pos),返回索引位置pos处的节点。<br />attributes属性中的每个节点的nodeName是对应属性的名字,nodeValue是属性的值。<br />比如,要取得元素id属性的值,可以使用以下代码:</p>
<pre><code class="lang-javascript">let id = element.attributes.getNamedItem('id').nodeValue;
// 使用中括号访问属性的简写形式
let id = element.attributes['id'].nodeValue;
</code></pre>
<p>也可以用这种语法设置属性的值,即先取得属性节点,再将其nodeValue设置为新值</p>
<pre><code class="lang-javascript">element.attributes['id'].nodeValue = 'someotherId';
</code></pre>
<p><strong>removeNamedItem()</strong>方法与元素上的removeAttribute()方法类似,也是删除指定名字的属性。<br />唯一的不同之处:removeNamedItem()返回表示被删除属性的Attr节点<br /><strong>setNamedItem()</strong>方法很少使用,它接收一个属性节点,然后给元素添加一个新属性<br />一般来说,因为使用起来更简便,通常开发者更喜欢使用getAttribute()、removeAttribute()和setAttribute()方法,而不是刚刚介绍的NamedNodeMap对象的方法。<br />attributes属性最有用的场景:需要迭代元素上所有属性时。<br />要把DOM结构序列化为XML或HTML字符串。<br />比如,以下代码能够迭代一个元素上的所有属性并以attribute1=”value1” attribute2=”value2”的形式,生成格式化字符串:</p>
<pre><code class="lang-javascript">function outputAttributes(element) {
let pairs = [];
for(let i = 0, len = element.attributs.length; i < len; ++i) {
const attribute = element.attributes[i];
pairs.push(`${attribute.nodeName} = ${attribute.nodeValue}`);
}
return pairs.join(' ');
}
</code></pre>
<p>这个函数使用数组存储每个名/值对,迭代完所有属性后,再将这些名/值对用空格拼接在一起。(这个技术常用于序列化为长字符串。)<br />函数中的for循环使用attributes.length属性迭代每个属性,将每个属性的名字和值输出为字符串。<br />不同浏览器返回的attributes中的属性顺序也可能不一样。HTML或XML代码中属性出现的顺序不一定与attributes中的顺序一致。
<a name="NwXmS"></a></p>
<h3 id="4bealu"><a name="4bealu" class="reference-link"></a><span class="header-link octicon octicon-link"></span>5.创建元素</h3><p><strong>document.createElement()</strong>方法创建新元素。<br />接收一个参数,即要创建元素的标签名。<br />在HTML文档中,标签名不区分大小写,而XML文档(包括XHTML)区分大小写。<br />要创建<div>元素,可以使用下面的代码:</p>
<pre><code class="lang-javascript">let div = document.createElement('div');
</code></pre>
<p>在新元素上设置属性只会附加信息。因为这个元素还没有添加到文档树,所以不会影响浏览器显示。<br />要把元素添加到文档树,可以使用appendChild()、insertBefore()或replaceChild()。<br />元素被添加到文档树之后,浏览器会立即将其渲染出来。之后再对这个元素所做的任何修改,都会立即在浏览器中反映出来。
<a name="zymNW"></a></p>
<h3 id="1fdqdk"><a name="1fdqdk" class="reference-link"></a><span class="header-link octicon octicon-link"></span>6.元素后代</h3><p>元素可以拥有任意多个子元素和后代元素,因为元素本身也可以是其他元素的子元素。<br />childNodes属性包含元素所有的子节点,这些子节点可能是其他元素、文本节点、注释或处理指令。<br />不同浏览器在识别这些节点时的表现有明显不同。<br />要取得某个元素的子节点和其他后代节点,可使用元素的getElementsByTagName()方法。<br />在元素上调用这个方法与在文档上调用是一样的,只不过搜索范围限制在当前元素之内,即只会返回当前元素的后代。<br />对于本节前面<ul>的例子,可以像下面这样取得其所有的<li>元素:</p>
<pre><code class="lang-javascript">let ul = document.getElementsByTagName('ul');
let items = ul.getElementsByTagName('li');
</code></pre>
<p><a name="TwLc7"></a></p>
<h2 id="2nijaw"><a name="2nijaw" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.4 Text类型</h2><p>Text节点由Text类型表示,包含按字面解释的纯文本,也可能包含转义后的HTML字符,但不含HTML代码。<br />Text类型的节点具有以下特征:<br />❑ nodeType等于3; ❑ nodeName值为”#text”;<br />❑ nodeValue值为节点中包含的文本; ❑ parentNode值为Element对象;<br />❑ 不支持子节点。<br />Text节点中包含的文本可以通过nodeValue属性访问,也可以通过data属性访问,这两个属性包含相同的值。<br />修改nodeValue或data的值,也会在另一个属性反映出来。<br />文本节点暴露了以下操作文本的方法:<br />❑ appendData(text),向节点末尾添加文本text; <br />❑ deleteData(offset, count),从位置offset开始删除count个字符;<br />❑ insertData(offset, text),在位置offset插入text;<br />❑ replaceData(offset, count, text),用text替换从位置offset到offset+count的文本;<br />❑ splitText(offset),在位置offset将当前文本节点拆分为两个文本节点;<br />❑ substringData(offset, count),提取从位置offset到offset+count的文本。<br />除了这些方法,还可以通过length属性获取文本节点中包含的字符数量。这个值等于nodeValue. length和data.length。<br />默认情况下,包含文本内容的每个元素最多只能有一个文本节点。</p>
<pre><code class="lang-javascript"><div></div> <!-- 没有内容,因为没有文本节点 -->
<div> </div> <!-- 有空格,因为有一个文本节点 -->
<div>你好,世界!</div> <!-- 有内容,因为有一个文本节点 -->
// 访问这个文本节点
let textNode = div.firstChild; // 或div.childNodes[0]
// 取得文本节点的引用后,可以像这样来修改它:
div.firstChild.nodeValue = 'some other message';
// 修改文本节点还有一点要注意,
// HTML或XML代码(取决于文档类型)会被转换成实体编码,即小于号、大于号或引号会被转义,
div.firstChild.nodeValue = 'some <strong> < strong > other < </strong>'
</code></pre>
<p>这是在将HTML字符串插入DOM文档前进行编码的有效方式。
<a name="UQ0uG"></a></p>
<h3 id="cs9yi0"><a name="cs9yi0" class="reference-link"></a><span class="header-link octicon octicon-link"></span>1.创建文本节点</h3><p>document.createTextNode()可以用来创建新文本节点,它接收一个参数,即要插入节点的文本。跟设置已有文本节点的值一样,这些要插入的文本也会应用HTML或XML编码
<a name="ORqXf"></a></p>
<h3 id="99uh6n"><a name="99uh6n" class="reference-link"></a><span class="header-link octicon octicon-link"></span>2.规范化文本节点</h3><p>DOM文档中经常出现两个相邻文本节点。<strong>normalize()可以合并相邻的文本节点</strong>,是在Node类型中定义的(因此所有类型的节点上都有这个方法)。<br />在包含两个或多个相邻文本节点的父节点上调用normalize()时,所有同胞文本节点会被合并为一个文本节点,这个文本节点的nodeValue就等于之前所有同胞节点nodeValue拼接在一起得到的字符串。
<a name="dp4bS"></a></p>
<h3 id="931uwr"><a name="931uwr" class="reference-link"></a><span class="header-link octicon octicon-link"></span>3.拆分文本节点</h3><p>Text类型定义了一个与normalize()相反的方法——<strong>splitText()</strong>,<strong>可在指定的偏移位置拆分nodeValue</strong>,将一个文本节点拆分成两个文本节点。<br />这个方法返回新的文本节点,具有与原来的文本节点相同的parentNode<br />拆分文本节点最常用于从文本节点中提取数据的DOM解析技术。
<a name="Ayh9D"></a></p>
<h2 id="5oz0vf"><a name="5oz0vf" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.5 Comment类型</h2><p>DOM中的注释通过Comment类型表示。<br />Comment类型的节点具有以下特征:<br />❑ nodeType等于8; ❑ nodeName值为”#comment”; <br />❑ nodeValue值为注释的内容; ❑ parentNode值为Document或Element对象;<br />❑ 不支持子节点。<br />Comment类型与Text类型继承同一个基类(CharacterData),因此拥有除splitText()之外Text节点所有的字符串操作方法。<br />与Text类型相似,注释的实际内容可通过nodeValue或data属性获得。<br />注释节点可以作为父节点的子节点来访问<br />注释节点很少通过JavaScrpit创建和访问,此外,浏览器不承认结束的</html>标签之后的注释。
<a name="Gav1b"></a></p>
<h2 id="4yf2uh"><a name="4yf2uh" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.6 CDATASection类型</h2><p>CDATASection类型表示XML中特有的CDATA区块。<br />CDATASection类型继承Text类型,因此拥有包括splitText()在内的所有字符串操作方法。<br />CDATASection类型的节点具有以下特征:<br />❑ nodeType等于4; ❑ nodeName值为”#cdata-section”;<br />❑ nodeValue值为CDATA区块的内容; ❑ parentNode值为Document或Element对象;<br />❑ 不支持子节点。<br />CDATA区块只在XML文档中有效,因此某些浏览器比较陈旧的版本会错误地将CDATA区块解析为Comment或Element。<br />在真正的XML文档中,可以使用document.createCDataSection()并传入节点内容来创建CDATA区块。
<a name="oENBJ"></a></p>
<h2 id="3iuxrl"><a name="3iuxrl" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.7 DocumentType类型</h2><p>DocumentType类型的节点包含文档的文档类型(doctype)信息,具有以下特征:<br />❑ nodeType等于10; ❑ nodeName值为文档类型的名称;<br />❑ nodeValue值为null; ❑ parentNode值为Document对象;<br />❑ 不支持子节点。<br />DocumentType对象在DOM Level 1中不支持动态创建,只能在解析文档代码时创建。<br />对于支持这个类型的浏览器,DocumentType对象保存在document.doctype属性中。<br />DOM Level 1规定了DocumentType对象的3个属性:name、entities和notations。<br />name是文档类型的名称,entities是这个文档类型描述的实体的NamedNodeMap,notations是这个文档类型描述的表示法的NamedNodeMap。<br />因为浏览器中的文档通常是HTML或XHTML文档类型,所以entities和notations列表为空。(这个对象只包含行内声明的文档类型。)<br />无论如何,<strong>只有name属性是有用的</strong>。这个属性包含文档类型的名称,即紧跟在<! DOCTYPE后面的那串文本。
<a name="GAtIU"></a></p>
<h2 id="4sezo1"><a name="4sezo1" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.8 DocumentFragment类型</h2><p>在所有节点类型中,DocumentFragment类型是唯一一个在标记中没有对应表示的类型。<br />DOM将文档片段定义为“轻量级”文档,能够包含和操作节点,却没有完整文档那样额外的消耗。DocumentFragment节点具有以下特征:<br />❑ nodeType等于11; ❑ nodeName值为”#document-fragment”;<br />❑ nodeValue值为null; ❑ parentNode值为null;<br />❑ 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntityReference。<br />不能直接把文档片段添加到文档。<br />相反,文档片段的作用是充当其他要被添加到文档的节点的仓库。可以使用document.createDocumentFragment()方法创建文档片段<br />文档片段从Node类型继承了所有文档类型具备的可以执行DOM操作的方法。<br />如果文档中的一个节点被添加到一个文档片段,则该节点会从文档树中移除,不会再被浏览器渲染。<br />添加到文档片段的新节点同样不属于文档树,不会被浏览器渲染。<br />可通过appendChild()或insertBefore()方法将文档片段的内容添加到文档。在把文档片段作为参数传给这些方法时,这个文档片段的所有子节点会被添加到文档中相应的位置。<br />文档片段本身永远不会被添加到文档树。
<a name="nUwXf"></a></p>
<h2 id="1povny"><a name="1povny" class="reference-link"></a><span class="header-link octicon octicon-link"></span>14.1.9 Attr类型</h2><p>元素数据在DOM中通过Attr类型表示。<br />Attr类型构造函数和原型,在所有浏览器中都可以直接访问。<br />技术上讲,属性是存在于元素attributes属性中的节点。<br />Attr节点具有以下特征:<br />❑ nodeType等于2; ❑ nodeName值为属性名;<br />❑ nodeValue值为属性值; ❑ parentNode值为null;<br />❑ 在HTML中不支持子节点; ❑ 在XML中子节点可以是Text或EntityReference。<br />属性节点尽管是节点,却不被认为是DOM文档树的一部分。<br />Attr节点很少直接被引用,通常开发者更喜欢使用getAttribute()、removeAttribute()和setAttribute()方法操作属性。<br />Attr对象上有3个属性:name、value和specified。<br />name包含属性名(与nodeName一样), value包含属性值(与nodeValue一样),specified是一个布尔值,表示属性使用的是默认值还是被指定的值。<br />可使用document.createAttribute()方法创建新的Attr节点,参数为属性名。<br />注:将属性作为节点来访问多数情况下并无必要。<br />推荐使用getAttribute()、removeAttribute()和setAttribute()方法操作属性,而不是直接操作属性节点。</p>