关于程序员:快速了解JavaScript的DOM模型

44次阅读

共计 18450 个字符,预计需要花费 47 分钟才能阅读完成。

概述

DOM 全称 Document Object Model,即文档对象模型。是 HTML 和 XML 文档的编程接口,DOM 将文档(HTML 或 XML)描绘成一个多节点形成的构造。应用 JavaScript 能够扭转文档的构造、款式和内容。

W3C DOM由以下三局部组成:

  • 外围 DOM – 针对任何结构化文档的规范模型。
  • XML DOM – 针对 XML 文档的规范模型。
  • HTML DOM – 针对 HTML 文档的规范模型。

分类

DOM 目前有三种级别:

  • DOM Level 1:1998 年 10 月成为 W3C 的举荐规范,次要定义 HTML 和 XML 文档的底层构造。
  • DOM Level 2:在 DOM1 根底上裁减了办法和属性,引入了与文档更多的交互能力。次要包含 DOM 视图、DOM 款式、DOM 事件、DOM 遍历和范畴等。
  • DOM Level 3:引入了将 XML 文档加载和序列化的办法。提供了验证文档有效性的能力。

节点树

以上面的 HTML 为例:

<html>
<head>
    <meta charset="UTF-8">
    <title> 节点树 </title>
</head>
<body>
    <div> 测试块 </div>
    <a href="/about"> 链接 </a>
</body>
</html>

浏览器会将 HTML 文档解析成节点并组成节点树。

HTML DOM 中通过不同类型节点来示意,Document是每个文档的根节点。这里的 document 只有一个 <html> 子节点,称之为文档元素 (Element)。Element 示意元素类型的节点,Text示意文本类型的节点。

节点类型

DOM 的最小组成单位叫做节点(node)。下面的节点树中,每一段都由不同类型的节点组成。节点的类型有如下几种:

  • Node:浏览器提供的原生节点,上面的节点都继承它。
  • Document:整个文档树的顶层节点
  • DocumentTypedoctype标签(比方<!DOCTYPE html>)。
  • Element:网页的各种 HTML 标签(比方 <body><a> 等)。
  • Attr:网页元素的属性(比方class="right")。
  • Text:标签之间或标签蕴含的文本。
  • Comment:正文。
  • CDATASectionCDATA区域,与 Comment 类型相似。
  • DocumentFragment:文档的片段。

Node

DOM1 中定义了一个 Node 接口,JavaScript 中所有节点类型都继承自 Node 类型,因而属性和办法都雷同。

Node 类型中定义了 nodeType 属性来表明节点的类型,由 12 个常量示意。任何节点必居其一:

  • Node.ELEMENT_NODE:数值 1,对应元素节点Element
  • Node.ATTRIBUTE_NODE:数值 2,对应属性节点Attr
  • Node.TEXT_NODE:数值 3,对应文本节点Text
  • Node.CDATA_SECTION_NODE:数值 4,对应文档中 CDATA 部(不会由解析器解析的文本)。
  • Node.ENTITY_REFERENCE_NODE:数值 5,对应实体援用。
  • Node.ENTITY_NODE:数值 6,对应实体类型Entity
  • Node.PROCESSING_INSTRUCTION_NODE:数值 7,对应解决指令。
  • Node.COMMENT_NODE:数值 8,对应正文节点Comment
  • Node.DOCUMENT_NODE:数值 9,对应文档节点Document
  • Node.DOCUMENT_TYPE_NODE:数值 10,对应文档类型节点DocumentType
  • Node.DOCUMENT_FRAGMENT_NODE:数值 11,对应文档片段节点DocumentFragment
  • Node.NOTATION_NODE:数值 12,对应 DTD 中申明的符号。

应用 nodeType 能够很容易确定节点类型,IE中没有公开 Node 类型的构造函数,应用 Node.ELEMENT_NODE 比拟会导致谬误。为了兼容IE,能够应用数值进行比拟:

if (onenode.nodeType == Node.ELEMENT_NODE) {    // 在 IE 中有效
    console.log("The Node is an Element.");
}
// 或者
if (oneNode.nodeType == 1) {    // 实用所有浏览器
    console.log("The Node is an Element.");
}

nodeName返回节点的标签名

var div = document.getElementByTagName('div')[0];
console.log(onenode.nodeName);    // DIV

