使用JavaScript遍历文档对象模型

万维网联盟( W3C )在不同的规范组(DOM级别1,DOM级别2和DOM级别3)中定义了文档对象模型(DOM)。 DOM将HTML或XML文档表示为由具有属性和方法的节点层次结构组成的树。 使用JavaScript等客户端语言,您可以将事件添加,修改,删除并将事件附加到树中的节点,从而可以生成交互式动态网页。

使用客户端脚本(JavaScript)修改DOM称为DOM脚本。 使用DOM脚本来代替通用术语“动态HTML(DHTML)”,该术语已在Web开发中用于指示通过HTML,CSS和JavaScript构建的交互式Web页面。

在本文中,探索DOM API中最常用的方法和属性。 一个详细的示例显示了如何使用JavaScript遍历DOM。 一个更复杂的模型说明了在何处考虑事件和侦听器。 了解如何利用JavaScript库与DOM进行交互。

您可以下载本文中使用的源代码。 相关主题为希望深入了解本文中讨论的概念的人员提供了链接。

DOM脚本

在DOM术语中,文档表示为树的根。 在JavaScript中,它是window.document ,或者仅仅是document(因为它附加到Window对象)。 这是某些JavaScript实现的起点。 清单1显示了一个HTML片段的示例。

清单1. HTML代码
<body>
   <p id="paragraph1">
      <span>This is some text</span>
      <a href="/index.html" title="Click here">Click here</a>
   <p>
</body>

从DOM的角度来看,在上面的示例中, p标签由DOM Element接口表示。 它是span标记和a标记的父代。 该spana标签是兄弟姐妹。

假设您想在清单1的代码中获取锚点的href属性。 访问DOM中元素的一种简单方法是使用getElementById方法。 以下代码字符串显示了包含以接口定义语言(IDL)编写的getElementById签名的文档接口定义的一部分: Element getElementById (in DOMString elementId)

JavaScript使用String对象实现DOMString接口,因此该方法接受元素id作为字符串形式的参数。 在示例片段中,唯一具有id属性的元素是p标签,因此可以使用var paragraph = document.getElementById("paragraph1");进行检索var paragraph = document.getElementById("paragraph1");

您可以使用childNodes属性获取嵌套在p标签中的锚点。 此属性属于Node接口,并返回NodeList类型的对象。 该对象是JavaScript中类似数组的对象。 类似数组的对象没有诸如pop()push() ,但是它们具有length属性。 从childNodes属性返回的对象在节点元素(HTML标记),文本节点或注释节点之间没有任何区别。 如果仅查找节点元素,则可以考虑children属性。 在不考虑文本和注释节点的情况下,它的性能要优于childNodes 。 在该示例中,锚点是该段落的第二个子var aElement = paragraph.children[1]; ,可以通过以下方式获得: var aElement = paragraph.children[1];

给定一个元素,要获取href属性的值,可以通过将属性名称作为参数传递(在本例中为href )来采用getAttribute方法。 IDL定义中包含getAttribute方法的部分是: DOMString getAttribute (in DOMString name)

在示例中,您可以像这样实现上述接口: var aHref = aElement.getAttribute("href"); // "index.html" var aHref = aElement.getAttribute("href"); // "index.html"

与JavaScript中一样,您可以链接方法。 要仅在一行中获得a标签的href属性值,请使用: var aHref = document.getElementById("paragraph1").children[1].getAttribute("href"); // index.html */ var aHref = document.getElementById("paragraph1").children[1].getAttribute("href"); // index.html */

深入研究DOM脚本:示例应用程序

本节探讨DOM脚本编写中的一些功能。 示例便笺应用程序是一个交互式网页,使用户无需重新加载页面即可添加“便笺”便笺。 图1显示了该页面。

图1. Sticky Notes应用程序前端
即时贴应用程序前端

清单2中显示了图1中显示的页面HTML代码。 head标签内是对CSS和JS文件的引用。 在body标记中,您可以看到页面中已有注释的结构: textarea标记和触发创建新注释的锚点。

清单2. HTML代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8">
        <title>
            Dom Scripting
        </title>
        <link rel="stylesheet" href="css/master.css" />
        <script src="js/script.js"></script>
    </head>
    <body>
        <div class="wrapper">
            <h1> Sticky Notes </h1>
            <div class="links">
                <textarea id="contentArea" cols="10"> </textarea>
                <a href="/random.html" class="add">Click here</a> 
<span>to add a sticky note</span>
            </div>
            <div id="notes">
                <div class="note">
                    <p>
                        This is a note
                    </p>
                </div>
            </div>
        </div>
    </body>
</html>

让我们分析该页面加载的script.js文件中包含JavaScript代码。 一旦页面加载或文档构建完成,就需要触发脚本的逻辑。 为此,一种选择是将一个函数绑定到onload window属性,如清单3所示。

清单3. onload属性
window.onload = init;
function init() {}

