JavaScript文档操作

文档对象模型(document object model,DOM)是W3C制定的一套技术规范,用来描述JavaScript脚本与HTML文档进行交互的Web标准。DOM规定了一系列标准接口,允许开发人员通过标准方式访问文档结构、操作网页内容、控制样式和行为等。

1、节点

节点(node)是DOM最基本的单元,并派生出不同类型的节点,它们共同构成文档结构模型。在网页中所有对象和内容都被称为节点,如文档、元素、文本、属性、注释等。

1.1、节点的类型

根据DOM规范,整个文档是一个文档节点,每个标签是一个元素节点,包含的文本是文本节点,标签的属性是一个属性节点,注释属于注释节点等。DOM节点类型说明如下表所示:
JavaScript文档操作_第1张图片

使用nodeType属性可以判断一个节点的类型,取值说明如下表所示:
JavaScript文档操作_第2张图片

1.2、节点的名称和值

使用nodeName和nodeValue属性可以读取节点的名称和值,属性取值说明如下表所示:
JavaScript文档操作_第3张图片

【示例】不同类型的节点,nodeName和nodeValue属性取值是不同的。元素的nodeName属性返回值是标签名,而元素的nodeValue属性返回值为null。因此在读取属性值之前,应该先检测类型:

    var node = document.getElementsByTagName("body")[0];
    if (node.nodeType==1)
        var value = node.nodeName;
    console.log(value);

nodeName属性在处理标签时比较实用,而nodeValue属性在处理文本信息时比较实用。

1.3、访问节点

DOM为Node类型定义如下属性,以方便JavaScript访问节点:

  • ownerDocument:返回当前节点的根元素(document对象)。
  • parentNode:返回当前节点的父节点。所有的节点都仅有一个父节点。
  • childNodes:返回当前节点的所有子节点的节点列表。
  • firstChild:返回当前节点的第一个子节点。
  • lastChild:返回当前节点的最后一个子节点。
  • nextSibling:返回当前节点之后相邻的同级节点。
  • previousSibling:返回当前节点之前相邻的同级节点。

【示例】针对下面文档结构:

    <!doctype html>
    <html>
    <head>
    <meta charset="utf-8">
    </head>
    <body><span class="red">body</span>元素</body></html>

可以使用下面方法访问body元素:

    var b = document.documentElement.lastChild;
    var b = document.documentElement.firstChild.nextSibling.nextSibling;

通过下面方法可以访问span包含的文本:

    var text = document.documentElement.lastChild.firstChild.firstChild.nodeValue;

1.4、操作节点

操作节点的基本方法如下表所示:
JavaScript文档操作_第4张图片
提示:appendChild()、insertBefore()、removeChild()、replaceChild() 4种方法用于对子节点进行操作。使用这4种方法之前,可以使用parentNode属性先获取父节点。另外,并不是所有类型的节点都有子节点,如果在不支持子节点的节点上调用这些方法将会导致错误发生。

【示例】为列表框绑定一个click事件处理程序,通过深度克隆,新的列表框没有添加JavaScript事件,仅克隆HTML类样式和style属性:

    <h1>DOM</h1>
    <p>DOM<cite>Document Object Model</cite>首字母简写,中文翻译为<b>文档对象模型</b>,是<i>W3C</i>组织推荐
的处理可扩展标识语言的标准编程接口。</p>
    <ul>
        <li class="red">D表示文档,HTML文档结构。</li>
        <li title="列表项目2">O表示对象,文档结构的JavaScript脚本化映射。</li>
        <li style="color:red;">M表示模型,脚本与结构交互的方法和行为。</li>
    </ul>
    <script>
    var ul = document.getElementsByTagName("ul")[0]; //获取列表元素
    ul.onclick = function(){                         //绑定事件处理程序
     this.style.border= "solid blue 1px";
    }
    var ul1 = ul.cloneNode(true);                    //深度克隆
    document.body.appendChild(ul1);                  //添加到文档树中body元素下
    </script>

2、文档

文档节点代表整个文档,使用document可以访问文档节点,它是文档内其他节点的访问入口,提供了操作其他节点的方法。文档节点是唯一的,也是只读的,主要特征:nodeType等于9、nodeName等于"#document"、nodeValue等于null、parentNode等于null、ownerDocument等于null。

2.1、访问文档

在不同环境中,获取文档节点的方法也不同,具体说明如下:

  • 在文档内部节点,使用ownerDocument访问。
  • 在脚本中,使用document访问。
  • 在框架页,使用contentDocument访问。
  • 在异步通信中,使用XMLHttpRequest对象的responseXML访问。

2.2、访问子节点