nodeValue返回字符串,示意节点的文本值,可读写。

var div = document.getElementByTagName('div')[0];
console.log(onenode.nodeValue);    // null

节点关系

文档中节点都存在着肯定的关系,节点之间的关系能够应用相似人类家族关系的模式形容。如,在 HTML 文档中,能够把 <html> 看作是 <body> 的父元素;绝对的,<body>也就是 <html> 的子元素;而作为 <body> 同级的 <head> 两者之间的关系为兄弟(姐妹)关系。

Node中提供了几种节点遍历的属性:parentNodechildNodesfirstNode/lastNodepreviousSibling/nextSiblingownerDocument

Document

JavaScript 通过 Document 类型示意文档。浏览器中的 document 对象是 HTMLDocument 的一个实例,示意整个 HTML 页面。Document节点具备以下特色:

  • nodeTypeNode.DOCUMENT_NODE,值为 9。
  • nodeName的值为#document
  • nodeValue的值为null
  • parentNode的值为null
  • ownerDocument的值为null
  • 其子结点可能是一个 DocumentTypeElementProcessingInstructionComment

document对象还有一些属性来示意网页的一些信息:

  • title:获得以后页面的题目,也能够批改以后页面的题目并反映在浏览器的标题栏中。但不会扭转 <title> 元素。
  • URL:蕴含页面残缺的URL
  • domain:只蕴含页面的域名。
  • referrer:保留着链接到以后页面的那个页面的URL。没有起源页面的状况下,可能蕴含空字符串。

URLdomain 属性是互相关联的。

Element

Element 类型用于体现 XML 或 HTML 元素,提供对元素标签名、子节点及个性的拜访。例如 <body><div>等。Element节点具备以下特色:

  • nodeTypeNode.ELEMENT_NODE,值为 1。
  • nodeName的值为元素的标签名。
  • nodeValue的值为null
  • parentNode可能是 DocumentElement
  • 其子节点可能是 ElementTextCommentProcessingInstructionCDATASectionEntityReference

要拜访元素的标签名,能够应用 nodeName 属性,也能够应用 tagName 属性;这两个属性会返回雷同值。

获取 <div id="divId"></div> 的标签名:

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

个性和属性

所有 HTML 元素都由 HTMLElement 类型示意,不能间接通过这个类型,也是通过它的子类型来示意。HTMLElement类型间接继承自 Element 并增加了一些属性。每个 HTML 元素中的个性(例如 <div id="d1" title="附加信息" lang="en" class="container"> 中的 idclass 等)会主动变成 DOM 对象的属性(class个性与 className 属性对应)。这些都能够通过 div.id 等获取并赋值。

一个元素中的 id 等是规范的个性,但也有非标准的个性不能应用 div.id 形式获取。那么要用什么办法来拜访非个性。

DOM次要提供了几个办法用于对任何个性进行操作,别离是:

  • hasAttribute(qualifiedName: string),查看 qualifiedName 这个个性是否存在。
  • getAttribute(qualifiedName: string),获取 qualifiedName 这个个性值,如果不存在,则返回null
  • setAttribute(qualifiedName: string, value: string),设置 qualifiedName 这个个性的值为value。设置的个性名会被批准转换为小写模式。
  • removeAttribute(qualifiedName: string),移除 qualifiedName 这个个性。
<div id="d1" title="附加信息" lang="en" class="container main" plug-add="增加的非标准的个性">

以下面的 HTML 为例,应用这几种办法。

var div = document.getElementById('d1');
div.getAttribute('title');    // 附加信息
div.hasAttribute('plug-add'); // true
div.setAttribute('title', '批改附加信息');
div.removeAttribute('plug-add');

有两类非凡的个性,虽有对应的属性名,但属性的值与 getAttribute() 返回的值并不相同。

第一类个性就是 style,用于通过 CSS 为元素指定款式。通过getAttribute() 返回的 style 中蕴含的是 CSS 文本,而通过属性返回的是一个对象。

第二类个性是 onclick 这样的事件处理。如果通过 getAttribute() 返回的是相应代码的字符串。而拜访 onclick 属性返回的是 JavaScript 函数(如果未指定相应个性,返回的是null)。

attributes 属性