onload属性与DOM事件负载相关联,这是在DOM级别0(所有浏览器均支持的“规范”,但不是标准)下如何将事件绑定到侦听器功能的典型方法。 相反,在DOM级别2规范中定义了标准的DOM事件模型。 在此规范中,定义了addEventListener方法(来自EventTarget接口)以在目标元素上注册事件处理程序。 以下代码公开了此方法的签名: object.addEventListener(eventType, eventHandler, useCapture);

其中eventType是要在对象上注册的事件,而eventHandler是绑定到特定事件的函数。 useCapture是一个可选的布尔值,用于定义函数将在事件流的哪个阶段被调用(冒泡或捕获)。 以下代码使用addEventListener函数将load方法绑定到窗口: window.addEventListener("load", init, false);

不幸的是,版本9之前的Internet Explorer(IE)不支持上述W3C方法,并且具有自己的实现: object.attachListener(eventType, eventHandler); 。 请参阅相关主题 ,以了解有关DOM Level 3的事件IE支持信息。

eventType需要在事件名称on加上前缀。 默认情况下,IE气泡中的事件,因此不存在useCapture参数。

清单4取自script.js,显示了addEvent函数,该函数处理所有浏览器中的事件绑定。 它是称为SA的全局对象的方法。 该方法适用于前面讨论的所有方法。

清单4. addEvent函数
window.SA  = {
addEvent : function(element, evType, fn, useCapture) { 
        if (element.addEventListener) { 
            element.addEventListener (evType, fn, useCapture); 
            return true; 
        } else if (element.attachEvent) { 
            var r = element.attachEvent('on' + evType, fn); 
            return r; 
        } else { 
            element['on' + evType] = fn; 
        } 
    }
}

如果使用addEvent函数,则可以将一个函数(称为SA.load )绑定到load事件,如清单5所示。

清单5.绑定函数
SA.addEvent(window, "load", SA.load, false);
SA = {
...
           load : function() {
                       // init block
           }
}

上面的SA.load函数仅在下载所有资源后才触发,因为它附加到load事件。 在一般情况下,附加到load事件的函数可能需要一段时间才能执行,尤其是在页面中有许多图像要下载的情况下。 将初始化脚本的功能附加到DOMContentLoaded事件是一个好习惯,该事件在现代浏览器中受支持,并在构建DOM时触发。 该功能将在下载外部资源之前执行,从而使页面更具响应性。 在版本9之前,IE没有开箱即DOMContentLoaded包含DOMContentLoaded事件,因此需要一种变通方法以使其像其他浏览器一样工作。 在此示例中,页面中没有任何图像,因此您可以保持加载方式(页面的性能不会受到很大影响)。

现在您可以将函数处理程序与目标锚点上的click事件相关联。 当用户单击锚点时,将执行特定的行为。 在示例中,将创建一个新注释。 第一个任务是遍历DOM以检索我们定位的锚点,如清单6所示。

清单6.检索到具有类名add锚点
load : function() {
  var anchorSelected;
        
  if (document.getElementsByClassName) {
    anchorSelected = document.getElementsByClassName("add")[0];
  } else {
    var anchors = document.getElementsByTagName("a"),
        alenght = anchors.length;
        
    for (var i = 0; i < alenght; i++ ) {
      var anchor = anchors[i];
            
      if (anchor.className === "add") {
      anchorSelected = anchor;
    }
    }
  }
}

在清单6中,您可能可以预测到document.getElementsByClassName方法使您可以检索具有给定类名的元素。 此方法返回HTML元素的集合,但是不幸的是,并非所有浏览器(例如IE6和IE7)都完全支持此方法。 对于那些浏览器,需要编写不同的逻辑。 您可以首先通过document.getElementsByTagName方法获取锚点列表,然后遍历该列表以使用名为addCSS类获得锚点。 GetElementsByTagName方法正式返回一个NodeList对象,幸运的是,所有主要浏览器都完全支持该方法。

清单6中,您将看到如何在alength变量中存储锚点数组的大小,以便在for循环中仅查询DOM一次。 修改和使用DOM是一项昂贵的操作,因此您应尽量减少与DOM交互的次数。

此时,一旦检索到锚点,就可以将click事件绑定到负责添加注释的侦听器函数,如清单7所示。

清单7.绑定事件
load : function() {
  ...
  SA.addEvent(anchorSelected, "click", SA.addNote, false);
}

清单7显示了附加到click事件的事件侦听器称为SA.addNote 。 此功能有几个目标:

  • 克隆最新创建的笔记
  • 将用户键入的文本注入刚刚克隆的笔记中
  • 将新笔记追加到笔记列表中

清单8显示了实现第一个目标的实现。

清单8.克隆最新创建的笔记
addNote : function(event) {
     var notes = document.getElementById("notes");
        
     // Clone the node
     var newNode = notes.children[0].cloneNode(true);
},

通过getElementById方法获取具有注释ID的div标签后,您将检索嵌套在div的第一个子级,并使用cloneNode方法对其进行克隆。 将刚刚克隆的DOM节点存储在名为newNode的变量中。