文档子节点包括以下元素:

  • doctype文档类型,如
  • html元素,如
  • 处理指令,如
  • 注释,如

访问方法如下:

  • 使用document.documentElement可以访问html元素。
  • 使用document.doctype可以访问doctype。注意,部分浏览器不支持。
  • 使用document.childNodes可以遍历子节点。
  • 使用document.firstChild可以访问第一个子节点,一般为doctype。
  • 使用document.lastChild可以访问最后一个子节点,如html元素或者注释。

2.3、访问特殊元素

文档中存在很多特殊元素,使用下面的方法可以获取,如果获取不到,将返回null。

  • 使用document.body可以访问body元素。
  • 使用document.head可以访问head元素。
  • 使用document.defaultView可以访问默认视图,即所属的窗口对象window。
  • 使用document.scrollingElement可以访问文档内滚动的元素。
  • 使用document.activeElement可以访问文档内获取焦点的元素。
  • 使用document.fullscreenElement可以访问文档内正在全屏显示的元素。

2.4、访问元素集合

document包含一组集合对象,使用它们可以快速地访问文档内元素,简单说明如下:

2.5、访问文档信息

document包含很多信息,简单说明如下:

1.静态信息

  • document.URL:返回当前文档的网址。
  • document.domain:返回当前文档的域名,不包含协议和接口。
  • document.location:访问location对象。
  • document.lastModified:返回当前文档最后修改的时间。
  • document.title:返回当前文档的标题。
  • document.characterSet:返回当前文档的编码。
  • document.referrer:返回当前文档的访问者来自哪里。
  • document.dir:返回文字方向。
  • document.compatMode:返回浏览器处理文档的模式,值包括BackCompat(向后兼容模式)和CSS1Compat(严格模式)。

2.状态信息

  • document.hidden:表示当前页面是否可见。如果窗口最小化、切换页面,document.hidden返回true。
  • document.hidden:表示当前页面是否可见。如果窗口最小化、切换页面,document.hidden返回true。
  • document.readyState:返回当前文档的状态。取值包括loading(正在加载)、interactive(加载外部资源)、complete(加载完成)。

2.6、访问文档元素

document对象包含多个访问文档内元素的方法,简单说明如下:

  • getElementById():返回指定id属性值的元素。注意,id值要区分大小写,如果找到多个id相同的元素,则返回第一个元素,如果没有找到指定id值的元素,则返回null。
  • getElementsByTagName():返回所有指定标签名称的元素节点。
  • getElementsByName():返回所有指定名称(name属性值)的元素节点。该方法多用于表单结构中,用于获取单选按钮组或复选框组。
  • getElementsByName():返回所有指定名称(name属性值)的元素节点。该方法多用于表单结构中,用于获取单选按钮组或复选框组。

【示例】先获取所有图片,再通过namedItem(“news”);找到name为news的图片:

    <img src="1.gif" />
    <img src="2.gif" name="news" />
    <script>
    var images = document.getElementsByTagName("img");
    var news = images.namedItem("news");
    </script>

也可以使用下面用法获取页面中所有的元素,其中参数*表示所有元素:

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

3、元素

元素节点的主要特征:nodeType等于1、nodeName等于标签名称、nodeValue等于null。元素节点包含5个公共属性,即id(标识符)、title(提示标签)、lang(语言编码)、dir(语言方向)、className(CSS类样式),这些属性可读可写。

3.1、访问元素

1.getElementById()方法

使用getElementById()方法可以准确地获取文档中指定的元素,用法如下:

    document.getElementById(ID)

参数ID表示文档中对应元素的id属性值。如果文档中不存在指定元素,则返回值为null,该方法只适用于document对象。

【示例1】使用getElementById()方法获取

对象,然后使用nodeName、nodeType、parentNode和childNodes属性查看该对象的节点类型、节点名称、父节点和第一个子节点的名称:

    <div id="box">盒子</div>
    <script>
    var box = document.getElementById("box");               //获取指定盒子的引用
    var info = "nodeName:" + box.nodeName;                 //获取该节点的名称
    info += "\rnodeType:" + box.nodeType;                  //获取该节点的类型
    info += "\rparentNode:" + box.parentNode.nodeName;     //获取该节点的父节点名称
    info += "\rchildNodes:" + box.childNodes[0].nodeName;  //获取该节点的子节点名称
    console.log(info);                                      //显示提示信息
    </script>

2.getElementByTagName()方法

使用getElementByTagName()方法可以获取指定标签名称的所有元素,用法如下:

    document.getElementsByTagName(tagName)

参数tagName表示指定名称的标签,该方法返回值为一个节点集合,使用length属性可以获取集合中包含元素的个数,利用下标可以访问其中某个元素对象。