Element类型的 attributes 属性返回该元素所有属性节点的一个汇合,该汇合是一个 ” 动静 ” 的 NamedNodeMap 对象。NamedNodeMap对象领有下列办法。

  • getNamedItem(name):返回 nodeName 属性等于 name 的节点。
  • removeNamedItem(name):从列表中移除 nodeName 属性等于 name 的节点。
  • setNamedItem(node):向列表中增加节点,以节点的 nodeName 属性为索引。
  • item(pos):返回位于数字 pos 地位处的节点。

attributes属性蕴含一系列节点,在节点中 节点名称 -nodeName 节点值 -nodeValue

var id = element.attributes.getNamedItem('id').nodeValue;
// 方括号语法
var id = element.attributes['id'].nodeValue;
// 属性名援用
var id = element.attributes.id;
// 如果晓得个性名所在的下标,也能够应用下标援用,假如 id 个性名所在下标为 0.
var id = element.attributes[0];

removeNamedItem() 办法与 removeAttribute() 办法都是将给定名称的个性删除,惟一区别就是 removeAttribute() 没有返回值,removeNamedItem()返回被删除个性的 Attr 节点。

var oldAttr = element.attributes.removeNamedItem('id');

setNamedItem()办法为 Element 增加一个新个性:

element.attributes.setNamedItem(newAttr);

个别状况下 getAttribute()removeAttribute()setAttribute()办法就够应用了,但想要遍历元素的个性,attributes属性倒是比拟不便。上面展现如果迭代元素中每一个个性并将它们以 name="value" name="value" 这样的字符串格局。

function listAttributes(element) {var pairs = new Array(),
        attrName,
        attrValue,
        i,
        len;
    if (element.hasAttributes()) {
        var attrs = element.attributes;
        for (i = 0, len = element.attributes.length; i < len; i++) {attrName = attrs[i].nodeName;
            attrValue = attrs[i].nodeValue;
            pairs.push(attrName + "=\"" + attrValue + "\"");
        }
    }
    return pairs.join(" ");
}

classList

className属性用于操作类名,但 className 是一个字符串,批改后要设置整个字符串的值。

HTML5 扩大了 classList 属性实现类名的操作。该属性返回 DOMTokenList 汇合。定义了几个办法:

  • add(value):增加字符串到列表中。如果存在就不增加。
  • contains(value):指定值是否存在于列表中,存在则为true,否则为false
  • remove(value):从列表中删除指定值。
  • toggle(value):列表中存在指定值,删除它;没有给定值,增加它。

Attr

Attr类型在 DOM 示意元素个性。个性是位于元素 attributes 属性中的节点。具备下列特色:

  • nodeTypeNode.TEXT_NODE,值为 3。
  • nodeName的值是个性的名称。
  • nodeValue的值是个性的名称。
  • parentNode的值为null
  • 在 HTML 中不反对子节点(没有子节点)。
  • 在 XML 中子节点能够是 TextEntityReference

个性节点不被认为是 DOM 文档树的一部分。最常应用 getAttrubute()setAttribute()removeAttribute()办法,很少间接援用个性节点。

Attr对象有 3 个属性:

  • name,个性名称,与 nodeName 的值雷同。
  • value,个性值,与 nodeValue 的值雷同。
  • specified,布尔值,用于辨别个性在代码中是指定的还是默认的。

如果要为元素增加个性,能够应用 document.createAttribute(localName) 办法,创立名为 localName 的个性节点。例如,要为元素增加 align 个性,能够应用下列代码:

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

Text

文本节点由 Text 类型示意,蕴含的是能够照字面解释的纯文本内容。纯文本中能够蕴含本义后的 HTML 字符,但不能蕴含 HTML 代码。Text节点具备以下特色:

  • nodeTypeNode.TEXT_NODE,值为 3。
  • nodeName的值为#text
  • nodeValue的值为节点所蕴含的文本。
  • parentNode是一个Element
  • 不反对子节点(没有子节点)。

能够通过 nodeValue 属性或 data 属性拜访 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 属性,保留着节点中字符的数目。而且,nodeValue.lengthdata.length 中也保留着同样的值。

批改文本节点的后果会立刻失去反映。因而字符串会通过 HTML(或 XML,取决于文档类型)编码。

