DOM 详解

DOM

IE中的所有DOM对象都是以COM对象的形式实现的。

节点层次

DOM可以将任何HTML或XML文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型:

文档元素: 在HTML页面中,文档元素始终都是<html>元素,(在XML中何元素都可能成为文档元素)

12种DOM节点层次(继承自一个基类型)

  • 1、node类型
  • 2、Document类型
  • 3、Element类型
  • 4、Text类型
  • 5、Comment类型
  • 6、CDATASection类型(only XML文档)
  • 7、DocumentType(类型)(在Web浏览器中并不常用)
  • 8、DocumentFragment
  • 9、Attr类型
  • ... 其他

一、Node类型(12个数值常量来表明节点的类型。)

DOM1级定义了一个Node接口,该接口将由DOM中的所有节点类型实现。
JavaScript中的所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。

1、dom.nodeNamenodeValue

这两个属性的值完全取决于节点的类型,对于元素节点,node-Name中保存的始终都是元素的标签名,而nodeValue的值则始终为null。

2、节点的关系 childNodesparentNodehasChildNodes()

每个节点都有一个childNodes属性,其中保存着一个NodeList对象,是一种类数组对象,用于保存一组有序的节点。childNodes列表中的每个节点相互之间都是同胞节点。通过使用列表中每个节点的previousSiblingnextSibling属性,可以访问同一列表中的其他节点。父节点与其第一个和最后一个子节点之间也存在特殊关系。父节点的firstChildlastChild属性分别指向其childNodes列表中第一个和最后一个节点。

 ParentNode.lastElementChild
 ParentNode.firstElementChild
3、操作节点
  • appendChild(): 将一个节点添加到指定父节点的子节点列表末尾, 如果传入到appendChild()中的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。即使可以将DOM树看成是由一系列指针连接起来的,但任何DOM节点也不能同时出现在文档中的多个位置上。
//将一个节点添加到指定父节点的子节点列表末尾。
const child = node.appendChild(child);
// node: 是要插入子节点的父节点.
// child: 即是参数又是这个方法的返回值.
  • insertBefore() 参考节点之前插入一个节点作为一个指定父节点的子节点
const insertedElement = parentElement.insertBefore(newElement, referenceElement);
//1. referenceElement为null则newElement将被插入到子节点的末尾。
//2. 如果newElement已经在DOM树中,newElement首先会从DOM树中移除。

insertedElement 是被插入的节点,即 newElement
parentElement 是新插入节点的父节点
newElement 是被插入的节点
referenceElement 在插入newElement之前的那个节点

  • replaceChild(): 接受的两个参数是:要插入的节点和要替换的节点
replacedNode = parentNode.replaceChild(newChild, oldChild);
// newChild 用来替换 oldChild 的新节点。如果该节点已经存在于DOM树中,则它会被从原始位置删除。
// oldChild  被替换掉的原始节点。
// replacedNode 和 oldChild相等。
  • removeChild() 从DOM中删除一个子节点。返回删除的节点
let oldChild = node.removeChild(child);
//OR
element.removeChild(child);
// child 是要移除的那个子节点.
// node 是child的父节点.
// oldChild保存对删除的子节点的引用. oldChild === child.
  • cloneNode(): 接受一个布尔值参数,表示是否执行深复制。在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false的情况下,执行浅复制,即只复制节点本身。
  • normalize()

cloneNode 和 normalize 所有类型的节点都有

二、Document 类型(document对象-文档对象)

JavaScript通过Document类型表示文档。在浏览器中,document对象继承自Document类型的一个实例,表示整个HTML页面,是window对象的一个属性

  • nodeType的值为9;
  • nodeName的值为"#document";
  • nodeValue的值为null;
  • parentNode的值为null;
  • ownerDocument的值为null;

最常见的应用还是作为HTMLDocument实例的document对象

1、文档的子节点

DOM标准规定Document节点的子节点可以是DoumentTypeElementProcessingInstructionComment,但还有两个内置的访问其子节点的快捷方式。

  • documentElement属性,该属性始终指向<html>元素。
  • childNodes列表访问文档元素,但通过documentElement属性则能更快捷、更直接地访问该元素。
// 取得对<html>的引用
var html = document.documentElement;
// true
alert(html === document.childNodes[0]);
//true
alert(html === document.firstChild);
//取得对<body>的引用
var body = document.body;
//取得对<!DOCTYPE>的引用
var doctype = document.doctype;
2、文档信息

作为HTMLDocument的一个实例,document对象还有一些标准的Document对象所没有的属性。

//取得文档标题
var originalTitle = document.title;
//设置文档标题
document.title = "New page title";
//取得完整的URL
var url = document.URL;
//取得域名
var domain = document.domain;
//取得来源页面的URL
var referrer = document.referrer;

只有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";

当页面中包含来自其他子域的框架或内嵌框架时,能够设置document.domain就非常方便了。由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而通过将每个页面的doc-ument.domain设置为相同的值,这些页面就可以互相访问对方包含的JavaScript对象了。

查找元素
  • getElementById()
  • getElementsByTagName()
  • getElementsByName() 只有HTMLDocument类型才有的方法
  • getElementsByClassName() (H5)

