第10章 DOM

(DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序编程接口)。
DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
1998年10月DOM1级规范成为W3C的推荐标准,为基本的文档结构及查询提供了接口。

IE中的所有DOM对象都是以COM对象的形式实现的。所以IE中的DOM对象与原生的JavaScript对象的行为或活动特点并不一致。

节点层次

节点分为几种不同的类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。
eg:

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

54eb4a7db9de404e466b18b18ed61518.png

每一段标记都可以通过树中的一个节点来表示,总共有12中节点类型,这些类型都继承自一个基类型。


Node类型

DOM1级定义了一个Node接口,该接口由DOM中的所有节点类型实现。

Node接口在JavaScript中是作为Node类型实现的,除IE之外,其他所有浏览器都可以访问到这个类型。

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

为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行比较:

if(someNode.nodeType == 1) {
    //适用于所有浏览器
    alert("Node is an element.");
}
  1. nodeName和nodeValue属性
    使用nodeName和nodeValue属性最好先检测节点类型。
if (someNode.nodeType == 1) {
    value = someNode.nodeName;      //nodeName的值是元素的标签名
}
  1. 节点关系
    在HTML中,可以将元素看成是元素的子元素,相应的元素就是元素的父元素。元素可以看成是元素的同胞元素。

每个节点都有一个childNodes属性,其中保存着一个NodeList对象,用于保存一组有序的节点,可以通过位置来访问这些节点。
NodeList不是Array的实例。
可以通过方括号,也可以使用item()方法来访问NodeList中的节点。
eg:

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.chldNodes.length;

length属性表示的是访问NodeList那一刻,其中包含的节点数量。

每个节点都有一个parentNode属性,指向文档树中的父节点。

通过使用列表中每个节点的previousSibling和nextSibling属性可以访问同一列表中的其他节点。列表中第一个节点的previousSibling属性为null,最后一个节点的nextSibling属性为null。
cc4f9c35e1de4ab73350ad75c3a1e2da.png

hasChildNodess()方法在节点包含一个或多个子节点的情况下返回true。

所有节点都有一个ownerDocument属性,指向表示整个文档的文档节点。

  1. 操作节点
    appendChild()方法用于向childNodes列表的末尾添加一个节点。添加节点后,childNodes的新增节点、父节点及以前的最后一个子节点的关系指针都会相应的得到更新。更新完成后,appendChild()返回新增的节点。
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode);             //true
alert(someNode.lastChild == newNode);       //true

如果传入到appendChild()中的节点已经是文档的一部分了,那么就会将该节点从原来的位置转移到新位置。

var returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild);         //false
alert(returnedNode == someNode.lastChild);          //true

使用insertBefore()方法可以将节点放在childNodes列表中某个特定的位置上,这个方法接收两个参数:要插入的节点和作为参照的节点。

//插入后成为最后一个子节点
returnedNode = someNode.insertBefore('newNode', null);
alert(newNode == someNode.lastChild);           //true

//插入后成为第一个子节点
returnedNode = someNode.insertBefore('newNode', someNode.firstChild);
alert(newNode == someNode.firstChild);           //true

//插入到最后一个子节点前面
returnedNode = someNode.insertBefore('newNode', someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]);           //true

replaceChild()方法接受两个参数:要插入的节点和要替换的节点。要替换的节点将又这个方法返回并从文档树中被移除,由要插入的节点占据其位置。

//替换第一个子节点
var returnedNode = someNode.replaceChild('newNode', someNode.firstChild);

//替换最后一个子节点
returnedNode = someNode.replaceChild('newNode', someNode.lastChild);

removeChild()方法接收一个参数,即要移除的节点,被移除的节点将成为方法的返回值。

//移除第一个子节点
var returnedNode = someNode.removeChild('newNode', someNode.firstChild);

//移除最后一个子节点
returnedNode = someNode.removeChild('newNode', someNode.lastChild);

通过replaceChild()和removeChild()方法移除的节点,仍然为文档所有,只是在文档中已经没有位置了。