应用 document.createTextNode() 能够创立文本节点,在 DOM 创立 中会讲述它。

Comment

正文在 DOM 中是通过 Comment 类型来示意的。Comment节点具备下列特色:

  • nodeType为 Node.COMMENT_NODE,数值为 8。
  • nodeName的值为#comment
  • nodeValue的值是正文的内容。
  • parentNode可能是 DocumentElement
  • 不反对子节点(没有子节点)。

Comment类型与 Text 类型继承自雷同的基类,因而它领有除 splitText() 之外的所有字符串操作方法。与 Text 类型类似,也能够通过 nodeValuedata属性获得正文的内容。

获取 <div id="divId"><!--A comment--></div> 代码中的正文:

var div = document.getElementById("divId");
var comment = div.firstChild;
console.log(comment.data);    // A comment

如果想创立正文节点,能够应用 document.createComment(data) 办法创立。

var comment = document.createComment("Create a comment node");

浏览器不会辨认位于 </html> 标签前面的正文。肯定保障拜访的正文节点位于 <html></html> 之间。

CDATASection

CDATASection类型只针对基于 XML 文档,示意的是 CDATA 区域。与 Comment 相似,CDATASection类型继承自 Text 类型,因而领有除 splitText() 之外的所有字符串操作方法。CDATASection节点具备以下特色:

  • nodeTypeCDATA_SECTION_NODE,值为 4。
  • nodeName的值为#cdata-section
  • nodeValue的值是 CDATA 区域中的内容。
  • parentNode可能是 DocumentElement
  • 不反对子节点(没有子节点)。

CDATA区域只会呈现在 XML 文档中,因而少数浏览器都会把 CDATA 区域谬误地解析为 CommentElement。以上面的代码为例:

<div id="divId"><![CDATA[This is content.]]></div>

这个例子中的 <div> 元素应该蕴含一个 CDATASection 节点。可是,四大支流浏览器无一可能这样解析它。即便对于无效的 XHTML 页面,浏览器也没有正确地反对嵌入的 CDATA 区域。

在真正的 XML 文档中,能够应用 document.createCDataSection() 来创立 CDATA 区域,只需为其传入节点的内容即可。

DocumentType

DocumentType类型在 Web 浏览器中并不罕用。DocumentType蕴含着与文档无关的 doctype 无关的所有信息,它具备下列特色:

  • nodeTypeNode.DOCUMENT_TYPE_NODE,值为 10。
  • nodeName的值为 doctype 的名称。
  • nodeValue的值为null
  • parentNodeDocument 类型。
  • 没有子节点。

DOM 1 级 规定的 DocumentType 对象不能动态创建,只通过解析文档代码的形式来创立。反对 DocumentType 的浏览器会把它保留在 document.doctype 中。

DocumentType对象在 DOM 1 级 中有 3 个属性:

  • DocumentType.name,文档类型的名称。
  • DocumentType.entities,文档类型形容的实体 NamedNodeMap 对象。
  • DocumentType.notations,文档类型形容的符号 NamedNodeMap 对象。

浏览器中个别是 HTMLXHTML类型的文档。所以 entitiesnotations都是空列表。只有 name 属性有用。

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

在这里,DocumentType中的 name 属性保留的就是HTML

console.log(document.doctype.name);    // HTML

DocumentFragment

DocumentFragment是文档片段,一种 ” 轻量级 ” 文档,能够蕴含和管制节点,但不像残缺文档那样占用额定资源。能够将它作为 ” 仓库 ” 应用。具备下列特色:

  • nodeTypeNode.DOCUMENT_FRAGMENT_NODE,值为 11。
  • nodeName的值为#document-fragment
  • nodeValue的值为null
  • parentNode的值为null
  • 子节点能够是 ElementProcessingInstructionCommentTextCDATASectionEntityReference

应用 document.createDocumentFragment() 办法创立文档片段,如下所示:

var fragment = document.createDocumentFragment();

继承了 Node 的所有办法,用于执行针对文档的 DOM 操作。能够通过 appendChild()insertBefore()增加到文档中相应地位,但自身不会成为文档树的一部分。来看上面的 HTML 示例代码:

<ul id="ulId"></ul>

咱们给 <ul id=ulId></ul> 增加 3 个列表项。能够应用文档片段来保留创立的列表项,一次性将它们增加到文档中,防止浏览器重复渲染。

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