【示例2】使用for循环获取每个p元素,并设置p元素的class属性为red:

    var p = document.getElementsByTagName("p");    //获取p元素的所有引用
    for(var i=0;i<p.length;i++){                   //遍历p数据集合
        p[i].setAttribute("class","red");          //为每个p元素定义red类样式
    }

3.2、遍历元素

使用parentNode、nextSibling、previousSibling、firstChild和lastChild属性可以遍历文档树中任意类型节点,包括空字符(文本节点)。HTML5新添加5个属性专门访问元素节点。

  • childElementCount:返回子元素的个数,不包括文本节点和注释。
  • firstElementChild:返回第一个子元素。
  • lastElementChild:返回最后一个子元素。
  • previousElementSibling:返回前一个相邻兄弟元素。
  • nextElementSibling:返回后一个相邻兄弟元素。

浏览器支持:IE 9+、Firefox 3.5+、Safari 4+、Chrome和Opera 10+。

3.3、创建元素

使用document对象的createElement()方法能够根据参数指定的标签名称创建一个新的元素,并返回新建元素的引用。用法如下:

    var element = document.createElement("tagName");

其中,element表示新建元素的引用,createElement()是document对象的一个方法,该方法只有一个参数,用来指定创建元素的标签名称。

【示例1】在当前文档中创建一个段落标记p,存储到变量p中。由于该变量表示一个元素节点,所以它的nodeType属性值等于1,而nodeName属性值等于p:

    var p = document.createElement("p");     //创建段落元素
    var info = "nodeName:" + p.nodeName;    //获取元素名称
    info += ",nodeType:" + p.nodeType;     //获取元素类型,如果为1则表示元素节点
    console.log(info);

使用createElement()方法创建的新元素不会被自动添加到文档里。如果要把这个元素添加到文档里,还需要使用appendChild()、insertBefore()或replaceChild()方法实现。

【示例2】演示把新创建的p元素增加到body元素下。当元素被添加到文档树中,就会立即显示出来:

    var p = document.createElement("p");      //创建段落元素
    document.body.appendChild(p);             //增加段落元素到body元素下

3.4、复制元素

cloneNode()方法可以创建一个节点的副本。

【示例】演示复制一个元素及其所有子节点。当复制其中创建的标题1节点之后,该节点所包含的子节点及文本节点都将复制过来,然后增加到body元素的尾部:

    var p = document.createElement("p");              //创建一个p元素
    var h1 = document.createElement("h1");            //创建一个h1元素
    var txt = document.createTextNode("Hello World"); //创建一个文本节点,文本内容为“Hello World”
    p.appendChild(txt);                               //把文本节点增加到段落中
    h1.appendChild(p);                                //把段落元素增加到标题元素中
    document.body.appendChild(h1);                    //把标题元素增加到body元素中
    var new_h1 = h1.cloneNode(true);                  //复制标题元素及其所有子节点
    document.body.appendChild(new_h1);                //把复制的新标题元素增加到文档中

注意:由于复制的节点会包含原节点的所有特性,如果原节点中包含id属性,就会出现id属性值重叠的情况。一般情况下,在同一个文档中,不同元素的id属性值应该不同。为了避免潜在冲突,应修改其中某个节点的id属性值。

3.5、插入元素

在文档中插入节点主要包括两种方法:

1.appendChild()方法

appendChild()方法可向当前节点的子节点列表的末尾添加新的子节点,用法如下:

    appendChild(newchild)

参数newchild表示新添加的节点对象,并返回新增的节点。

【示例】展示把段落文本增加到文档中指定的div元素中,使它成为当前节点的最后一个子节点:

    <div id="box"></div>
    <script>
    var p = document.createElement("p");            //创建段落节点
    var txt = document.createTextNode("盒模型");    //创建文本节点,文本内容为“盒模型”
    p.appendChild(txt);                             //把文本节点增加到段落节点中
    document.getElementById("box").appendChild(p);  //获取box元素,把段落节点增加进来
    </script>

如果文档树中已经存在参数节点,则将从文档树中删除,然后重新插入新的位置。如果添加节点是DocumentFragment节点,则不会直接插入,而是把它的子节点插入当前节点的末尾。

2.insertBefore()方法

使用insertBefore()方法可以在已有的子节点前插入一个新的子节点,用法如下:

    insertBefore(newchild,refchild)

其中,参数newchild表示新插入的节点,refchild表示插入新节点后的节点,用于指定插入节点的后面相邻的位置。插入成功后,该方法将返回新插入的子节点。