以上4个方法操作的都是某个节点的子节点,要使用这几个方法必须先取得父节点。

并不是所有节点都有子节点,在不支持子节点的节点上调用以上方法将会导致错误。

  1. 其他方法
    cloneNode()方法和normalize()方法是所有类型的节点都有的。

cloneNode()方法,用于创建调用这个方法的节点的一个完全相同的副本,该方法接收一个布尔值参数,表示是否执行深复制(复制节点及其整个子节点树)。

通过cloneNode()方法复制的节点必须通过appendChild()、insertBefore()或replaceChild()添加到文档中。
eg:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    <body>
        <ul id="myList">
            <li>item 1</li>
            <li>item 2</li>
            <li>item 3</li>
        </ul>
        <script>
        var myList = document.getElementById("myList");
        var deepList = myList.cloneNode(true);
        alert(deepList.childNodes.length);      //3(ie < 9)或7(其他浏览器)

        var shallowList = myList.cloneNode(false);
        alert(shallowList.childNodes.length);       //0
        </script>
    </body>
</html>

normalize()方法唯一的作用就是处理文档树中的文本节点。


Document类型

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

document对象还有一个body属性,直接指向元素。

var body = document.body;       //取得<body>的引用

所有浏览器都支持document.documentElement和document.body属性。

通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性来访问它的信息。

var doctype = document.doctype;     //取得<!DOCTYPE>引用

浏览器对document.doctype的支持差别很大,总结如下:

  • IE8及之前版本:如果存在文档类型说明,会将其错误地解释为一个注释并把它当做Comment节点;而document.doctype的值始终未null。
  • IE9+及Firefox:如果存在文档类型声明,则将其作为文档的第一个子节点:document.doctype是一个DocumentType节点,也可以通过document.firstChild或document.childNodes[0]访问同一个节点。
  • Safari、Chrome和Opera:如果存在文档类型声明,则将其解析,但不作为文档的子节点。document.doctype是一个DocumentType节点,但该节点不会出现在document.childNodes中。

浏览器在处理标签外部的注释时存在以下差异:
以简单的Html页面为例。

<!-- 第一条注释 -->
<html>
    <body>
    
    </body>
</html>
<!-- 第二条注释 -->
  • IE8及之前版本、Safari 3.1及更高版本、Opera和Chrome只为第一条注释创建节点,部位第二条注释创建节点。结果,第一条注释就会成为document.childNodes中的第一个子节点。
  • IE9及更高版本会将第一条注释创建为document.childNodes中的一个注释节点,也会将第二条注释创建为document.childNodes中的注释节点。
  • Firefox以及Safari3.1之前的版本会完全忽略这两条注释。

多数情况下,我们都用不着在document对象上调用appendChild()、removeChild()和replaceChild()方法,因为文档类型(如果存在的话)是只读的,而且它只有一个元素子节点(该节点通常早就已经存在了)。

  1. 文档信息
    title属性包含元素中的文本。
//取得文档标题
var originalTitle = document.title;

//设置文档标题
document.title = "New page title";

URL属性包含页面完成的URL(即地址栏中显示的URL)
domain属性中包含页面的域名
referrer属性中保存着链接到当前页面的那个页面的URL。

//取得完整的URL
var url = document.URL;

var domain = document.domain;

//取得来源页面的URL
var referrer = document.referrer;

URL和referrer属性不可设置,domain可以设置,但是有限制:只能将domain设置为RUL中包含的域,并且如果域名一开始是“松散的”,就不能将它再设置为“紧绷的”。
eg:

//假设页面来自p2p.wrox.com域
document.domain = "wrox.com";        //成功
document.domain = "nczonline.net";   //失败

document.domain = "wrox.com";       //松散的(成功)
document.domain = "p2p.wrox.com";   //紧绷的(出错)