DOM 查找

当初有一段 html 页面模板

...
<style>
    .container {
        background-color: blue;
        width: 55%;
        height: 55%;
    }
</style>
...

<div class="container" id="divId1">Div Text One</div>
<p class="container" id="pId1">P Text One</p>
<a class="container" id="aId1">A Text One</a>
<div class="container" id="divId2">Div Text Two</div>
<form id="primary-form" action="#" method="get">
    <p>UserName: <input type="text" name="input-name"></p>
    <p class="container">NickName: <input type="text" name="input-name"></p>
    <p>Email: <input type="text" name="input-email"></p>
    <input type="submit" value="Submit">
</form>
...

在该模板中,咱们想要获取这些标签元素,能够应用 document 对象获取的几种办法:

id 选择器

getElementById(elementId: string): HTMLElement | null;

该办法返回匹配指定 id 属性的元素节点,如果不存在,则返回null

上面通过 id 选择器来获取 idaId1的元素:

let div = document.getElementById("aId1");
console.log(div);    // <a class="container" id="aId1">A Text One</a>

留神:严格匹配,包含大小写。如果写成document.getElementById("aid1"),输入为null

标签选择器

getElementsByTagName(qualifiedName: string): HTMLCollectionOf<Element>;

该办法返回匹配指定 HTML 标签名的元素列表。返回的是一个相似数组对象(HTMLCollection实例),能够实时反映 HTML 文档的变动,如果不存在,则返回null

let inputs = document.getElementsByTagName('input');
console.log(inputs);    /* HTMLCollection(4) [input, input, input, input, input-name: input, email: input]*/

这个对象能够应用 length 属性获取元素数量,应用数组语法或 item() 办法来拜访 HTMLCollection 对象中的项。

inputs.length;                // 输入 p 标签的数量
inputs[0].id;                  // 输入 p 标签汇合中第一个元素的 id 个性的名称
inputs.item(0).className;      // 输入 p 标签接种中第一个元素的 class 个性的名称

还能够通过 namedItem() 办法依据元素的 name 个性获取汇合中的项。

var nameOfInput = inputs.namedItem("input-name");

也能够应用方括号语法来拜访:

var nameOfInput = inputs["input-name"];

要想获得文档中的所有元素,能够向 getElementsByTagName()中传入"*",示意 ” 全副 ”。

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

name 选择器

getElementsByName(elementName: string): NodeListOf<HTMLElement>;

该办法返回匹配 name 属性的所有元素,返回值是NodeList,如果不存在,则返回null

var names = document.getElementsByName("input-name");
console.log(names);

留神,这个选择器在不同浏览器的成果是不同的,比方在 IE 和 Opera 浏览器下,这个办法也会返回 id 属性为这个值的元素。在应用的时候,应该小心应用,尽量保障 name 不和其它元素的 id 统一。

类选择器

getElementsByClassName(classNames: string): HTMLCollectionOf<Element>;是 HTML5 中增加的办法。

该办法返回匹配 class 属性的所有元素,返回值是HTMLCollection,如果不存在,则返回null

// 获取所有 class 中同时蕴含 'red' 和 'test' 的元素
var classes = document.getElementsByClassName("container");
console.log(classes);

能够接管蕴含一个或多个类名的字符串,传入的多个类名的先后顺序不重要。

Selectors API

W3C 发动指定的规范,可使浏览器反对 CSS 查问。Selectors API 的外围是两个办法:querySelector()querySelectorAll()。兼容的浏览器中通过DocumentElement节点类型的实例进行调用。

querySelector()办法

querySelector<E extends Element = Element>(selectors: string): E | null;

该办法返回匹配指定选择符的第一个 HTMLElement 元素,如果不存在,则返回 null。传入的selectors 必须是无效的 CSS 选择器;如果选择器不非法,会引发 SYNTAX_ERR 异样。

document.querySelector("#aId1");    // 获得 Id 为 "aId1" 的元素
document.querySelector("p");    // 获得 p 元素
document.querySelector(".container");    // 获得类为 "container" 的第一个元素
document.querySelector("..selector");    // 引发 'SYNTAX_ERR' 异样(Uncaught DOMException:Failed to execute 'querySelector on'Document':'..selector'is not a valid selector. 意思是'..selector' 不是一个无效的抉择。)

