《JavaScript高级程序设计》读书笔记 -14.1 DOM 节点层级
文档对象模型(DOM,Document Object Model)是HTML和XML文档的编程接口。DOM表示由多层节点构成的文档,通过它开发者可以添加、删除和修改页面的各个部分。脱胎于网景和微软早期的动态HTML(DHTML),DOM现在是真正跨平台、语言无关的表示和操作网页的方式。
DOM与浏览器中的HTML 网页相关,并且在JavaScript 中提供了DOM API。
14.1 节点层级
任何HTML或XML文档都可以用DOM表示为一个由节点构成的层级结构。节点分很多类型,每种类型对应着文档中不同的信息和标记,也都有自己不同的特性、数据和方法,而且与其他类型由某种关系。这些关系构成了层级,让标记可以表示为一个以特定节点为根的属性结构。
其中,document 节点表示每个文档的根节点。在这里,根节点的唯一子节点是元素,我们称之为文档元素(documentElement)。文档元素是文档最外层的元素,所有其他元素都存在于这个元素之
内。每个文档只能有一个文档元素。在HTML 页面中,文档元素始终是元素。
HTML 中的每段标记都可以表示为这个树形结构中的一个节点。元素节点表示HTML 元素,属性节点表示属性,文档类型节点表示文档类型,注释节点表示注释。DOM中总共有12 种节点类型,这些类型都继承一种基本类型。
14.1.1 Node类型
在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){
alert("Node is an element.");
}
浏览器并不支持所有节点类型。开发者最常用到的是元素节点和文本节点。
1 nodeName与nodeValue
nodeName 与nodeValue 保存着有关节点的信息。这两个属性的值完全取决于节点类型。在使用这两个属性前,最好先检测节点类型,如下所示:
if (someNode.nodeType == 1){
value = someNode.nodeName; // 会显示元素的标签名
}
在这个例子中,先检查了节点是不是元素。如果是,则将其nodeName 的值赋给一个变量。对元素而言,nodeName 始终等于元素的标签名,而nodeValue 则始终为null。
2 节点关系
文档中的所有节点都与其他节点有关系。这些关系可以形容为家族关系,相当于把文档树比作家谱。
每个节点都有一个childNodes 属性,其中包含一个NodeList 的实例。
下面的例子展示了如何使用中括号或使用item()方法访问NodeList 中的元素:
let firstChild = someNode.childNodes[0];
let secondChild = someNode.childNodes.item(1);
let count = someNode.childNodes.length;
使用ES6 的Array.from()静态方法可以把NodeList 对象转换为数组。
let arrayOfNodes = Array.from(someNode.childNodes);
每个节点都有一个parentNode 属性,指向其DOM 树中的父元素。。此外,childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点。而使用previousSibling 和nextSibling 可以在这个列表的节点间导航。
if (someNode.nextSibling === null){
alert("Last node in the parent's childNodes list.");
} else if (someNode.previousSibling === null){
alert("First node in the parent's childNodes list.");
}
最后还有一个所有节点都共享的关系。ownerDocument 属性是一个指向代表整个文档的文档节点的指针。所有节点都被创建它们(或自己所在)的文档所拥有,因为一个节点不可能同时存在于两个或者多个文档中。
3 操纵节点
因为所有关系指针都是只读的,所以DOM 又提供了一些操纵节点的方法。最常用的方法是appendChild(),用于在childNodes 列表末尾添加节点。添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点。appendChild()方法返回新添加的节点,如下所示
let returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); // true
alert(someNode.lastChild == newNode); // true
如果想把节点放到childNodes 中的特定位置而不是末尾,则可以使用insertBefore()方法。
// 作为最后一个子节点插入
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); // true
// 作为新的第一个子节点插入
returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); // true
alert(newNode == someNode.firstChild); // true
// 插入最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。
// 替换第一个子节点
let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
// 替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
要移除节点而不是替换节点,可以使用removeChild()方法。
// 删除第一个子节点
let formerFirstChild = someNode.removeChild(someNode.firstChild);
// 删除最后一个子节点
let formerLastChild = someNode.removeChild(someNode.lastChild);
4 其他方法
所有节点类型还共享了两个方法。第一个是cloneNode(),会返回与调用它的节点一模一样的节点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入true 参数时,会进行深复制,即复制节点及其整个子DOM 树。如果传入false,则只会复制调用该方法的节点。复制返回的节点属于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。可以通过appendChild()、insertBefore()或replaceChild()方法把孤儿节点添加到文档中。
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
如果myList保存着对这个<ul>元素的引用,则下列代码展示了使用cloneNode()方法的两种方式:
let deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); // 3(IE9 之前的版本)或7(其他浏览器)
let shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); // 0
注意 cloneNode()方法不会复制添加到DOM 节点的JavaScript 属性,比如事件处理程序。这个方法只复制HTML 属性,以及可选地复制子节点。
要介绍的最后一个方法是normalize()。这个方法唯一的任务就是处理文档子树中的文本节点。由于解析器实现的差异或DOM 操作等原因,可能会出现并不包含文本的文本节点,或者文本节点之间互为同胞关系。在节点上调用normalize()方法会检测这个节点的所有后代,从中搜索上述两种情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。这个方法将在本章后面进一步讨论。
14.1.2 document类型
Document 类型是JavaScript 中表示文档节点的类型。在浏览器中,文档对象document 是HTMLDocument 的实例,表示整个HTML 页面。document 是window对象的属性,因此是一个全局对象。Document类型的节点有以下特征:
子节点可以是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment 类型。
1文档子节点
子节点的快捷访问:
- documentElement属性,始终指向HTML页面中的
<html>
元素。
let html = document.documentElement; // 取得对<html>的引用
alert(html === document.childNodes[0]); // true
alert(html === document.firstChild); // true
- body 属性,直接指向
<body>
元素。
let body = document.body; // 取得对<body>的引用
所有主流浏览器都支持document.documentElement 和document.body。
Document 类型另一种可能的子节点是DocumentType。<!doctype>标签是文档中独立的部分,其信息可以通过doctype 属性(在浏览器中是document.doctype)来访问,比如:
let doctype = document.doctype; // 取得对<!doctype>的引用
2文档信息
document 作为HTMLDocument 的实例,还有一些标准Document 对象上所没有的属性。这些属性提供浏览器所加载网页的信息。
其中第一个属性是title,包含<title>
元素中的文本,通常显示在浏览器窗口或标签页的标题栏。
// 读取文档标题
let originalTitle = document.title;
// 修改文档标题
document.title = "New page title";
接下来要介绍的3 个属性是URL、domain 和referrer。其中,URL 包含当前页面的完整URL(地址栏中的URL),domain 包含页面的域名,而referrer 包含链接到当前页面的那个页面的URL。如果当前页面没有来源,则referrer 属性包含空字符串。示例 :
// 取得完整的URL
let url = document.URL;
// 取得域名
let domain = document.domain;
// 取得来源
let referrer = document.referrer;
URL 跟域名是相关的。比如,如果document.URL 是http://www.wrox.com/WileyCDA/,则document.domain 就是www.wrox.com。
在这些属性中,只有domain 属性是可以设置的。出于安全考虑,给domain 属性设置的值是有限制的。如果URL 包含子域名如p2p.wrox.com,则可以将domain 设置为"wrox.com"(URL 包含“www”时也一样,比如www.wrox.com)。不能给这个属性设置URL 中不包含的值,比如:
// 页面来自p2p.wrox.com
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!
当页面中包含来自某个不同子域的窗格(<frame>
)或内嵌窗格(<iframe>
)时,设置document.domain 是有用的。因为跨源通信存在安全隐患,所以不同子域的页面间无法通过JavaScript通信。此时,在每个页面上把document.domain 设置为相同的值,这些页面就可以访问对方的JavaScript对象了。比如,一个加载自www.wrox.com 的页面中包含一个内嵌窗格,其中的页面加载自p2p.wrox.com。这两个页面的document.domain 包含不同的字符串,内部和外部页面相互之间不能访问对方的JavaScript 对象。如果每个页面都把document.domain 设置为wrox.com,那这两个页面之间就可以通信了。
浏览器对domain 属性还有一个限制, 即这个属性一旦放松就不能再收紧。
// 页面来自p2p.wrox.com
document.domain = "wrox.com"; // 放松,成功
document.domain = "p2p.wrox.com"; // 收紧,错误!
3定位元素
使用DOM 最常见的情形可能就是获取某个或某组元素的引用,然后对它们执行某些操作。document 对象上暴露了一些方法,可以实现这些操作。getElementById()和getElementsByTagName()就是Document 类型提供的两个方法。
getElementById()方法接收一个参数,即要获取元素的ID,如果找到了则返回这个元素,如果没找到则返回null。参数ID 必须跟元素在页面中的id 属性值完全匹配,包括大小写。
<div id="myDiv">Some text</div>
可以使用如下代码取得这个元素:
let div = document.getElementById("myDiv"); // 取得对这个<div>元素的引用
但参数大小写不匹配会返回null:
let div = document.getElementById("mydiv"); // null
如果页面中存在多个具有相同ID 的元素,则getElementById()返回在文档中出现的第一个元素。
getElementsByTagName()是另一个常用来获取元素引用的方法。这个方法接收一个参数,即要获取元素的标签名,返回包含零个或多个元素的NodeList。在HTML 文档中,这个方法返回一个HTMLCollection 对象。
let images = document.getElementsByTagName("img");
取得元素的数量:
alert(images.length); // 图片数量
alert(images[0].src); // 第一张图片的src 属性
alert(images.item(0).src); // 同上
HTMLCollection 对象还有一个额外的方法namedItem(),可通过标签的name 属性取得某一项的引用。如页面中包含<img>
元素:
<img src="myimage.gif" name="myImage">
那么也可以像这样从images 中取得对这个元素的引用:
let myImage = images.namedItem("myImage");
let myImage = images["myImage"];
要取得文档中的所有元素,可以传入getElementsByTagName(),
let allElements = document.getElementsByTagName("*");
这行代码可以返回包含页面中所有元素的HTMLCollection 对象,顺序就是它们在页面中出现的顺序。
小结:item是按下标获取,nameItem是按name获取。
HTMLDocument 类型上定义的获取元素的第三个方法是getElementsByName()。方法getElementsByName()最常用于单选按钮,因为同意字段的单选按钮必须具有相同的name属性才能确保把正确的值发送给服务器,比如下面的例子:
<fieldset>
<legend>Which color do you prefer?</legend>
<ul>
<li>
<input type="radio" value="red" name="color" id="colorRed">
<label for="colorRed">Red</label>
</li>
<li>
<input type="radio" value="green" name="color" id="colorGreen">
<label for="colorGreen">Green</label>
</li>
<li>
<input type="radio" value="blue" name="color" id="colorBlue">
<label for="colorBlue">Blue</label>
</li>
</ul>
</fieldset>
下面可以取得所有单选按钮:
let radios = document.getElementsByName("color");
getElementsByName()方法也返回HTMLCollection。不过在这种情况下,namedItem()方法只会取得第一项(因为所有项的name 属性都一样)。
4特殊集合
-
document.anchors 包含文档中所有带name 属性的元素。
-
document.forms 包含文档中所有元素(与document.getElementsByTagName (“form”)
返回的结果相同)。 -
document.images 包含文档中所有元素(与document.getElementsBy TagName (“img”)
返回的结果相同)。 -
document.links 包含文档中所有带href 属性的元素。
这些特殊集合始终存在于HTMLDocument 对象上,而且与所有HTMLCollection 对象一样,其内容也会实时更新以符合当前文档的内容。
5DOM 兼容性检测
DOM Level 1 在document.implementation 上只定义了一个方法,即hasFeature()。这个方法接收两个参数:特性名称和DOM版本。
let hasXmlDom = document.implementation.hasFeature("XML", "1.0");
由于实现不一致,因此hasFeature()的返回值并不可靠。目前这个方法已经被废弃,不再建议使用。为了向后兼容,目前主流浏览器仍然支持这个方法,但无论检测什么都一律返回true。
6文档写入
document 对象有一个古老的能力,即向网页输出流中写入内容。这个能力对应4 个方法:write()、writeln()、open()和close()。其中,write()和writeln()方法都接收一个字符串参数,可以将这个字符串写入网页中。write()简单地写入文本,而writeln()还会在字符串末尾追加一个换行符(\n)。这两个方法可以用来在页面加载期间向页面中动态添加内容,如下所示:
<html>
<head>
<title>document.write() Example</title>
</head>
<body>
<p>The current date and time is:
<script type="text/javascript">
document.write("<strong>" + (new Date()).toString() + "</strong>")
</script>
</p>
</body>
</html>
write()和writeln()方法经常用于动态包含外部资源,如JavaScript 文件。在包含JavaScript 文件时,记住直接包含字符串"",因为这个字符串会被解释为脚本块的结尾,导致后面的代码不能执行。为避免出现这个问题,需要稍加修改:
<html>
<head>
<title>document.write() Example</title>
</head>
<body>
<script type="text/javascript">
document.write("<script type=\"text/javascript\" src=\"file.js\">" +
"<\/script>");
</script>
</body>
</html>
前面的例子展示了在页面渲染期间通document.write()向文档中输出内容。如果是在页面加载完之后再调write(),则输出的内容会重写整个页面,document.
<html>
<head>
<title>document.write() Example</title>
</head>
<body>
<p>This is some content that you won't get to see because it will be
overwritten.</p>
<script type="text/javascript">
window.onload = function(){
document.write("Hello world!");
};
</script>
</body>
</html>
这个例子使用了window.onload 事件处理程序,将调用document.write()的函数推迟到页面加载完毕后执行。
open()和close()方法分别用于打开和关闭网页输出流。在调用write()和writeln()时,这两个方法都不是必需的。
14.1.3 Element类型
Element 表示XML或HTML元素,对外暴露出访问元素标签名、子节点和属性的能力。
div.tagName可以获取这个元素的标签名,它实际上返回的是”DIV“而不是“div“。在HTML 中,元素标签名始终以全大写表示;在XML(包括XHTML)中,标签名始终与源代码中的大小写一致。最好将标签名转换为小写形式,以便比较:
if (element.tagName.toLowerCase() == "div"){ // 推荐,适用于所有文档
// 做点什么
}
1 HTML元素
HTMLElement直接继承Element 并增加了一些属性。下面是一些常用的属性:
- id,元素在文档中的唯一标识符;
- title,包含元素的额外信息,通常以提示条形式展示;
- lang,元素内容的语言代码(很少用);
- dir,语言的书写方向("ltr"表示从左到右,"rtl"表示从右到左,同样很少用);
- className,相当于class 属性,用于指定元素的CSS 类(因为class 是ECMAScript 关键字,
所以不能直接用这个名字)。
使用:
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
//读取
let div = document.getElementById("myDiv");
alert(div.id); // "myDiv"
alert(div.className); // "bd"
alert(div.title); // "Body text"
alert(div.lang); // "en"
alert(div.dir); // "ltr"
//修改
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";
2 取得元素
每个元素都有零个或多个属性,通常用于为元素或其内容附加更多信息。与属性相关的DOM 方法主要有3 个:getAttribute()、setAttribute()和removeAttribute()。这些方法主要用于操纵属性,包括在HTMLElement 类型上定义的属性。
let div = document.getElementById("myDiv");
alert(div.getAttribute("id")); // "myDiv"
alert(div.getAttribute("class")); // "bd"
alert(div.getAttribute("title")); // "Body text"
alert(div.getAttribute("lang")); // "en"
alert(div.getAttribute("dir")); // "ltr"
注意,属性名不区分大小写,因此"ID"和"id"被认为是同一个属性。
通过DOM对象访问的属性中有两个返回值根使用getAttribute()取得的值不一样。
-
第一个属性是style属性。在使用getAttribute()访问style 属性时,返回的是CSS 字符串;而在通过DOM对象的属性访问时,style 属性返回的是一个(CSSStyleDeclaration)对象。
-
第二个属性其实是一类,即事件处理程序(或者事件属性),比如onclick。在元素上使用事件属性时(比如onclick),属性的值是一段JavaScript 代码。
3 设置属性
div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
div.setAttribute("title", "Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir", "rtl");
在DOM对象上添加自定义属性,不会自动让它变成元素的属性。
4 attributes属性
Element类型是唯一使用attributes属性的DOM节点类型。
attributes属性最有用的场景是需要迭代元素上所有属性的时候:
function outputAttributes(element) {
let pairs = [];
for (let i = 0, len = element.attributes.length; i < len; ++i) {
const attribute = element.attributes[i];
pairs.push(`${attribute.nodeName}="${attribute.nodeValue}"`);
}
return pairs.join(" ");
}
这个函数使用数组存储每个名/值对,迭代完所有属性后,再将这些名/值对用空格拼接在一起。(这个技术常用于序列化为长字符串。
5 创建元素
可以使document.createElement()方法创建新元素。
let div = document.createElement("div");
添加属性:
div.id = "myNewDiv";
div.className = "box";
添加到文档:
document.body.appendChild(div);
6 元素后代
例子:
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul
主要有两种方法:
-
以下代码会遍历某个元素的子节点,并且只在nodeType 等于1(即Element 节点)时执行某个
操作。for (let i = 0, len = element.childNodes.length; i < len; ++i) { if (element.childNodes[i].nodeType == 1) { // 执行某个操作 } }
-
要取得某个元素的子节点和其他后代节点,可以使用元素的getElementsByTagName()方法。
let ul = document.getElementById("myList"); let items = ul.getElementsByTagName("li");
14.1.4 Text类型
Text 节点中包含的文本可以通过nodeValue 属性访问,也可以通过data 属性访问,这两个属性包含相同的值。
方法有:
-
appendData(text),向节点末尾添加文本text;
-
deleteData(offset, count),从位置offset 开始删除count 个字符;
-
insertData(offset, text),在位置offset 插入text;
-
replaceData(offset, count, text),用text 替换从位置offset 到offset + count 的
文本; -
splitText(offset),在位置offset 将当前文本节点拆分为两个文本节点;
-
substringData(offset, count),提取从位置offset 到offset + count 的文本。
还可以通过length 属性获取文本节点中包含的字符数量。
默认情况下,包含文本内容的每个元素最多只能有一个文本节点。
<!-- 没有内容,因此没有文本节点 -->
<div></div>
<!-- 有空格,因此有一个文本节点 -->
<div> </div>
<!-- 有内容,因此有一个文本节点 -->
<div>Hello World!</div>
修改文本节点会被转换成实体编码:
// 输出为"Some <strong>other</strong> message"
div.firstChild.nodeValue = "Some <strong>other</strong> message";
1 创建文本节点
document.createTextNode()可以用来创建新文本节点,它接收一个参数,即要插入节点的文本。
let element = document.createElement("div");
element.className = "message";
let textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);
这个例子首先创建了一个
一般来说一个元素只包含一个文本子节点。不过,也可以让元素包含多个文本子节点,如下面的例子所示:
let element = document.createElement("div");
element.className = "message";
let textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
let anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
在将一个文本节点作为另一个文本节点的同胞插入后,两个文本节点的文本之间不会包含空格。
2 规范化文本节点
normalize(),是在Node 类型中定义的(因此所有类型的节点上都有这个方法)。在包含两个或多个相邻文本节点的父节点上调用normalize()时,所有同胞文本节点会被合并为一个文本节点,这个文本节点的nodeValue 就等于之前所有同胞节点nodeValue 拼接在一起得到的字符串。
let element = document.createElement("div");
element.className = "message";
let textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
let anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
alert(element.childNodes.length); // 2
element.normalize();
alert(element.childNodes.length); // 1
alert(element.firstChild.nodeValue); // "Hello world!Yippee!"
浏览器在解析文档时,永远不会创建同胞文本节点。同胞文本节点只会出现在DOM 脚本生成的文档树中。
3 拆分文本节点
Text 类型定义了一个与normalize()相反的方法——splitText()。这个方法可以在指定的偏移位置拆分nodeValue,将一个文本节点拆分成两个文本节点。
let element = document.createElement("div");
element.className = "message";
let textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);
let newNode = element.firstChild.splitText(5);
alert(element.firstChild.nodeValue); // "Hello"
alert(newNode.nodeValue); // " world!"
alert(element.childNodes.length); // 2
拆分文本节点最常用于从文本节点中提取数据的DOM解析技术。
14.1.5 Comment类型
Comment 类型与Text 类型继承同一个基类(CharacterData),因此拥有除splitText()之外Text 节点所有的字符串操作方法。与Text 类型相似,注释的实际内容可以通过nodeValue 或data属性获得。
注释节点可以作为父节点的子节点来访问。
<div id="myDiv"><!-- A comment --></div>
这里的注释是
let div = document.getElementById("myDiv");
let comment = div.firstChild;
alert(comment.data); // "A comment"
可以使用document.createComment()方法创建注释节点,参数为注释文本。
14.1.6 CDATASection类型
CDATASection类型表示XML中特有的CDATA区块。
CDATA区块在XML文档中有效。
14.1.7 DocumentType类型
DocumentType 对象保存在document.doctype 属性中。DOM Level 1 规定了DocumentType 对象的3 个属性:name、entities 和notations。
<!DOCTYPE HTML PUBLIC "-// W3C// DTD HTML 4.01// EN"
"http:// www.w3.org/TR/html4/strict.dtd">
对于这个文档类型,name 属性的值是"html":
alert(document.doctype.name); // "html
14.1.8 DocumentFragment类型
在所有节点类型中,DocumentFragment 类型是唯一一个在标记中没有对应表示的类型。DOM将文档片段定义为“轻量级”文档,能够包含和操作节点,却没有完整文档那样额外的消耗。
可以使用document.createDocumentFragment()方法像下面这样创建文档片段:
let fragment = document.createDocumentFragment();
下面的HTML为例:
<ul id="myList"></ul>
假设想给这个
-
元素添加3 个列表项。如果分3 次给这个元素添加列表项,浏览器就要重新渲染3 次页面,以反映新添加的内容。为避免多次渲染,下面的代码示例使用文档片段创建了所有列表项,然后一次性将它们添加到了
- 元素:
let fragment = document.createDocumentFragment();
let ul = document.getElementById("myList");
for (let i = 0; i < 3; ++i) {
let li = document.createElement("li");
li.appendChild(document.createTextNode(`Item ${i + 1}`));
fragment.appendChild(li);
}
ul.appendChild(fragment);
这个例子先创建了一个文档片段,然后取得了
- 元素的引用。接着通过for 循环创建了3 个列表项,每一项都包含表明自己身份的文本。为此先创建
- 元素,再创建文本节点并添加到该元素。然后通过appendChild()把
- 元素添加到文档片段。循环结束后,通过把文档片段传给appendChild()将所有列表项添加到了
-
元素。此时,文档片段的子节点全部被转移到了
- 元素。
-
元素。此时,文档片段的子节点全部被转移到了
14.1.9 Attr类型
元素数据在DOM中通过Attr类型表示。Attr类型构造函数嗯哼原型在素有浏览器中都可以直接访问。
开发者更喜欢使用getAttribute()、removeAttribute()和setAttribute()方法操作属性。
Attr 对象上有3 个属性:name、value 和specified。其中,name 包含属性名(与nodeName一样),value 包含属性值(与nodeValue 一样),而specified 是一个布尔值,表示属性使用的是默认值还是被指定的值。
比如,要给元素添加align 属性,可以使用下列代码:
let attr = document.createAttribute("align");
attr.value = "left";
element.setAttributeNode(attr);
alert(element.attributes["align"].value); // "left"
alert(element.getAttributeNode("align").value); // "left"
alert(element.getAttribute("align")); // "left"