来自不同子域的页面无法通过javascript通信。

  1. 查找元素
    Document类型提供以下方法取得元素引用:
  • getElementById(),接收一个参数:要取得的元素的ID。如果找到相应的元素则返回该元素,如果不存在返回null。
    IE8及较低版本不区分ID的大小写。
    如果页面中多个元素的ID值相同,则返回第一次出现的元素。

  • getElementsByTagName(),接收一个参数:要取得元素的标签名,返回包含零或多个元素的NodeList。在HTML文档中,返回一个HTMLCollection对象。

  • getElementsByName(),接收一个参数:要取得元素的name。返回带有给定name特性的所有元素。

  1. 特殊集合
    除了属性和方法,document对象还有一些特殊的集合,这些集合都是HTMLCollection对象:
  • document.anchors,包含文档中所有带name特性的元素;
  • document.applets,包含文档中所有的元素,因为不在推荐使用元素,所以这个集合已经不建议使用了;
  • document.forms,包含文档中所有的元素,与document.getElementsByTagName(“form”)得到的结果相同。
  • document.images,包含文档中素有的元素,与document.getElementsByTagName(“img”)得到的结果相同;
  • document.links,包含文档中所有带href特性的元素。
  1. DOM一致性检测
    document.implementation属性提供了浏览器对DOM的实现相关信息和功能对象。
    该属性的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由鼠标引发的事件(click、mouseover等)
MutationEvents2.0,3.0DOM树变化时引发的事件
HTMLEvents2.0HTML4.01事件
Range2.0用于操作DOM树中某个范围的对象和方法
Traversal2.0遍历DOM树的方法
LS3.0文件与DOM树之间的同步加载和保存
LS-Async3.0文件与DOM树之间的异步加载和保存
Validation3.0在确保有效的前提下修改DOM树的方法
  1. 文档写入
    四个方法可以将输出流写入到网页中:
  • write(),接受衣蛾字符串参数,既要写入到输出流中的文本。
<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>
  • writelnm(),接收一个字符串参数,既要写入到输出流中的文本。
<html>
<head>
    <title>document.write() Example2</title>
</head>
<body>
    <script type="text/javascript">
        document.writeln("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");
    </script>
</body>
</html>
  • open(),打开网页输出流。
  • close(),关闭网页输出流。

严格型XHTML文档不支持文档写入。


Element类型

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

  • nodeType值为1;
  • nodeName值为元素的标签名;
  • nodeValue值为null;
  • parentNode可能是Document或Element;
  • 其子节点可能是Element、Text、Comment、ProcessingInstruction、CDATASection或EntityReference。

访问元素的标签名可以使用nodeName属性,也可以使用tagName属性。

<html>
<head>
    <title>Example</title>
</head>
<body>
    <div id="myDiv"></div>
    <script type="text/javascript">
        var div = document.getElementById("myDiv");
        alert(div.tagName);                     //"DIV"
        alert(div.tagName == div.nodeName);     //true
    </script>
</body>
</html>

在HTML中,标签名始终以全部大写表示;在XML(有时候也包括XHTML)中,标签名始终与源代码保持一致。

  1. HTML元素
    所有HTML元素都有HTMLElement类型表示。HTMLElement类型直接继承自Element并添加了一些属性。
    每个HTML元素中都存在以下标准特性:
  • id,元素在文档中的唯一标识符。
  • title,有关元素的附加说明信息,一般通过工具提示条显示。
  • lang,元素内容的语言代码,很少使用。
  • dir,语言的方向,值为ltr或rtl,也很少使用。
  • className,与元素的class特性对应,为元素指定的CSS类。
<html>
<head>
    <title>Example</title>
</head>
<body>
    <div id="myDiv" class="db" title="Body text" lang="en" dir="ltr"></div>
    <script type="text/javascript">
        var div = document.getElementById("myDiv");
        alert(div.id);              //"myDiv"
        alert(div.className);       //"db"
        alert(div.title);           //"Body text"
        alert(div.lang);            //"en"
        alert(div.dir);             //"ltr"
    </script>