DocumentElement 都能够调用 querySelector() 办法,只是 Document 会在文档元素的范畴内查找匹配的元素;Element只会在该元素后辈元素的范畴内查找匹配的元素。

querySelectorAll()办法

querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;

该办法返回匹配指定选择符的元素列表,返回的对象是 NodeList,如果不存在,则返回空的NodeList。传入的selectors 必须是无效的 CSS 选择器;如果选择器不非法,会引发 SYNTAX_ERR 异样。

// 获取 id 为 "primary-form" 中所有 <p> 元素
document.getElementById("primary-form").querySelectorAll("p");
// 获取类为 "container" 的所有元素
document.querySelectorAll(".container");
// 获取所有 <form> 元素中的所有 <p> 元素
document.querySelectorAll("form p");

matches()办法

Selectors API Level 2 标准为 Element 类型新增了一个办法:

matches(selectors: string): boolean;

该办法判断以后 DOM 节点是否能齐全匹配指定选择符,如果匹配胜利,返回true;匹配失败,返回false

var elems = document.getElementsByTagName('p');
for (var i = 0; i < elems.length; i++) {
    // 获取匹配 'container' 类选择符的 dom 节点 
    if (elems.item(i).matches('.container')) {console.log('The' + elems.item(i).textContent + 'is container');
    }
}
/*The P Text One is container
The NickName:  is container*/

留神,有些供应商会有本人实验性办法在 matchesSelector() 办法之前加些前缀。如果想应用这种办法,能够编写一个包装函数。

function matchesSelector(element, selector){if (element.matches) {
        // 规范办法
        return element.matches(selector);
    } else if (element.matchesSelector){return element.matchesSelector(selector);
    } else if (element.msMatchesSelector){      // IE 9+ 反对
        return element.msMatchesSelector(selector);
    } else if (element.mozMatchesSelector){     // Firefox 3.6+ 反对 
        return element.mozMatchesSelector(selector);
    } else if (element.webkitMatchesSelector){  // Safari 5+ 和 Chrome 反对
        return element.webkitMatchesSelector(selector);
    } else {throw new Error("Not supported.");
    }
}
if (matchesSelector(document.body, ".container")){// 执行操作}

当有的浏览器不反对 Element.matches()Element.matchesSelector(),但反对 document.querySelectorAll() 办法,能够有代替计划:

if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
}

DOM 遍历

Node节点提供了几种属性,用于拜访 DOM 节点。

parentNode

node.parentNode属性用于返回指定节点的父节点。除 document 外,所有节点都有父节点,document对象的父节点为null。示例如下:

document.getElementById('divId2').parentNode;

childNodes

node.childNodes属性用于返回指定节点的子结点的 Node 对象汇合。示例如下:

document.getElementById('primary-form').childNodes;

firstChild/lastChild

node.firstChild属性用于拜访第一个子节点;node.lastChild属性用于拜访最初一个子节点。如果要拜访的节点不存在,则返回null。示例如下:

document.getElementById('primary-form').firstChild
document.getElementById('primary-form').lastChild;

previousSibling/nextSibling

node.previousSibling属性用于拜访之前的同级节点;node.nextSibling属性用于拜访之后的同级节点。具备雷同父节点为同级节点,之前或之后示意它们在文档中呈现的程序。实例如下:

document.getElementById('divId2').previousSibling;
document.getElementById('divId2').nextSibling;

ownerDocument

node.ownerDocument属性用于返回元素的根节点,即:文档对象(Document)。通过这个属性,咱们可能间接拜访根节点而不用层层遍历。实例如下:

document.getElementById('divId2').ownerDocument;

Element Traversal

Element Traversal API 为 DOM 元素增加了以下 5 个属性。

  • childElementCount:返回子元素(不包含文本节点和正文)的格局。
  • firstElementChild:指向第一个子元素;firstChild的元素版。
  • lastElementChild:指向最初一个子元素;lastChild的元素版。
  • previousElementSibling:指向前一个同辈元素;previousSibling的元素版。
  • nextElementSibling:指向后一个同辈元素;nextSibling的元素版。

反对的浏览器为 DOM 元素增加了这些属性,利用这些元素不用放心空白文档节点,从而能够更不便地查找 DOM 元素。