选择嵌套在newNode内的段落节点,在克隆的节点上调用getElementsByTagName方法。 DOM提供了一个名为textContent的属性来获取节点的内容。 不幸的是,并非所有浏览器都完全支持它。 您需要采用另一种方法:在该段中,访问firstChild属性,然后firstChild检索nodeValue属性。 现在,使用页面中存在的textarea标记的内容设置刚刚获得的nodeValuetextarea的内容来自通过getElementById方法实现的textarea DOM元素的value属性。 清单9显示了如何将用户键入的文本注入到刚刚克隆的笔记中(第二个目标)。

清单9.将文本区域中的文本注入刚刚克隆的笔记中
addNote : function(event) {
  ...
 // Set the content of the node
 newNode.getElementsByTagName("p")[0].firstChild.nodeValue =  
 document.getElementById("contentArea").value;
        
 notes.appendChild(newNode);
}

对于最后一个目标,使用appendChild方法将创建的新笔记追加到笔记列表中,如清单10所示。

清单10.将新注释追加到列表
addNote : function(event) {
  ...

 notes.appendChild(newNode);
}

最后,您需要防止click事件的默认行为(对于锚点,默认行为是将用户重定向到href属性中指定的URL)。 DOM通过将参数传递给处理函数来指定preventDefault()方法来完成此任务,将其应用于事件。 同样,在版本9之前的IE中不支持此方法。要实现相同的目标,在9版之前的IE中,可以将event.returnValue属性设置为false。 清单11显示了代码。

清单11.防止click事件的默认行为
addNote : function(event) {
  ...

 event.preventDefault ? event.preventDefault() : event.returnValue = false;
}

清单12显示了script.js文件中包含的所有JavaScript代码。

清单12. Script.js
window.SA = {
    
addEvent : function(element, evType, fn, useCapture) { 
        if (element.addEventListener) { 
            element.addEventListener (evType, fn, useCapture); 
            return true; 
        } else if (element.attachEvent) { 
            var r = element.attachEvent('on' + evType, fn); 
            return r; 
        } else { 
            element['on' + evType] = fn; 
        } 
    },
    
    load : function() {
        
        var anchorSelected;
        
        if (document.getElementsByClassName) {
anchorSelected =  document.getElementsByClassName("add")[0];

        } else {
            var anchors = document.getElementsByTagName("a"),
                alenght = anchors.length;
        
            for (var i = 0; i < alenght; i++ ) {
                var anchor = anchors[i];
            
                if (anchor.className === "add") {
                    anchorSelected = anchor;
                }
            }
        }
        
        SA.addEvent(anchorSelected, "click", SA.addNote, false);
    },
    
    addNote : function(event) {
        
        var notes = document.getElementById("notes");
        
        // Clone the node
        var newNode = notes.children[0].cloneNode(true);
            
        // Set the content of the node
        newNode.getElementsByTagName("p")[0].firstChild.nodeValue     
= document.getElementById("contentArea").value;
        
        notes.appendChild(newNode);
        
event.preventDefault ? event.preventDefault() : event.returnValue = false;
    }
}

SA.addEvent(window, "load", SA.load, false);

JavaScript库和DOM

当开发人员编写JavaScript代码时,他们经常使用JavaScript库或框架,这些库或框架在不同的浏览器中处理DOM的不同实现。 注意,如何使用流行的jQuery库重写清单13

清单13. Script-jquery.js
$(function(){
    $('a.add').click(function(){
        var newNote = $('.note').eq(0).clone();
        newNote.find('p').text($('#contentArea').val());
        $('#notes').append(newNote);
        return false;
    });
});

保存了几行代码,代码简洁整洁。

因为需要使用HTML导入库才能使用jQuery,如清单14所示,所以发出一个附加的HTTP请求,并且将需要更多时间来执行该库。 此过程可能会使应用程序变慢,因此由您来决定如何在编写较少代码的情况下平衡使用库。

清单14.将库导入HTML
<html>
    <head>
        ...
        <script src="//ajax.googleapis.com/ajax/libs/jquery
/1.6.1/jquery.min.js"></script>
    </head>

JavaScript库是非常强大的工具,可以使您的生活更轻松。 但是,您需要了解DOM脚本,因为使用库并不总是处理DOM的最有效方法。 还建议您了解图书馆幕后发生的事情。

结论

DOM对Web开发人员很重要,因为它是JavaScript访问网页的方式。 浏览器供应商实施DOM API的方式存在一些问题和限制。 并非所有浏览器都完全支持某些属性和方法(例如addEventListener()textContent ),或者在某些情况下它们的行为有所不同。

性能是DOM脚本要考虑的另一个重要因素。 如本文所述,只要您知道JavaScript和DOM的交互方式,就可以利用某些JavaScript框架来操纵和遍历DOM。


翻译自: https://www.ibm.com/developerworks/web/library/wa-jsdomupdate/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值