目录

第十章 - DOM

本章内容

  • 理解包含不同层次节点的 DOM
  • 使用不同的节点类型
  • 克服浏览器兼容性问题及各种陷阱

DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)。DOM 描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。

10.1 节点层次

在 HTML 页面中,文档元素始终都是元素。在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素。 HTML 文档表示为一个层次结构: https://raw.githubusercontent.com/codingbylch/Figure_bed_for_blog/master/img_for_blog/20200323212317.png

10.1.1 Node类型

DOM1 级定义了一个 Node 接口,该接口将由 DOM 中的所有节点类型实现。每个节点都有一个 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)。
  1. nodeName 和 nodeValue 属性 要了解节点的具体信息,可以使用 nodeNamenodeValue 这两个属性。这两个属性的值完全取决于节点的类型。
if (someNode.nodeType == 1){ 
 value = someNode.nodeName; //nodeName 的值是元素的标签名 
} 
  1. 节点关系 文档中所有的节点之间都存在这样或那样的关系。每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。如何访问保存在 NodeList 中的节点——可以通过方括号,也可以使用 item()方法。
var firstChild = someNode.childNodes[0]; 
var secondChild = someNode.childNodes.item(1); 
var count = someNode.childNodes.length; 

通过使用列表中每个节点的 previousSiblingnextSibling 属性,可以访问同一列表中的其他节点。 父节点的 firstChildlastChild 属性分别指向其 childNodes 列表中的第一个和最后一个节点。 https://raw.githubusercontent.com/codingbylch/Figure_bed_for_blog/master/img_for_blog/20200323214352.png 另外,**hasChildNodes()**也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回 true; 所有节点都有的最后一个属性是 ownerDocument,该属性指向表示整个文档的文档节点document。 3. 操作节点 因为关系指针都是只读的,所以 DOM 提供了一些操作节点的方法。

  • appendChild():用于向 childNodes 列表的末尾添加一个节点。更新完成后,appendChild()返回新增的节点。 js var returnedNode = someNode.appendChild(newNode); alert(returnedNode == newNode); //true alert(someNode.lastChild == newNode); //true
  • insertBefore():把节点放在 childNodes 列表中某个特定的位置上。这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。
  • replaceChild():替换节点。要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。
  • removeChild():移除节点。接受一个参数即要移除的节点,然后返回被移除的节点。 四个方法操作的都是某个节点的子节点,所以必须先取得父节点(使用 parentNode 属性)。
  1. 其他方法 有两个方法是所有类型的节点都有的。
    • cloneNode():用于创建调用这个方法的节点的一个完全相同的副本。cloneNode()方法接受一个布尔值参数,表示是否执行深复制。执行深复制,也就是复制节点及其整个子节点树;在参数为 false 的情况下,执行浅复制,即只复制节点本身(孤儿)。
    • normalize():处理文档树中的文本节点。

10.1.2 Document类型

在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个 HTML 页面。document 对象是 window 对象的一个属性,因此可以将其作为全局对象来访问。Document 节点具有下列特征:

  • nodeType 的值为 9;
  • nodeName 的值为"#document";
  • nodeValue 的值为 null;
  • parentNode 的值为 null;
  • ownerDocument 的值为 null;
  • 其子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或 Comment。
  1. 文档的子节点 documentElement属性,该属性始终指向 HTML 页面中的元素。
var html = document.documentElement; //取得对<html>的引用 
alert(html === document.childNodes[0]);     //true 
alert(html === document.firstChild); //true

body 属性,直接指向<body>元素,document.body 在 JavaScript 代码中出现的频率非常高。 document.doctype来访问<!DOCTYPE>标签,但各个浏览器的支持不一致:

var doctype = document.doctype;     //取得对<!DOCTYPE>的引用
  1. 文档信息 document 对象还有一些标准的 Document 对象所没有的属性。 document.title包含着<title>元素中的文本,修改 title 属性的值不会改变<title>元素。 对网页的请求有关的三个属性:URL、domain 和 referrer。 URL 属性:包含页面完整的 URL(即地址栏中显示的 URL); domain 属性:只包含页面的域名; referrer 属性:保存着链接到当前页面的那个页面的 URL。 在这 3 个属性中,只有 domain 是可以设置的。能将这个属性设置为 URL 中不包含的域。
//假设页面来自 p2p.wrox.com 域 
 
document.domain = "wrox.com"; // 成功 
 
document.domain = "nczonline.net";      // 出错! 