提示:insertBefore ()方法与appendChild()方法一样,可以把指定元素及其所包含的所有子节点都一起插入指定位置中。同时会先删除移动的元素,然后再重新插入新的位置。

3.6、删除元素

removeChild()方法可以从子节点列表中删除某个节点,用法如下:

    nodeObject.removeChild(node)

其中,参数node为要删除的节点。如果删除成功,则返回被删除节点;如果删除失败,则返回null。

当使用removeChild()方法删除节点时,该节点所包含的所有子节点将同时被删除。

【示例】单击按钮时将删除红盒子中的一级标题:

    <div id="red">
        <h1>红盒子</h1>
    </div>
    <div id="blue">蓝盒子</div>
    <button id="ok">移动</button>
    <script>
    var ok = document.getElementById("ok");             //获取按钮元素的引用
    ok.onclick = function(){                            //为按钮注册一个鼠标单击事件处理函数
     var red = document.getElementById("red");          //获取红色盒子的引用
     var h1 = document.getElementsByTagName("h1")[0];   //获取标题元素的引用
     red.removeChild(h1);                               //移出红盒子包含的标题元素
    }
    </script>

3.7、替换元素

replaceChild()方法可以将某个子节点替换为另一个子节点,用法如下:

    nodeObject.replaceChild(new_node,old_node)

其中,参数new_node为指定新的节点,old_node为被替换的节点。如果替换成功,则返回被替换的节点;如果替换失败,则返回null。

【示例】以上节示例为基础,重写脚本,新建一个二级标题元素,并替换红色盒子中的一级标题元素:

    var ok = document.getElementById("ok");             //获取按钮元素的引用
    ok.onclick = function(){                            //为按钮注册一个鼠标单击事件处理函数
     var red = document.getElementById("red");          //获取红色盒子的引用
     var h1 = document.getElementsByTagName("h1")[0];   //获取一级标题的引用
     var h2 = document.createElement("h2");             //创建二级标题元素,并引用
     red.replaceChild(h2,h1);                           //把一级标题替换为二级标题
    }

演示发现,当使用新创建的二级标题替换一级标题之后,则原来的一级标题所包含的标题文本已经不存在。这说明,替换节点的操作不是替换元素名称,而是替换其包含的所有子节点,以及其包含的所有内容。

4、文本

文本节点表示元素和属性的文本内容,包含纯文本内容、转义字符,但不包含HTML代码。文本节点不包含子节点。其主要特征:nodeType等于3、nodeName等于"#text"、nodeValue等于包含的文本。

4.1、创建文本

使用document对象的createTextNode()方法可以创建文本节点,用法如下:

    document.createTextNode(data)

参数data表示字符串。

【示例】创建一个新div元素,并为它设置class值为red,然后添加到文档中:

    var element = document.createElement("div");
    element.className = "red";
    document.body.appendChild(element);

注意:由于DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。为了避免这种情况,一般应该在父元素上调用normalize()方法,删除空文本节点,合并相邻文本节点。

4.2、访问文本

使用nodeValue或data属性可以访问文本节点包含的文本。使用length属性可以获取包含文本的长度,利用该属性可以遍历文本节点中的每个字符。

4.3、读取HTML字符串

使用元素的innerHTML属性可以返回调用元素包含的所有子节点对应的HTML标记字符串。最初它是IE的私有属性,HTML5规范了innerHTML的使用,并得到所有浏览器的支持。

【示例】使用innerHTML属性读取div元素包含的HTML字符串:

    <div id="div1">
        <style type="text/css">p { color:red;}</style>
        <p><span>div</span>元素</p>
    </div>
    <script>
    var div = document.getElementById("div1");
    var s = div.innerHTML;
    console.log(s);
    </script>

4.4、插入HTML字符串

使用innerHTML属性可以根据传入的HTML字符串,创建新的DOM片段,然后用DOM片段完全替换调用元素原有的所有子节点。设置innerHTML属性值之后,可以像访问文档中的其他节点一样访问新创建的节点。

【示例】创建一个1000行的表格。先构造一个HTML字符串,然后更新DOM的innerHTML属性:

    <script>
    function tableInnerHTML() {
        var i, h = [''];
        h.push('');
        h.push('');for( i =1; i <=1000; i++){
            h.push('
id<\/th>yes?<\/th>name<\/th>url<\/th>action<\/th><\/tr>'); h.push('<\/thead>'); h.push('
'); h.push(i); h.push('<\/td>'); h.push('And the answer is... ' + (i % 2 ? 'yes' : 'no')); h.push('<\/td>'); h.push('my name is #' + i); h.push('<\/td>'); h.push('+ i + '.html">http://example.org/' + i + '.html<\/a>'); h.push('<\/td>'); h.push('