DOM 操作

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

appendChild()

node.appendChild(newChild)办法用于向 childNodes 列表的开端增加一个节点并返回新增的节点。

var returnedNode = div.appendChild(newNode);

insertBefore()

node.insertBefore(newChild, refChild)办法会在指定的参照节点 refChild 之前插入新节点 newChild。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被办法返回。如果refChildnull,则 insertBefore()appendChild()执行雷同的操作。

var returnedNode = div.insertBefore(newNode, div.lastChild);

replaceChild()

node.replaceChild(newChild, oldChild)办法将要替换的节点 oldChild 移除,并将要插入的节点 newChild 插入并占据其地位。实例如下:

document.getElementById('divId2').replaceChild(newnode, oldnode);

在应用 replaceChild() 插入一个节点时,该节点的所有关系指针都会从被它替换的节点复制过去。被替换的节点依然还在文档中,但它在文档中曾经没有本人的地位了。

removeChild()

而如果只想移除而非替换节点,能够应用 node.removeChild(oldChild) 办法,该办法将要移除的节点 oldChild 移除,并返回移除的节点。

var removedNode = node.removeChild(node.firstChild);

与应用 replaceChild() 办法一样,通过 removeChild() 移除的节点依然为文档所有,只不过在文档中曾经没有了本人的地位。

下面介绍的四种办法操作的都是某个节点的子节点,要应用这几个办法必须先获得父节点。另外,并不是所有类型的节点都有子节点,如果在不反对子节点的节点上调用这些办法,将会导致谬误产生。

DOM 创立

DOM 节点创立最罕用的便是 document.createElement()document.createTextNode()办法。

createElement()

document.createElement(tagName)办法依据指定 tagName 标签名创立新元素节点,返回一个 HTMLElement 对象。标签名在 HTML 文档中不辨别大小写,在 XML(包含 XHTML)文档中辨别大小写。例如,创立一个 <p> 元素。

var p = document.createElement('p');

在应用 createElement() 办法创立新元素的同时,也为新元素设置了 ownerDocument 属性。

createTextNode()

document.createTextNode(data: string)办法依据指定 data 文本创立新文本节点。作为参数的文本依照 HTML 或 XML 的格局进行编码。

var textNode = document.createTextNode("<h4>Hello </h4> world!");

在创立新文本节点的同时,也会为其设置 ownerDocument 属性。

cloneNode()

node.cloneNode(deep)办法用于对调用这个办法的节点创立一个完全相同的正本。deep是布尔值,设置是否执行深复制。默认为 false,执行浅复制(只复制节点自身);当为true 时,执行深复制(复制节点及其整个子节点树)。

var node = document.getElementById('divId2').lastChild.cloneNode(true);

这些用于创立节点和复制节点的办法,创立或复制的节点并不会呈现在文档中,须要通过 appendChild()insertBefore()replaceChild()将它增加到文档中。

document.getElementById('divId2').appendChild(node);

不论是 createElement()createTextNode() 或者 cloneNode() 三种的哪种办法,创立新的节点都未被增加到文档树中。能够应用下面介绍的 appendChild()insertBefore()replaceChild()办法将新节点增加到文档树中。

一旦将节点增加到文档树中,浏览器就会立刻出现。

一旦应用 appendChild() 等办法插入相邻的同胞文本节点时,会导致相邻文本节点凌乱。

而在一个蕴含两个或多个文本节点的父元素上调用 normalize() 办法,就会将所有文本节点合并成一个节点。

var element = document.createElement("div");
element.className = "message";
var textNode1 = document.createTextNode("Hello,");
element.appendChild(textNode1);
var textNode2 = document.createTextNode("Pandora!");
element.appendChild(textNode2);
document.body.appendChild(element);
console.log(element.childNodes.length);  // 2
element.normalize();
console.log(element.childNodes.length);  // 1
console.log(element.firstChild.nodeValue);  // "Hello, Pandora!"

浏览器在解析文档时永远不会创立相邻的文本节点。这种状况只会作为执行 DOM 操作的后果呈现。

还有一个与 normalize() 相同的办法 splitText(offset):将一个文本节点分成两个文本节点。

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

更多内容请关注公众号「海人为记」

正文完
 0