说说 DOM1 级的节点层次以及 JavaScript 对它的支持

xiaoxiao2021-02-27  300

DOM 会将任意的 HTML 或 XML 文档描绘成由多节点构成的结构。即表现为以一个特定节点为根节点的树型结构:

比如下面这个 HTML:

<html> <head> <title>Sample Page</title> </head> <body> <p>Hello World!</p> </body> </html>

这个例子中的 <html> 元素称为文档元素,是文档最外层的元素。XML 中没有预定义的元素,所以任何元素都可能称为文档元素。

这个例子可以表示为一个层次结构:

1 Node 类型

DOM1 级定义了 Node 接口,它由 DOM 中的所有节点类型实现。在 JavaScript 中是作为 Node 类型实现的;除了 IE,其他类型的浏览器都可以访问这个类型。JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型都共享着相同的基本属性和方法。

每个节点都有 nodeType 属性,表示节点的类型,有 12 种,是用 Node 类型中定义的数值常量来表示的:

* 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){//IE 中无效 console.log("Node is an element."); }

为了跨浏览器实现,最好是跟节点类型的数值进行比较:

if (someNode.nodeType == 1){//跨浏览器 console.log("Node is an element."); }

1.1 nodeName 和 nodeValue 属性

在使用之前,最好先检测一下节点的类型:

if (someNode.nodeType == 1){ value = someNode.nodeName;//取得元素的标签名 }

对于元素节点,nodeName 表示的是元素的标签名,nodeValue 返回 null。

1.2 节点关系

节点之间的各种关系,可以用传统的家族关系来描述,把文档树比喻为家谱。

每个节点都有一个 childNodes 属性,它保存着一个 NodeList 对象。NodeList 对象是一种类数组对象,保存了一组有序的节点,可以通过位置来访问这些节点。注意,它不是 Array 的实例。 NodeList 对象是基于 DOM 结构动态查询的结果,也就是说,DOM 结构的变化会自动反映在 NodeList 对象中。

可以这样访问保存在 NodeList 中的节点:

var firstChild = someNode.childNodes[0];//方括号方式 var secondChild = someNode.childNodes.item(1);//item() var count = someNode.childNodes.length;//节点数量

可以将 NodeList 转换为数组(跨浏览器):

/** * 把 NodeList 对象转换为数组 * @param nodes * @returns {*} */ function convertToArray(nodes) { var array = null; try { array = Array.prototype.slice.call(nodes, 0);//针对非 IE 浏览器 } catch (ex) { array = new Array(); for (var i = 0, len = nodes.length; i < len; i++) {//手动枚举所有成员 array.push(nodes[i]); } } return array; }

每个节点都有 parentNode 属性,它指向文档树中的父节点。在 childNodes 列表中的所有节点都有相同的父节点。列表中的每个节点都有 previousSibling 和 nextSibling 属性,它们可以访问同一列表中的其他节点:

if (someNode.nextSibling === null){ console.log("childNodes 中最后一个节点") } else if (someNode.previousSibling === null){ console.log("childNodes 中第一个节点") }

如果列表中只有一个节点,那么它的 previousSibling 和 nextSibling 都为 null。具体关系如下图:

还有一个 hasChildNodes() 方法,如果节点包含一个或者多个子节点的情况下,它会返回 true。

节点还有一个属性是 ownerDocument,它指向整个文档的文档节点,通过它,我们可以直接访问文档节点。


注意: 所有节点类型都继承自 Node,但不是所有类型的节点都有子节点(以后会做详细说明)。


1.3 操作节点

因为关系指针都是只读的,所以 DOM 提供了一些操作节点的方法。

1.3.1 appendChild()

appendChild() ,它向 childNodes 列表的末尾添加一个节点,然后返回这个新节点:

var returnNode = someNode.appendChild(newNode); console.log(returnNode == newNode);//true console.log(someNode.lastChild == newNode);//true

如果传入到 appendChild() 方法的节点已经是文档的一部分,那么就将这个节点从原来的位置移动到新位置。因此如果把父节点中的第一个子节点传入 appendChild() 方法,那么它就会成为这个父节点的最后一个子节点:

//someNode 有多个子节点 var returnedNode = someNode.appendChild(someNode.firstChild); console.log(returnNode == someNode.firstChild);//false console.log(returnNode == someNode.lastChild);//true

1.3.2 insertBefore()

会把节点插入到参照节点的前一个同胞节点。

输入参数:

要插入的节点。作为参照的节点。

如果参照节点是 null,则功能与 appendChild() 相同。

//插入后成为最后一个子节点 returnNode = someNode.insertBefore(newNode, null); console.log(newNode == someNode.lastChild);//true //插入后成为第一个子节点 var returnNode = someNode.insertBefore(newNode, someNode.firstChild); console.log(returnNode == newNode);//true console.log(returnNode == someNode.firstChild);//true //插入到最后一个子节点前面 returnNode = someNode.insertBefore(newNode, someNode.lastChild); console.log(newNode == someNode.childNodes[someNode.childNodes.length-2]);//true

1.3.3 replaceChild()

从文档树中移除要替换的节点,把要插入的节点放在这个位置。

输入参数:

要插入的节点。要替换的节点。 //替换第一个子节点 var returnedNode = someNode.replaceChild(newNode, someNode.firstChild); //替换最后一个子节点 returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

1.3.4 removeChild()

只是移除输入的节点,但这个节点仍然为文档树所有,只是它没有了自己的位置。


注意: 以上介绍的四种方法,都是针对某个节点的子节点进行操作,所以如果在不支持子节点的节点上调用这些方法,就会抛出错误。


1.3.5 cloneNode()

创建节点的一个完全相同的副本。

它只有一个参数,是否执行深度复制:

true,深度复制,即复制节点以及整个子节点树。false, 浅复制,即只复制节点本身。

复制后的节点为文档所有,但它与文档树中的其他节点没有任何关系,除非使用操作方法把它添加到文档中。

假设原先的文档是这样的:

<ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul>

假设已经把 <ul> 元素的引用赋值给了 myList 中:

var deepList = myList.cloneNode(true);//深度复制 console.log(deepList.childNodes.length);//3 (IE < 9) 或 7(其他浏览器) var shallowList = myList.cloneNode(false);//钱复制 console.log(shallowList.childNodes.length);//0

因为 IE9 之前的版本不会为空白符创建节点,所以它统计到的值比其他浏览器来的小。


注意: cloneNode() 不会复制添加到 DOM 节点中的 JavaScript 属性(比如事件处理程序)。它只复制特性、子节点(深度复制),其他都不会被复制。IE 存在一个 bug,它会复制事件处理程序。所以我们最好在复制之前先移除上面的事件处理程序,保证跨浏览器的一致性。


1.3.6 normalize()

它用于处理文档树中的文本节点。在某个节点上调用该方法时,会删除找到的空文本节点,将找到的相邻文本节点合并为一个文本节点。

2 Document 类型

JavaScript 是通过 Document 类型来表示文档。在浏览器中,document 对象是 HTMLDocument 的一个实例,表示整个 HTML 页面。而且 document 对象是 window 对象的一个属性,所以可以作为全局对象来访问。

属性名称属性值nodeType9nodeNamedocumentnodeValuenullparentNodenullownerDocumentnull

它的子节点可能是 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction 或 Comment。

Document 类型表示 HTML 页面或者 XML 文档。它最常用于 HTMLDocument 实例的 document 对象。通过它,不仅可以取得与页面有关的信息,还可以操作页面的外观以及底层结构。


注意: 在所有浏览器(包括 IE8 以及后续版本)都可以访问 HTMLDocument 类型的构造函数和原型,但只有在 Firefox、Safari、Chrome 和 Opera 中,才能通过脚本访问 Document 类型的构造函数和原型。


2.1 文档的子节点

2.1.1 documentElement 属性

documentElement 属性会指向 HTML 页面中的 <html> 元素。比如有这样的页面:

<html> <body> </body> </html>

可以这样访问到 <html> 元素:

var html = document.documentElement;//取得 <html> 的引用 console.log(html === document.childNodes[0]);//true console.log(html === document.firstChild);//true

所有浏览器都支持。

2.1.2 body 属性

body 属性指向 <body> 元素:

var body = document.body;

所有浏览器都支持。

2.1.3 doctype 属性

用于访问 <!DOCTYPE> 标签的信息。

因为浏览器对这个属性支持的差别很大,所以它的用处很有限。

2.2 文档信息

因为 document 对象是 HTMLDocument 的实例,所以还有一些 Document 对象所没有的属性。

2.2.1 title 属性

可以取得或者修改当前页面的标题,即浏览器窗口的标题栏或者标签页上的文本。

var originalTitle = document.title;//取得标题 document.title = "New page title";//设置标题

2.2.2 与网页请求有关属性(URL、domain、referrer)

URL 属性包含页面完整的 URL 地址。domain 属性只包含页面的域名。referrer 属性保存着链接到当前页面的页面 URL 地址。如果没有来源页面,该属性为空串。

所有这些属性信息都存于请求的 HTTP 头部。

只有 domain 属性是可以设置的,但因为安全方面的原因,如果 URL 中包含一个子域名,比如map.baidu.com,那么就只能将 domain 设置为 baidu.com,而不能把这个属性设置为 URL 中不包含的域名。

当页面中包含来自其他子域框架的时候,因为跨域安全限制,它们无法通过 JavaScript 通信,这时就可以把 domain 属性设置为相同的值,这样就可以相互访问咯。

domain 属性还有一个限制:如果域名一开始设置为是大范围的,那么不能再次把它设置为小范围的。

//假设当前页面是 map.baidu.com document.domain = "baidu.com";//成功 document.domain = "map.baidu.com";//失败

所有浏览器都有这一限制。

2.3 查找元素

2.3.1 getElementById()

输入参数:要取得元素的ID。严格匹配,区分大小写。IE8 及较低版本不区分大小写。

返回:如果找到,就返回该元素;如果不存在,则返回 null。

2.3.2 getElementsByTagName()

输入参数:要取得元素的标签名。 返回:包含零或多个元素的 NodeList。在 HTML 文档中,会返回 HTMLCollection 对象,它也是一个动态获取的集合。

var images = document.getElementsByTagName("img"); console.log(images.length);//图片数量 console.log(images[0].src);//输出第一个图像元素的 src 特性 console.log(images.item(0).src);//输出第一个图像元素的 src 特性

HTMLCollection 对象还有一个 namedItem() 方法,它可以通过元素的 name 属性取得集合中的项:

<img src="myimage.gif" name="myImage"> var myImage = images.namedItem("myImage"); myImage = images["myImage"];//也可以使用方括号语法

这样可以取得文档中的所有元素,按照元素的先后顺序(因为 IE 将注释解释为元素,所以它会带上所有的注释节点):

var allElements = document.getElementsByTagName("*");

注意: 虽然标准规定标签名要区分大小写,但为了最大限度与既有的 HTML 页面兼容,传给getElementsByTagName() 的参数是无需区分大小写的。但对于 XML 文档(包括 XHTML),还是需要区分大小写。


2.3.3 getElementsByName()

只有 HTMLDocument 才有的方法。

输入参数:标签的 name 属性。 返回:带有给定 name 属性的所有元素。

最常用于单选按钮:

<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>

所有的单选按钮的 name 值都是 color,但 ID 不同,这样可以确保只有一个值被发送给服务器。

var radios = document.getElementsByName("color");

对于单选按钮来说,namedItem() 只会取得第一项的元素。

2.4 特殊集合

这些都是 HTMLDocument 对象,它们为访问文档的常用部分提供了便捷的方法:

集合方法说明document.anchors所有带有 name 属性的 <a> 元素document.applets所有 <applet> 元素。不建议使用document.forms所有 <form> 元素。即 document.getElementsByTagName(“form”)。document.images所有 <img> 元素。即 document.getElementsByTagName(“img”)。document.links所有带有 <href> 属性的 <a> 元素。

它们都会随着当前文档内容的更新而更新。

2.5 DOM 一致性检测

DOM1 级中的 document.implementation 有一个 hasFeature() 方法。

输入参数:

要检测的 DOM 的功能名称版本号

返回:如果浏览器支持给定名称和版本的功能,就会返回 true。

var hasXmlDom = document.implementation.hasFeature("XML", "1.0"); 功能版本号说明Core1.0、2.0、3.0核心 DOM,表现文档的节点数。XML1.0、2.0、3.0Core 的 XML 扩展,添加了对 CDATA、处理指令以及实体的支持HTML1.0、2.0XML 的 HTML 扩展,,添加了对 HTML特有元素以及实体的支持Views2.0基于某些样式完成文档的格式化StyleSheets2.0将样式表关联到文档CSS2.0对层叠样式表 1 级的支持CSS22.0对层叠样式表 2 级的支持Events2.0、3.0常规 DOM 事件UIEvents2.0、3.0用户界面事件MouseEvents2.0、3.0由鼠标引发的事件MutationEvents2.0、3.0DOM 树变化引发的事件HTMLEvents2.0HTML4.01 事件Range2.0用于操作 DOM 树中某个范围的对象和方法Traversal2.0遍历 DOM 树的方法LS3.0文件与 DOM 树之间的同步加载和保存LS-Async3.0文件与 DOM 树之间的异步加载和保存Validation3.0在确保有效的前提下修改 DOM 树的方法

要注意,即使 hasFeature 返回 true,也不意味着真是实现了该规范。 Safari 2.x 及更早的版本会在没有完全实现某些 DOM 功能的情况下也会返回 true。因此在使用 hasFeature() 之外,同时还能使用能力检测。

2.6 文档写入

就是将文档写入到网页中,有四种方法。

write(),接受一个字符串参数,即要写入到输出流中的文本。writeln(),与 write() 功能相同,只是它会在字符串的末尾添加一个换行符(\n)。

利用这两种方法,可以向页面动态地加入内容:

document.write("<strong>" + (new Date()).toString() + "</strong>");

也可以利用这两种方法,让页面动态包含外部的资源,比如 JavaScript 文件。但不能直接包含字符串 </script>,因为这会导致该字符串被解释为脚本块的结束,必须把它分开写,像这样:

document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");

如果在文档加载结束后,再调用 document.write(),那么输出的内容就会重写整个页面:

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>文档加载结束后再调用 document.write,会重写整个页面</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>

另外两种方法是 open() 和 close(),它们分别用于打开和关闭网页的输出流。如果在页面加载期间使用 write() 或 writeln() ,是不需要用到上面这两种方法的。


注意: 严格型 XHTML 文档以及那些按照 application/xml+xhtml 内容类型提供的页面,它们不支持文档写入。


3 Element 类型

Element 类型用于表现 XML 或 HTML 元素,它提供了对元素标签名、子节点以及特性的访问。

属性名称属性值nodeType1nodeName元素的标签名nodeValuenullparentNodeDocument 或 Element子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或 EntityReference。

可以使用 nodeName 属性或者 tagName 属性来访问元素的标签名,它们会返回相同的值。建议使用 tagName ,因为它字面表达的更清晰。

假设页面上有这样一个元素:

<div id="myDiv"></div>

可以这样取得这个元素以及标签名:

var div = document.getElementById("myDiv"); console.log(div.tagName);//DIV console.log(div.tagName == div.nodeName);//true

在 HTML 中标签名始终都是大写,在 XML(XHTML)中,标签名与源代码的一致。如果不确定脚本是将在 HTML 中还是 XML 中执行,最好在比较前先将标签名转换为相同的大小写形式:

if (element.tagName.toLowerCase() == "div"){ ... }

注意: 在 Safari2 之前的版本以及 Opera 8 之前的版本,不能访问 Element 类型的构造函数,其他浏览器都能通过脚本访问 Element 类型的构造函数及原型。


3.1 HTML 元素

每个 HTML 元素都存在下列标准特性:

属性名称属性值id元素在文档中的唯一标识符title附加说明,一般通过工具提示条显示出来lang语言代码,很少使用dir语言的方向;ltr:left-to-right,从左到右;rtl:right-to-left,从右到左,很少使用className为元素指定的 CSS 类,因为 class 是 JavaScript 的保留字,所以没有使用这一名字。

元素中的这些特性既可以取得,也可以设置:

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> <script type="text/javascript"> var div = document.getElementById("myDiv"); console.log(div.id);//myDiv console.log(div.className);//bd console.log(div.title);//Body text console.log(div.lang);//en console.log(div.dir);//ltr div.id = "someOtherId"; div.className = "ft"; div.title = "Some other text"; div.lang = "fr"; div.dir = "rtl"; </script>

对 dir 的修改,会立即影响页面中文本的左、右对齐方式。

3.2 取得特性

var div = document.getElementById("myDiv"); console.log(div.getAttribute("id"));//myDiv console.log(div.getAttribute("class"));//bd ...

注意,传递给 getAttribute() 方法的特性名必须与实际的特性名一致,比如这里传入的是 class,而不是 className。如果给定的特性不存在,会返回 null。

通过 getAttribute() 也可以取得自定义特性的值,根据 HTML5 的规范,自定义特性应该在名字前加上 data- 前缀。

只有非自定义的特性才会以属性的形式被添加到 DOM 对象中,在早期的 IE 中却会为自定义的特性创建属性,IE10 修正了过来:

<div id="myDiv" align="left" my_special_attribute="hello!"></div> <script type="text/javascript"> var div=document.getElementById("myDiv"); console.log(div.id);//myDiv console.log(div.align);//left console.log(div.my_special_attribute);//undefined(早期的 IE 除外,实测 IE10 返回的也是 undefined) </script>

有两类特殊的属性,通过属性的值与通过 getAttribute() 返回的值不相同:

属性名说明属性的值getAttribute() 返回的值style为元素指定的样式对象CSS 文本onclick点击事件处理程序JavaScript 函数JavaScript 函数的代码

因为这些差异,开发人员一般只使用对象的属性,只有为了取得自定义属性的值,才使用 getAttribute() 方法。

3.3 设置特性

getAttribute(),接受两个参数:

要设置的特性名要设置的特性值

如果特性已经存在,会用新值替换旧值;如果特性不存在,就会创建这个特性并设置它的值:

div.setAttribute("id", "someOtherId"); div.setAttribute("class", "ft"); div.setAttribute("title", "Some other text"); div.setAttribute("lang", "fr"); div.setAttribute("dir", "rtl");

setAttribute() 既可以操作 HTML 的特性,也可以操作自定义的特性。它会把特性名统一转换为小写形式。

可以直接给属性赋值,但不能直接给自定义的属性赋值:

//直接给属性赋值 div.id = "someOtherId"; div.align = "left"; //这样添加的一个自定义属性,不会自动成为元素的属性 div.mycolor = "red"; console.log(div.getAttribute("mycolor"));//null(老版本的 IE 除外,实测 IE10 返回的也是 null)

注意: 在 IE7 及之前的版本中,setAttribute() 存在异常行为。使用它来设置 class 、style 特性以及事件处理程序,没有效果,IE8修复了这些问题。


3.4 attributes 属性

Element 类型是使用 attributes 属性的唯一一个 DOM 节点类型。attributes 属性中包含一个 NamedNodeMap,也是一个 “动态” 集合。元素的每一个特性都由一个 Attr 节点表示,每个节点都保存在 NamedNodeMap 对象中。NamedNodeMap 对象拥有以下方法:

方法名称说明getNamedItem(name)返回 nodeName 属性等于 name 的节点。removeNamedItem(name)从列表中移除 nodeName 属性等于 name 的节点。setNamedItem(node)向列表中添加节点,以节点 nodeName 属性为索引。item(pos)返回位于 pos 处的节点。

节点的 nodeValue 就是特性的值,比如要取得元素的 id 特性:

var id = element.attributes.getNamedItem("id").nodeValue;

也可以使用方括号语法获取或设置值:

var id = element.attributes.["id"].nodeValue; element.attributes.["id"].nodeValue = "someOtherId";

removeNamedItem(name) 与 在元素上调用 removeAttribute() 效果相同,都是直接删除给定名称的特性。它们的区别是,removeNamedItem(name) 会返回被删除特性的 Attr 节点。

由于 attributes 属性没有之前介绍的 getAttribute()、removeAttribute() 以及 setAttribute() 来的方便,所以开发人员更喜欢使用后面的方法。

attributes 属性更多用于遍历元素的特性上。比如需要将 DOM 结构序列化为 XML 或 HTML 字符串时,就会需要遍历元素的特性:

function outputAttributes(element) { var pairs = new Array(), attrName, attrValue, i, len; for (i = 0, len = element.attributes.length; i < len; i++) { attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; pairs.push(attrName + "=\"" + attrValue + "\"");//返回 name="value" ... } return pairs.join(" "); } console.log(outputAttributes(document.getElementById("myDiv")));

这个函数使用一个数组来保存名值对,并以空格作为它的分隔符。

针对 attributes 对象中的特性,不同浏览器返回的顺序是不同的。IE7 以及早期版本会返回 HTML 元素中所有可能的特性。因此我们可以对这个函数进行改进,让它只返回指定的特性。每个特性节点都有一个 specified 属性,如果为 true,表示在 HTML 中指定了该特性,或者是通过 setAttribute() 设置了该特性,所以我们可以利用这个属性进行过滤:

function outputAttributes(element) { var pairs = new Array(), attrName, attrValue, i, len; for (i = 0, len = element.attributes.length; i < len; i++) { attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; if(element.attributes[i].specified){//确保IE7 以及更早的版本,也只返回指定的特性 pairs.push(attrName + "=\"" + attrValue + "\""); } } return pairs.join(" "); } console.log(outputAttributes(document.getElementById("myDiv")));

3.5 创建元素

document.createElement(),接受一个参数,即要创建元素的标签名。这个标签名在 HTML 文档中不区分大小写,但在 XML 文档中是区分大小写的!

var div = document.createElement("div"); //创建后,就可以操作元素的特性咯 div.id = "myDiv"; div.className = "box"; document.body.appendChild(div);//添加到文档树

在 IE 中可以使用 createElement(),可以传入完整的元素标签:

document.createElement("<div id=\"myNewDiv\" class=\"box\"></div>");

这可以解决 IE7 以及早期版本在动态创建元素上的一些问题:

不能设置动态创建的 <iframe> 元素的 name 属性。不能通过表单的 reset() 方法重设动态创建的 元素。动态创建的 type=reset 的 <button> 元素无法重设表单。动态创建的一批 name 相同的单选按钮彼此毫无联系。 if (client.browser.ie && client.browser.ie <=7){ //创建带 name 属性的 iframe 元素 var iframe = document.createElement("<iframe name=\"myframe\"></iframe>"); //创建 input 元素 var input = document.createElement("<input type=\"checkbox\">"); //创建 button 元素 var button = document.createElement("<button type=\"reset\"></button>"); //创建单选按钮 var radio1 = document.createElement("<input type=\"radio\" name=\"choice\" + "value=\"1\">"); var radio2 = document.createElement("<input type=\"radio\" name=\"choice\" + "value=\"2\">"); }

因为要用到浏览器检测,所以建议只在 IE7 以及早期版本存在上述问题的情况下再使用。

3.6 元素的子节点

元素可以有任意个数的子节点以及后代节点。这些子节点可能是元素、文本节点、注释或处理指令。

<ul id="myList> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>

IE 会认为这段代码中的 ul 拥有 3 个子节点(li)。其他浏览器则认为 ul 拥有 7 个子节(3 个 li,4 个分隔 li 的空白文本节点),所以在迭代时,需要先检测一下 nodeType 属性:

for (var i=0, len=element.childNodes.length; i < len; i++){ if (element.childNodes[i].nodeType == 1){//是元素节点 ... } }

getElementsByTagName() 会根据特定的标签名取得子节点或者后代节点,它会从当前元素开始搜索,即只会返回当前元素的后代:

var ul = document.getElementById("myList"); var items = ul.getElementsByTagName("li");//如果 ul 包含更多层次的后代元素,那么各个层次中的 li 元素也会返回

4 Text 类型

指的是村文本内容,也可以包含转义后的 HTML 字符。

属性名称属性值nodeType3nodeNametextnodeValue节点所包含的文本parentNodeElement子节点没有

有这些方法可以操作节点中的文本:

方法名说明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</div><!-- 有内容,所以有一个文本节点-->

这样访问或者修改文本节点:

<div id="deniro">message</div> <script type="text/javascript"> var div = document.getElementById("deniro"); var textNode = div.firstChild;//或者 div.childNodes[0] div.firstChild.nodeValue="Some other message"; </script>

4.1 创建文本节点

createTextNode() 创建新文本节点,它接受一个参数,即要插入节点中的文本:

var element=document.createElement("div"); element.className="message"; var textNode=document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element);

一般来说,每个元素只有一个文本子节点。不过也可以包含多个文本节点:

var element=document.createElement("div"); element.className="message"; var textNode=document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode=document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element);

如果两个文本节点是相邻的同胞节点,那么这两个节点中的文本就会连起来显示。

4.2 规范化文本节点

normalize():如果在一个包含超过一个文本节点的父节点上调用该方法,那么所有的文本节点就会合并为一个节点:

var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element); console.log(element.childNodes.length);//2 element.normalize(); console.log(element.childNodes.length);//1 console.log(element.firstChild.nodeValue);//Hello world!Yippee!

注意: 在某些情况下,在 IE6 下执行 normalize 方法会导致浏览器崩溃!IE7 修复了这一问题。


4.3 分割文本节点

splitText():会按照指定的位置,将一个文本节点分为两个。它会返回新文本节点(即分割后的第二个节点):

var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); var newNode = element.firstChild.splitText(5); console.log(element.firstChild.nodeValue);//"Hello" console.log(newNode.nodeValue);//" world!" console.log(element.childNodes.length);//2

分割文本节点是一种从文本节点中提取数据的常用方法。

5 Comment 类型

属性名称属性值nodeType8nodeNamecommentnodeValue注释的内容parentNodeDocument 或 Element子节点没有

可以通过父节点来访问注释节点:

<div id="myDiv"><!-- A comment --></div> <script type="text/javascript"> var div = document.getElementById("myDiv"); var comment = div.firstChild; console.log(comment.data);//A comment </script>

创建注释节点:

var comment = document.createComment("A comment ");

注意:浏览器不会识别位于 </html>标签之后的注释。所以如果要访问,一定要保证注释节点是 </html> 的后代。

6 CDATASection 类型

CDATASection 类型只针对 XML 文档,它继承自 Text 类型。

属性名称属性值nodeType4nodeNamecdata-sectionnodeValueCDATA 区域中的内容parentNodeDocument 或 Element子节点没有

7 DocumentType 类型

Firefox、Safari、Opera以及 Chrome 4.0 支持这个类型。

属性名称属性值nodeType10nodeNamedoctype 名称nodeValuenullparentNodeDocument子节点没有

在 DOM1 级中,只能通过解析文档代码的方式创建。它有三个属性:

属性名称说明name文档类型的名称,即出现在 <!DOCTYPE 后面的文本entitiesNamedNodeMap 对象(文档描述)notationsNamedNodeMap 对象(符号)

其实只有 name 是真正有用的。

以严格型的 HTML 4.01 的文档声明为例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01/EN" "http://www.w3.org/TR/html4/strict.dtd"> document.doctype.name;//HTML

8 DocumentFragement 类型

它在文档中没有对应的标签。它可以包含和控制节点,但不会占用额外的资源:

属性名称属性值nodeType11nodeNamedocument-fragmentnodeValuenullparentNodenull子节点可以是 Element、ProcessingInstruction、Comment、Text、CDATASection 或 EntityReference

它可以作为一个仓库,在这里保存将来会添加到文档中的节点。如果将文档中的节点添加到了文档片段中,那么就会从文档树中移除该节点。可以通过 appendChild() 或 insertBefore() 将文档片段上的所有子节点都添加到相应的位置上。利用这种技术可以一次性把多个元素添加到文档中,避免了浏览器在一个一个元素添加时的反复渲染问题:

<ul id="myList"></ul> <script type="text/javascript"> var fragment=document.createDocumentFragment(); var ul=document.getElementById("myList"); var li=null; for(var i=0;i<3;i++){ li=document.createElement("li"); li.appendChild(document.createTextNode("Item "+(i+1))); fragment.appendChild(li); } ul.appendChild(fragment); </script>

9 Attr 类型

表示的是元素的属性,所有的浏览器都可以访问 Attr 类的构造函数和原型。

属性名称属性值nodeType11nodeName特性的名称nodeValue特性的值parentNodenull子节点HTML 中没有,XML中是 Text 或 EntityReference

一般开发中很少使用这个类型。

Attr 对象有三个属性:

属性名称属性值name属性名称value属性值

spe cified | 布尔值,是在代码中指定还是默认

<div id="myDiv"></div> <script type="text/javascript"> var element = document.getElementById("myDiv"); var attr = document.createAttribute("align"); attr.value = "left"; element.setAttributeNode(attr); console.log(element.attributes["align"].value);//left console.log(element.getAttributeNode("align").value);//left console.log(element.getAttribute("align"));//left </script>

注意: 使用 getAttribute()、setAttribute() 和 removeAttribue() 操作节点的属性更方便!


转载请注明原文地址: https://www.6miu.com/read-3670.html

最新回复(0)