可以通过设置相同的document.domain来解决不同子域的页面的相互访问。浏览器对 domain 属性还有一个限制,即如果域名一开始是“松散的”(loose),那么不能将它再设置为“紧绷的”(tight)。

//假设页面来自于 p2p.wrox.com 域 
 
document.domain = "wrox.com";         //松散的(成功) 
 
document.domain = "p2p.wrox.com";     //紧绷的(出错!) 
  1. 查找元素 getElementById()和 getElementsByTagName()。 第一个方法,getElementById(),接收一个参数:要取得的元素的 ID。 另一个常用于取得元素引用的方法是 getElementsByTagName()。这个方法接受一个参数,即要取得元素的标签名,而返回的是包含零或多个元素的 NodeList。在 HTML 文档中,这个方法会返回一个 HTMLCollection 对象,作为一个“动态”集合,该对象与NodeList 非常类似。 namedItem()使用这个方法可以通过元素的 name 特性取得集合中的项。 HTMLCollection 还支持按名称访问项,对命名的项也可以使用方括号语法来访问:
var myImage = images["myImage"]; 

要想取得文档中的所有元素,可以向 getElementsByTagName()中传入"*"。 getElementsByName():这个方法会返回带有给定 name 特性的所有元素。最常使用 getElementsByName()方法的情况是取得单选按钮:

<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> 
  1. 特殊集合 略。
  2. DOM 一致性检测略。
  3. 文档写入 write()、writeln()、open()和 close()。 **write()**和 **writeln()**方法都接受一个字符串参数,即要写入到输出流中的文本。write()会原样写入,而 writeln()则会在字符串的末尾添加一个换行符(\n)。在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。此外,还可以使用 write()和 writeln()方法动态地包含外部资源,例如 JavaScript 文件等。如果在文档加载结束后再调用 document.write(),那么输出的内容将会重写整个页面。 方法 **open()**和 **close()**分别用于打开和关闭网页的输出流。

10.1.3 Element类型

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

  • nodeType 的值为 1;
  • nodeName 的值为元素的标签名;
  • nodeValue 的值为 null;
  • parentNode 可能是 Document 或 Element;
  • 其子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或EntityReference。
if (element.tagName == "div"){ //不能这样比较,很容易出错! 
    //在此执行某些操作 
} 
 
if (element.tagName.toLowerCase() == "div"){ //这样最好(适用于任何文档) 
    //在此执行某些操作 
} 
  1. HTML 元素 所有 HTML 元素都由 HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表示。添加的这些属性分别对应于每个 HTML元素中都存在的下列标准特性。
  • id,元素在文档中的唯一标识符。
  • title,有关元素的附加说明信息,一般通过工具提示条显示出来。
  • lang,元素内容的语言代码,很少使用。
  • dir,语言的方向,值为"ltr"(left-to-right,从左至右)或"rtl"(right-to-left,从右至左),也很少使用。
  • className,与元素的 class特性对应,即为元素指定的 CSS 类。 元素中指定的所有信息,都可以通过下列 JavaScript 代码取得:
var 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"
  1. 取得特性 getAttribute()、setAttribute()和 removeAttribute()。
var 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" 

有两类特殊的特性,属性的值与通过 getAttribute()返回的值并不相同。 第一类特性就是 style,用于通过 CSS 为元素指定样式。在通过 getAttribute()访问时,返回的 style 特性值中包含的是 CSS 文本,而通过属性来访问它则会返回一个对象。 第二类与众不同的特性是 onclick 这样的事件处理程序。onclick 特性中包含的是 JavaScript 代码,如果通过 getAttribute()访问,则会返回相应代码的字符串。而在访问onclick 属性时,则会返回一个 JavaScript 函数。

  1. 设置特性 setAttribute(),这个方法接受两个参数:要设置的特性名和值。 通过 setAttribute()方法既可以操作 HTML 特性也可以操作自定义特性。通过这个方法设置的特性名会被统一转换为小写形式,即"ID"最终会变成"id"。
div.setAttribute("id", "someOtherId"); 
div.setAttribute("class", "ft"); 

发现这样看红宝书效率不高,暂停,略。

10.3 小结

DOM 是语言中立的 API,用于访问和操作 HTML 和 XML 文档。DOM1 级将 HTML 和 XML 文档形象地看作一个层次化的节点树,可以使用 JavaScript 来操作这个节点树,进而改变底层文档的外观和结构。 DOM 由各种节点构成,简要总结如下。

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

NodeList 对象都是“动态的”,这就意味着每次访问NodeList 对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少 DOM 操作。