classList 属性
HTML5新增了一种操作类名的方式,可以让操作更简单也更安全,那就是为所有元素添加classList属性。这个classList属性是新集合类型DOMTokenList的实例。与其他DOM集合类似,DOMTokenList有一个表示自己包含多少元素的length属性,而要取得每个元素可以使用item()方法,也可以使用方括号语法。此外,这个新类型还定义如下方法。

  • add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
  • contains(value):表示列表中是否存在给定的值,如果存在则返回true,否则返回false。
  • remove(value):从列表中删除给定的字符串。
  • toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。
特殊集合
  • document.anchors,包含文档中所有带name特性的<a>元素;
  • document.forms,包含文档中所有的<form>元素,与doc-ument.getElementsByTagName("form")得到的结果相同;
  • document.images,包含文档中所有的<img>元素,与doc-ument.getElementsByTagName ("img")得到的结果相同;
  • document.links,包含文档中所有带href特性的<a>元素。
文档写入

write()writeln()open()close()

三、Element 类型

除Document类型之外,Element类型就要算是Web编程中最常用的类型了。Element类型用于表现XML或HTML元素,提供了对元素标签名、子节点及特性的访问。

  • nodeType的值为1;
  • nodeName的值为元素的标签名;
  • nodeValue的值为null;
  • parentNode可能是Document或Element;
  • 其子节点可能是Element、Text、Comment、Processin-gInstruction、CDATASection或EntityReference
<div id="myDiv"></div>
//可以像下面这样取得这个元素及其标签名: 
var div = document.getElementById("myDiv");
//"DIV"
alert(div.tagName);
//true
alert(div.tagName == div.nodeName);
// 在HTML中,标签名始终都以全部大写表示
1、HTML元素

所有HTML元素都是由HTMLElement或者其更具体的子类型来表示的。

2、操作特性
  • getAttribute()
  • setAttribute()
  • removeAttribute()
3、attributes属性

attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个“动态”的集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap对象拥有下列方法。

4、创建元素

document.createElement()

var div = document.createElement("div");
div.id = "myNewDiv";
div.className = "box";
document.body.appendChild(div);
5、元素的子节点

元素可以有任意数目的子节点和后代节点,因为元素可以是其他元素的子节点。元素的childNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。

四、Text 类型

文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML代码。

  • nodeType的值为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属性,保存着节点中字符的数目。
<!-- 没有内容,也就没有文本节点 -->
<div></div>
<!-- 有空格,因而有一个文本节点 -->
<div> </div>
<!-- 有内容,因而有一个文本节点 -->
<div>Hello World!</div>

通过 nodeValue 访问值

1.创建文本节点
  • document.createTextNode()
var textNode = document.createTextNode("<strong>Hello</strong> world!");

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);
2.规范化文本节点normalize()。
3.分割文本节点splitText()。

五、Comment类型

<div id="myDiv"><!--A comment --></div>

DOM操作技术

动态脚本

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "client.js";
document.body.appendChild(script);

动态样式

var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "style.css";
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);

使用NodeList

NodeList每当文档结构发生变化时,NodeList都会得到更新。

小结

DOM由各种节点构成,简要总结如下。

  • 最基本的节点类型是Node,用于抽象地表示文档中一个独立的部分;所有其他类型都继承自Node。
  • Document类型表示整个文档,是一组分层节点的根节点。在JavaScript中,document对象是Document的一个实例。使用document对象,有很多种方式可以查询和取得节点。
  • Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
  • 另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。

DOM扩展

  • querySelector()
  • querySelectorAll()
  • matchesSelector() 这个方法接收一个参数,即CSS选择符,如果调用元素与该选择符匹配,返回true;否则,返回false。

H5

一、焦点管理

  • document.activeElement
  • document.hasFocus()

二、HTMLDocument 的变化

  • readyState属性
document.readyState === 'loading'
document.readyState === 'complete'
  • 字符集属性
//"UTF-16"
alert(document.charset);
document.charset = "UTF-8";
  • 自定义数据属性
    HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以data-开头即可。
    dataset属性来访问自定义属性的值。dataset属性的值是DOMStringMap的一个实例,也就是一个名值对儿的映射。在这个映射中,每个data-name形式的属性都会有一个对应的属性
//本例中使用的方法仅用于演示
var div = document.getElementById("myDiv");
//取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;//设置值

三、插入标记

  • innerHTML属性
  • outerHTML属性
  • insertAdjacentHTML()方法

四、内存与性能问题

// bad
for (var i=0, len=values.length; i < len; i++){    
//要避免这种频繁操作!!   
ul.innerHTML += "<li>" + values[i] + "</li>";
}
// good
var itemsHtml = ""; 
for (var i=0, len=values.length; i < len; i++){    
itemsHtml += "<li>" + values[i] + "</li>"; 
}

ul.innerHTML = itemsHtml; 

五、scrollIntoView()

scrollIntoView()可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入true作为参数,或者不传入任何参数,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入false作为参数,调用元素会尽可能全部出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐,例如:

//让元素可见
document.forms[0].scrollIntoView();

推荐阅读更多精彩内容