</body>
</html>
  1. 取得特性
    每个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。
    操作特性的DOM方法主要由三个:
  • getAttribute()
  • setAttribute()
  • removeAttribute()
    传递给getAttribute()的特姓名与实际的特性名相同,如果给定名称的特性不存在,该方法返回null。
    通过getAttribute()可以取得自定义特性的值。
    根据HTML5规范,自定义特性应该加上data-前缀以便验证。
  1. 设置特性
    setAttribute()方法接收两个参数:要设置的特姓名和值。如果特性已存在,该方法会以指定值替换现有值,如果不存在,该昂发会创建该特性并设置相应的值。

使用removeAttribute()方法用于彻底删除元素的特性,调用这个方法不仅会清除特性的值,也会从元素中完全删除特性。

  1. attributes属性
    Element类型是使用attribute属性的唯一一个DOM节点类型。
    元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。
    NamedNodeMap对象拥有下列方法:
  • getNamedItem(name):返回nodeName属性等于name的节点;
  • removeNamedItem(name):从列表中移除nodeName属性等于name的节点;
  • setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引;
  • item(pos):返回位于数字pos位置处的节点。
  1. 创建元素
    document.createElement()方法可以创建新元素,该方法接收一个参数,即要创建元素的标签名,这个签名在HTML文档中不区分大小写,在XML中区分。

在使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性。

在IE中可以以另外一种方式使用createElement(),即为这个方法传入完整的元素标签,也可以包含属性。
eg:

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

通过以上方法可以避开在IE7及更早版本中动态创建元素的某些问题:

  • 不能设置动态创建的>元素的name特性。
  • 不能通过表单的reset()方法重设动态创建的元素。
  • 动态创建的type特性值为"reset"的元素重设不了表单。
  • 动态创建的一批name相同的单选按钮彼此毫无关系。name值相同的一组单元按钮本来应该用于表示同一选项的不同值,但动态创建的一批这种单选按钮之间却没有这种关系。
  1. 元素的子节点
    元素可以有任意数目的子节点和后代节点。
    元素的childNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。不同浏览器看待这些节点方面存在显著的不同。
    如:
<ul id="myList">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

以上代码在IE中会有3个子节点,分别是3个

  • 元素,而在其他浏览器中会有7个元素,包括3个
  • 元素和4个文本节点(表示
  • 元素之间的空白符)。
    如果删除空白符:
  • <ul id="myList"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
    

    以上代码在任何浏览器中都包含3个子节点。
    如果需要通过childNodes属性遍历子节点,那么一定不要忘记浏览器间的这一差别。在执行某项操作前,先检查一下nodeType属性,当nodeType等于1时,再执行某些操作

    for (var i = 0, len = element.childNodes.length; i< len; i++) {
        if (element.childNodes[i].nodeType == 1) {
            //执行某些操作
        }
    }
    

    元素也支持getElementsByTagName()方法,只是搜索起点是当前元素。


    Text类型

    文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。
    特征:

    • 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属性,保存着节点中字符的数目。nodeValue.length和data.length也保存着同样的值。

    在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。

            <!-- 没有内容,也就没有文本节点 -->
            <div></div>
    
            <!-- 有空格,因而有一个文本节点 -->
            <div> </div>
    
            <!-- 有内容,因而有 一个文本节点 -->
            <div>Hello World!</div>
    

    可以访问和修改文本子节点

    var textNode = div.firstChild;  //或者div.childNodes[0]
    div.firstChild.nodeValue = "Some other message";
    

    在修改文本节点时,字符串会经过HTML(或XML,取决于文档类型)编码。

        div.firstChild.nodeValue = "Some <strong>other</strong>message";
        //输出结果是"Some &lt;strong&gt;other&lt;/strong&gt;message"
    
    1. 创建文本节点
      document.createTextNode()创建新文本节点,该方法接受一个参数——要插入节点中的文本。在新建文本节点的同时,也会为其设置ownerDocument属性。
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
        </head>
        <body>
        <script type="text/javascript">
            var element = document.createElement("div");
            element.className = "message";
    
            var textNode = document.createTextNode("Hello world!");
            element.appendChild(textNode);
    
            document.body.appendChild(element);
        </script>
        </body>
    </html>
    

    此处需要注意,不能将JavaScript代码写到内,否则会出现类似“Cannot read property ‘appendChild’ of null”的错误,执行javascript时页面还未完全加载。

    如果一个元素包含多个文本子节点,并且两个文本节点是相邻的同胞节点,那么这两个节点中的文本就会连起来显示,中间不会有空格。

    1. 规范化文本节点
      如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
        </head>
        <body>
        <script type="text/javascript">
            var element = document.createElement("div");
            element.className = "message";
    
            var textNode = document.createTextNode("Hello world!");
            element.appendChild(textNode);
    
            var otherTextNode = document.createTextNode("Yippee!");
            element.appendChild(otherTextNode);
    
            document.body.appendChild(element);
    
            alert(element.childNodes.length);       //2
    
            element.normalize();
            alert(element.childNodes.length);       //1
            alert(element.firstChild.nodeValue);    //"Hello world!Yippee!"
        </script>
        </body>
    </html>
    
    1. 分割文本节点
      splitText()方法会将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新文本节点将包含剩下的文本。改方法返回一个新文本节点,改节点与原节点的parentNode相同。
            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);
            alert(element.firstChild.nodeValue);    //"Hello"
            alert(newNode.nodeValue);               //" world!"
            alert(element.childNodes.length);       //2
    

    Comment类型

    DOM中Comment表示注释类型,具有下列特征:

    • nodeType值为8;
    • nodeName值为"#comment";
    • nodeValue值是注释的内容;
    • parenNode可能是Document或Element;
    • 不支持(没有)子节点。

    Comment类型与Text类型继承自相同的基类,因此它拥有除splitText()之外的所有字符串操作方法。与Text类型相似,也可以通过nodeValue或data属性来取得注释内容。

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
        </head>
        <body>
        <div id="myDiv"><!-- A Comment --></div>
        <script type="text/javascript">
            var div = document.getElementById("myDiv");
            var comment = div.firstChild;
            alert(comment.data);        //"A Comment"
    
        </script>
        </body>
    </html>
    

    CDATASection类型

    CDATASection类型只针对基于XML的文档,表示的是CDATA区域。CDATASection节点具有下列特征:

    • nodeType值Wie4;
    • nodeName值为"#cdata-section";
    • nodeValue值是CDATA区域中的内容;
    • parentNode可能是Document或Element;
    • 不支持(没有)子节点。

    DocumentType类型

    仅有Firefox、Safari和Opera支持DocumentType类型。该类型包含着与文档的doctype有关的所有信息,具有下列特征:

    • nodeType的值为10;
    • nodeName的值为doctype的名称;
    • nodeValue的值为null;
    • ParentNode是Document;
    • 不支持(没有)子节点。

    在DOM1级中,DocumentType对象不能冬天创建,只能通过解析文档代码的方式来创建。


    DocumentFragment类型

    DocumentFragment类型在文档中没有对应的标记,DOM规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整文档那样占用额外的资源。该类型具有以下特征:

    • nodeType的值为11;
    • nodeName值为“#document-fragment”;
    • nodeValue值为null;
    • parentNode值为null;
    • 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntityReference。

    可以将文档片段当做一个仓库来使用,字啊里面保存将来可能会添加到文档中的节点。

    使用document.createDocumentFragment()方法可以创建文档片段。

    文档片段继承了Node的所有方法,通常用于执行那些针对文档的DOM操作。

    如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。

    使用文档片段可以避免浏览器反复渲染新信息的问题。
    eg:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Document</title>
        </head>
        <body>
        <ul id="myList"><!-- A Comment --></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>
        </body>
    </html>
    

    Attr类型

    元素的特性在DOM中已Attr类型来表示。在所有浏览器中都可以访问Attr类型的构造函数和原型。该类型具有以下特征:

    • nodeType值为11;
    • nodeName值为特性的名称;
    • nodeValue的值为特性的值;
    • parentNode值为null;
    • 在HTML中不支持(没有)子节点;
    • 在XML中子节点可以是Text或EntityReference。
      尽管Attr也是节点,但特性不被认为是DOM文档树的一部分。

    开发人员最常使用的是getAttribute()、setAttribute()和removeAttribute()方法,很少直接饮用特性节点。

    Attr对象有3个属性:name、value和specified。
    name:特性名称。
    value:特性的值
    specified:布尔值,用于区分特性是在代码中指定的还是默认的。

    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"
    

    DOM操作技术

    动态脚本

    动态加载的外部JavaScript文件能够立即运行:

    <script type="text/javascript" src="client.js"></script>
    

    创建以上节点的DOM代码如下:

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

    将以上代码进行封装后:

    function loadScript(url) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        document.body.appendChild(script);
    }
    

    可以通过行内方式指定JavaScript代码:

    <script type="text/javascript">
        function sayHi(){
            alert("hi");
        }
    </script>
    

    以上代码也可以通过DOM代码的方式:

    var script = document.createElement("script");
    script.type = "text/javascript";
    script.appendChild(document.createTextNode("function sayHi(){alert('hi');});
    document.body.appendChild(script);
    

    以上代码在Firefox、Safari、Chrome和Opera中正常,但在IE中会导致错误,因为IE不允许DOM访问其子节点。但是可以设置script的text属性。

    var script = document.createElement("script");
    script.type = "text/javascript";
    script.text="function sayHi(){alert('hi');}";
    document.body.appendChild(script);
    

    一种更具兼容性的实现方式是:

    var script = document.createElement("script");
    script.type = "text/javascript";
    var code = "function sayHi(){alert('hi');}";
    try{
        script.appendChild(document.createTextNode(code));
    } catch (ex) {
        script.text = code;
    }
    document.body.appendChild(script);
    

    进一步封装成函数:

    function loadScriptString(code){
        var script = document.createElement("script");
        script.type = "text/javascript";
        try{
            script.appendChild(document.createTextNode(code));
        } catch (ex) {
            scrpt.text = code;
        }
        document.body.appendChild(script);
    }
    

    动态样式

    元素用于包含来自外部的文件,

    典型的元素用法:

    使用DOM代码创建动态样式:

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

    进一步封装以上代码:

    function loadStyle(url) {
        var link = document.createElement("link");
        link.rel = "stylesheet";
        link.type = "text/css";
        link.href = url;
        var head = document.getElementsByTagName("head")[0];
        head.appendChild(link);
    }
    

    典型的嵌入式css:

    <style type="text/css">
    body {
        background-color: red;
    }
    </style>
    

    使用DOM代码创建:

    var style = document.createElement("style");
    style.type = "text/css";
    style.appendChild(document.createTextNode("body{background-color:red}"));
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(style);
    

    以上代码在IE中依然会报错,因为

    var style = document.createElement("style");
    style.type = "text/css";
    try{
        style.appendChild(document.createTextNode("body{background-color:red}"));
    } catch (ex) {
        style.styleSheet.cssText = "body{background-color:red}";
    }
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(style);
    

    兼容的解决方案是:

    function loadStyleString(css) {
        var style = document.createElement("style");
        style.type = "text/css";
        try{
            style.appendChild(document.createTextNode(css));
        } catch (ex) {
            style.styleSheet.cssText = css;
        }
        var head = document.getElementsByTagName("head")[0];
        head.appendChild(style);
    }
    

    在IE中,重用同一个


    操作表格

    使用核心DOM方法创建和修改表格通常需要编写大量大码。
    eg:

    <table boreder="1" width="100%">
        <tbody>
        <tr>
            <td>Cell 1,1</td>
            <td>cell 2,1</td>
        </tr>
        <tr>
            <td>Cell 1,2</td>
            <td>cell 2,2</td>
        </tr>
        </tbody>
    </table>
    

    使用核心DOM创建:

    //创建table
    var table = document.createElement("table");
    table.border = 1;
    table.width = "100%";
    
    //创建tbody
    var tbody = document.createElement("tbody");
    table.appendChild(tbody);
    
    //创建第一行
    var row1 = document.createElement("tr");
    tbody.appendChild(row1);
    var cell1_1 = document.createElement("td");
    cell1_1.appendChild(document.appendTextNode("Cell 1,1"));
    row1.appendChild(cell1_1);
    var cell2_1 = document.createElement("td");
    cell2_1.appendChild(document.appendTextNode("Cell 2,1"));
    row1.appendChild(cell2_1);
    
    //创建第二行
    var row2 = document.createElement("tr");
    tbody.appendChild(row2);
    var cell1_2 = document.createElement("td");
    cell1_2.appendChild(document.appendTextNode("Cell 1,2"));
    row2.appendChild(cell1_2);
    var cell2_2 = document.createElement("td");
    cell2_2.appendChild(document.appendTextNode("Cell 2,2"));
    row2.appendChild(cell2_2);
    
    //将表格添加到文档主体中
    document.body.appendChild(table);
    

    为了方便构建表格,HTML DOM为

    、、和等元素添加了一些属性和方法。

    元素: * caption:保存着对元素的HTMLCollection。 * tFoot:保存着对元素(如果有)的指针。 * tHead:保存着对元素(如果有)的指针。 * rows:是一个表格中所有行的HTMLCollection。 * createTHead():创建元素,将其放到表格中,返回引用。 * createTFoot():创建元素,将其放到表格中,返回引用。 * createCaption():创建元素。 * deleteTFoot():删除元素。 * deleteCaption():删除
    元素(如果有)的指针。 * tBodies:是一个 元素,将其放到表格中,返回引用。 * deleteTHead():删除 元素。 * deleteRow(pos):删除指定位置的行。 * insertRow(pos):向rows集合中的指定位置插入一行。
    元素: * rows:保存着元素中行的HTMLCollection。 * deleteRow(pos):删除指定位置的行。 * insertRow(pos):向rows集合中的指定位置插入一行,返回对新插入行的引用。 元素: * cells:保存着元素中单元格的HTMLCollecton。 * deleteCell(pos):删除指定位置的单元格。 * insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用。

    使用以上方法重写表格DOM代码:

    //创建table
    var table = document.createElement("table");
    table.border = 1;
    table.width = "100%";
    
    //创建tbody
    var tbody = document.createElement("tbody");
    table.appendChild(tbody);
    
    //创建第一行
    tbody.insertRow(0);
    tbody.rows[0].insertCell(0);
    tboyd.rows[0].cells[0].appendChild(document.createTextNode("cell 1,1"));
    tbody.rows[0].insertCell(1);
    tboyd.rows[0].cells[1].appendChild(document.createTextNode("cell 2,1"));
    
    //创建第二行
    tbody.insertRow(1);
    tbody.rows[1].insertCell(0);
    tboyd.rows[1].cells[0].appendChild(document.createTextNode("cell 1,2"));
    tbody.rows[1].insertCell(1);
    tboyd.rows[1].cells[1].appendChild(document.createTextNode("cell 2,2"));
    
    //将表格添加到文档主体中
    document.body.appendChild(table);
    

    使用NodeList

    NodeList、NamedNodeMap和HTMLCollection都是动态的,每当文档结构发生变化时,他们都会得到更新。
    从本质上说,所有NodeList对象都是在访问DOM文档时实时运行的查询。
    以下代码会导致无限循环:

    var divs = document.getElementsByTagName("div"),
        i,
        div;
    
    for (i = 0; i < divs.length; i++) {
        div = document.createElement("div");
        document.body.appendChild(div);
    }
    

    想要迭代一个NodeList,最好使用length属性初始化第二个变量,然后将迭代器与该变量进行比较:

    var divs = document.getElementsByTagName("div"),
        i,
        len,
        div;
    
    for (i = 0, len = divs.length; i < len; i++) {
        div = document.createElement("div");
        document.body.appendChild(div);
    }
    

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

    • 最近本的节点类型是Node,用于抽象地表示文档中的一个独立的部分;所有其他类型都继承自Node。
    • Document类型表示整个文档,是一组分层节点的跟节点。在JavaScript中,document对象是Document的一个实例。使用document对象,有很多中方式可以查询和取得节点。
    • Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
    • 另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。
      访问DOM的操作在多数情况下都很直观,不过在处理
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值