美文网首页
DOM(Document Object Model)

DOM(Document Object Model)

作者: DHFE | 来源:发表于2018-03-22 00:44 被阅读4次

    参考书:《JavaScript高级程序设计》


    知识点前提:什么是节点


    Node类型

    DOM1级定义了一个Node接口,该接口将DOM中所有节点类型实现。Node接口在JavaScript中是作为Node类型实现的;除了IE之外,在其他所有浏览器都可以访问到这个类型。

    nodeType

    只读

    每个节点都有一个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);

    test1

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <h1 id="h">i'm h1</h1>
    </body>
    <script>
        var h = document.getElementById("h");
        console.log(h.nodeType);                                    // 1
        if (h.nodeType === Node.ELEMENT_NODE) console.log(true);    // true
    </script>
    </html>
    

    使用Node类型常量来比较。
    由于IE没有公开Node类型函数,因此上面代码在IE中会导致错误。为确保浏览器兼容,最好将nodeType属性与数字进行比较

    test2

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <h1 id="h">i'm h1</h1>
    </body>
    <script>
        var h = document.getElementById("h");
        if (h.nodeType === 1) console.log(true);    // true
    </script>
    </html>
    

    不是所有节点都会受到浏览器支持,最常用的就是元素和文本节点。

    MDN —— NodeType


    nodaName | nodeValue

    nodaName
    只读

    nodaValue
    可写可读

    使用nodeName和nodeValue可以具体了解节点信息,这两个属性的值完全取决于节点的类型

    可以检测节点类型后在使用其属性,下图列出了不同节点类型的nodeValue的返回值。


    from MDN

    所以,对于element这样的节点,返回值都为null。

    test3

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <h1 id="h">i'm h1</h1>
    </body>
    <script>
        var h = document.getElementById("h");
        console.log(h.nodeName+" | "+h.nodeValue);
        // H1 | null
    </script>
    </html>
    

    可以这样

    test4

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <h1 id="h">i'm h1</h1>
    </body>
    <script>
        var h = document.getElementById("h").getAttributeNode("id");
        console.log(h);             //  id  [object attr]
        console.log(h.nodeValue);   //  h
    </script>
    </html>
    

    使用getAttributeNode方法,输入属性名,返回其attribute节点对象,然后使用NodeValue方法,返回值就为实际属性值。


    节点关系

    文档中所有的节点之间都存在这样或者那样的关系。节点间的各种关系可以用传统的家族关系来描述,每个节点存在childNodes属性,其中保存着一个Nodelist对象,是一种类数组对象,保存一组有序的节点,可以通过位置来访问这些节点。
    虽然可以使用方括号语法来访问Nodelist的值,而且这个对象也有length属性,但它并不是Array的实例。Nodelist对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反映在Nodelist对象中。

    我们常说,Nodelist是有生命、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。

    test5*

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div">
            <p>i'm P</p>
            <h1>i'm H1</h1>
        </div>
    </body>
    <script>
        var html = document.getElementsByTagName("html")[0];
        console.log(html);
    </script>
    </html>
    

    可以看到其中childNodes属性里,Nodelist对象的传承,包含了从根目录下的所有节点。
    其中,#div的Nodelist里,是否发现了有5个节点,但实际上源代码只有2个元素啊,其实[0],[2],[4]所代表的#text是我们源代码换行时留下的空位,在Nodelist里就用#text来表示了。
    <div id="div"><p>i'm P</p><h1>i'm H1</h1></div>
    修改后,将只有两个element。

    注意,length属性表示的是访问Nodelist的那一刻,其中包含的节点数量。
    对于JS函数内部类数组对象arguments对象使用Array.prototype.slice()方法可以将其转换为数组。而采用同样的方法,也可以将Nodelist对象转换为数组。

    test6

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div">
            <p>i'm P</p>
            <h1>i'm H1</h1>
        </div>
    </body>
    <script>
        var div = document.getElementById("div");
    
        // 将Nodelist对象转换为数组
        var arrayOfNodes = Array.prototype.slice.call(div.childNodes,0);
        
        // 修改数组不会反映到Nodelist
        arrayOfNodes.splice(1,0,arrayOfNodes[3]);
        console.log(div.childNodes);
    </script>
    </html>
    

    为方便演示,缩小范围,只抽取了#div做演示,
    白箭头即为转换后的真数组,对转换后数组进行了一次数组splice增添节点操作,那么,是否会映射到Nodelis上呢?
    红箭头是Nodelist对象,看看它,依然不变。

    当然,通过Nodelist对象方法添加是可以,后续~

    对于刚才的Nodelist对象转换数组方法,由于在IE8及更早版本将Nodelist实现为一个COM对象,而我们不能像使用JavaScript对象那样使用这种对象,因此原方法会导致错误,要想在IE中Nodelist转换为数组,必须手动枚举所有成员。
    下列代码在所有浏览器均可运行:

    test7

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div">
            <p>i'm P</p>
            <h1>i'm H1</h1>
        </div>
    </body>
    <script>
        function convertToArray(nodes) {
            var array = null;
            try {
                array = Array.prototype.slice.call(nodes,0);    // 针对非IE浏览器
            } catch (ex) {
                array = new Array();
                for (var i=0,len=nodes.length;i<len;i++) {
                    array.push(nodes[i]);
                }
            }e
            return array;
        }
        var div = document.getElementById("div");
        console.log(convertToArray(div.childNodes));
    </script>
    </html>
    

    Array(5) [#text, p, #text, h1, #text]

    函数首先尝试了创建数组的最简单的方式。如果导致错误(说明在IE8及更早版本中),则通过try-catch来捕获错误,然后手动创建数组。
    每个节点上都有一个parentNode属性,该属性指向文档树中的父节点。包含在childNodes列表中的所有节点都具有相同的父节点,因此它们的parentNode属性都指向同一个节点。此外,包含在childNodes列表中的每个节点相互之间都是同胞节点(siblingNode)。通过使用列表中的每个节点的previousSibling和nextSibling属性,可以访问同一列表中的其他节点。

    关于节点关系,主要就是子节点、节点、父节点之间的定位关系。


    test8

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
    </body>
    <script>
        var div = document.getElementById("div");
        var p = document.getElementById("p");
        var h = document.getElementById("h1"); 
    
    
        console.log(div.firstChild);        // 选中#div的第一个子元素:p#p
        console.log(div.lastChild);         // 选中#div的最后一个子元素:h1#h1
    
        console.log(p.nextSibling);         // 选中#p的下一个兄弟元素:h1#h1
        console.log(h.previousSibling);     // 选中#h1的上一个兄弟元素:p#p
    
        console.log(p.parentNode);          // 选中#p的父元素:div#div
    </script>
    </html>
    

    在反映这些关系的所有属性当中,childNodes属性与其他属性相比更方便一些,因为只须使用简单的关系指针,就可以通过它访问文档树中的任何节点。另外,hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回true;应该说,这是比查询childNodes列表的length属性更简单的方法。
    所有节点都有的最后一个属性时ownerDocument,该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在与两个或多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。


    操作节点

    因为关系指针都是只读的,所以DOM提供了一些操作节点的方法。其中,最常用的方法时appendChild(),用于向childNodes列表的末尾添加一个节点。添加节点后,childNodes的新增节点、父节点及以前的最后一个子节点的关系指针都会相应的得到更新(因为childNodes属性里的Nodelist对象更新了)。更新完成后,appendChild()返回新增的节点。

    test9

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div">
            <p id="p">i'm P</p>
            <h1 id="h1">i'm H1</h1>
        </div>
    </body>
    <script>
        var h6 = document.createElement("h6");
        var div = document.getElementById("div");
    
        var returnNode = div.appendChild(h6);
        console.log(returnNode === h6);         // true
        console.log(div.lastChild === h6);      // true
    </script>
    </html>
    

    如果传入到appendChild()中的节点已经是文档中的一部分了,那结果就是将该节点从原来的位置转移到新位置。即使可以将DOM数看成是由一系列指针连接起来的 ,但任何DOM节点也不能同时出现在文档中的多个位置上。因此,如果在调用appenChild()时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点。

    test10

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div">
            <p id="p">i'm P</p>
        </div>
    </body>
    <script>
        var div = document.getElementById("div");
        var returnNode = div.appendChild(div.firstChild);
    
        console.log(returnNode === div.firstChild);     // false
        console.log(returnNode === div.lastChild);      // true
    
    </script>
    </html>
    

    如果需要把节点放在childNodes列表中某个特定的位置上,而不是放在末尾,那么可以使用inserBefore()方法。这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(preivousSibling),同时被方法返回,如果参照节点是null,则insertBefore()与appendChild()执行相同的操作。

    test11

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
    </body>
    <script>
        var div = document.getElementById("div");
        var h1 = document.getElementById("h1");
        var p = document.getElementById("p");
        console.log(div);
    
        // 创建的新节点h6
        var h6 = document.createElement("h6");
        var text = document.createTextNode("i'm h6");
        h6.appendChild(text);
    
        // 将新创建的h6节点插入到h1之前
        div.insertBefore(h6,h1);
        console.log(h1.previousSibling === h6);     // true
        // p > h6 > h1
        
        // 将h1插入到p之前,同时h6位置为最后一个子节点
        div.insertBefore(h6,p);
        console.log(p.previousSibling === h6);      // true 
        console.log(div.firstChild === h6);         // true
        console.log(div.lastChild === h1);          // true
         
    </script>
    </html>
    
    注意,如果将代码换行,保证可读性的同时,会使Nodelist对象增加若干的#text节点,这是换行导致的。

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

    test12

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
        </div>
    </body>
    <script>
        var div = document.getElementById("div");
        var h1 = document.getElementById("h1");
        var p = document.getElementById("p");
    
        // 创建一个新节点h3
        var h3 = document.createElement("h3");
        h3.appendChild(document.createTextNode("i'm h3"));
    
        // 替换p节点
        var returnNode = div.replaceChild(h3,p);
        console.log(returnNode);            // p#p
        
    </script>
    </html>
    

    使用replaceChild()插入一个节点时,该节点所有关系指针都会从被它替换的节点赋值过来,尽管从技术上讲,被替换的节点还在文档中,但它在文档中已经没有了自己的位置。

    如果只想移除而非替换节点,可以使用removeChild()方法。
    方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值。
    test13

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
        </div>
    </body>
    <script>
        var div = document.getElementById("div");
        var h1 = document.getElementById("h1");
        var p = document.getElementById("p");
    
        // 创建一个新节点h3
        var h3 = document.createElement("h3");
        h3.appendChild(document.createTextNode("i'm h3"));
    
        // 替换p节点
        var returnNode = div.replaceChild(h3,p);
        console.log(returnNode);            // p#p
    
        // 移除第一个节点
        var formerFirstChild = div.removeChild(div.firstChild);
        console.log(formerFirstChild);      // h3
        console.log(div.firstChild);        // h1#h1
    </script>
    </html>
    

    有两个方法是所有类型节点都有的。

    • cloneNode()

    • normalize()

    cloneNode()方法接受一个布尔值参数,表示是否执行深复制。参数为true情况下,执行深复制,也就是复制节及其整个子节点树;在参数为false情况下,执行潜复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此这个节点副本就成为了一个“孤儿”,除非通过appedChild,insertBefore,replaceChild将它添加到文档中。

    test14

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Fuck!</title>
    </head>
    <body>
        <ul>
            <li>item 1</li>
            <li>item 2</li>
            <li>item 3</li>
        </ul>
    </body>
    <script>
        var ul = document.getElementsByTagName("ul")[0];
    
        var deepList = ul.cloneNode(true);
        var shallow = ul.cloneNode(false);
    
        console.log(deepList.childNodes.length);    //  7 4个空隙,三个元素,长度为7
        console.log(shallow.childNodes.length);     //  0 
    </script>
    </html>
    

    cloneNode()不会赋值添加到DOM节点中的JavaScript属性,例如事件处理程序等,这个方法只复制特性、子节点(true),其他一切都不会复制。


    normalize()

    这个方法唯一作用就是处理文档树中的文本节点。
    由于解析器的实现或者DOM操作等原因,可能会出现文本节点或者不包含文本,或者接连出现两个文本节点的情况。
    当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到空白文本节点,则删除它,如果找到相邻的文本节点,则将它们合并为一个文本节点。


    Document类型

    JavaScript通过Document类型表示文档。在浏览器汇总,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问。

    window.document === document // true

    Document节点具有下列特征:

    • nodeType的值为:9
    • nodeName的值为:"#document"
    • nodeValue的值为:null
    • parentNode的值为:null
    • 其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment。

    Document类型可以表示HTML页面或者其他基于XML的文档。不过,最常见的应用还是作为HTMLDocument实例的document对象。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。

    1.文档的子节点

    虽然DOM标准规定Document节点的子节点可以是DocumentType、Element、ProcessingIn-struction或Comment,但还有两个内置的访问其子节点的快捷方式。第一个就是documentElement属性,该属性始终指向HTML页面中的<html>元素。另一个就是通过childNodes列表访问文档元素,但通过documentElement属性则能更快捷、直接的访问元素。

    <html>
        <body>
            
        </body>
    </html>
    

    这个页面经浏览器解析后,其文档中只包含一个子节点,即<html>元素。可以通过documentElement或childNodes列表来访问这个元素。

            var html = document.documentElement;            // 取得对<html>的引用
            console.log(html === document.childNodes[0]);   // true
            console.log(html === document.firstChild);      // true
    

    documentElement、firstChild和childNodes[0]的值相同。,都指向<html>元素。

    作为HTMLDocument的实例,document对象还有一个body属性,直接指向<body>元素。因为开发人员经常要使用这个元素,所以document.body在JS代码汇总出现的频率很高。
    var body = document.body;
    所有浏览器都支持document.documentElement和document.body属性。
    Document另一个可能的子节点就是DocumentType。通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性来访问它的信息。
    var doctype = document.doctype;

    类似的,还有如下功能方法,取得文档信息。

    取得文档标题
    var originalTitle = document.title;
    设置文档标题
    document.title = "New page ttile";

    网页请求相关方法;
    • URL:属性中包含页面完整的URL(即地址栏中显示的URL)
    • domain:只包含页面的域名
    • referrer:保存链接到当前页面的页面的URL(从哪到这的),referrer属性可能包含空字符串。所有信息都存在请求HTTP头部,只不过通过这些属性让我们能够在JavaScript中访问它们而已。
    //取得完整URL
    var url = document.URL;
    
    //取得域名
    var domain = document.domain;
    
    //取得来源页面的URL
    var referrer = document.referrer;
    

    URL和domain属性是相互关联的。例如,如果document.URL为http://www.wrox.com/WileyCDA/,那么document.domain就是www.wrox.com

    这三个属性中,只有domain是可以设置的,但由于安全方面限制,并非可以给domain设置任何值。如果URL包含一个子域名,例如p2p.wrox.com,那么就只能将domain设置为wrox.com。不能将这个属性设置为URL中不包含的域。

            // 假设页面来自:p2p.wrox.com
            document.domain = "wrox.com";       // 成功
            document.domain = "nczonline.net";  // 出粗!
    

    当页面中包含来自其他子域的框架或内嵌框架时,能够设置domain就非常方便了。由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而通过将每个页面的document.domain设置为相同的值,这些页面就可以互相访问对方包含的JavaScript对象了。例如,假设有一个页面加载自www.wrox.com,其中包含一个内嵌框架,框架内的页面加载来自p2p.wrox.com。由于document.domain字符串不一样,内外两个页面之间无法相互访问对方的JavaScript对象。但如果将两个页面的document.domain值设置为wrox.com,他们之间就可以通信了。

    浏览器对domain属性还有一个限制,即如果域名一开始是松散的,那么不能将它设置为紧绷的。换句话说,在将document.domain设置为wrox.com之后,不能再将其设置回p2p.wrox.com,否认导致错误。

    // 假设页面来自于p2p.wrox.com域
    document.domain = "wrox.com";    // 松散的(成功)
    document.domain = "p2p.wrox.com"    // 紧绷的(出错!)
    

    查找元素

    Document类型提供的两个方法:

    • getElementById()
    • getElementsByTagName()

    第一个方法,接受一个参数:要取得的元素ID,如果找到相应的元素则返回该元素,如果不存在带有相应的ID的元素,则返回null。注意,这里的ID必须与页面中的元素的ID特性严格匹配,包括大小写。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="myDiv"></div>
        <script>
            var div = document.getElementById("myDiv");     // 取得对<div>元素的引用
        </script>
    </body>
    </html>
    

    但是,在IE7下和更早版本的浏览器都将返回null。
    var div = document.getElementById("mydiv");
    IE8及较早版本不区分ID的大小写,因此"myDiv"和"mydiv"会被当作相同的元素ID。
    如果页面中多个元素的ID值相同,getElmentById()只返回文档中第一次出现的元素。

    document.getElementsByTagName()
    接受一个参数,既要取得元素的标签名,返回的是包含零或多个元素的Nodelist。在HTML文档中,这个方法会返回一个HTMLCollection对象,作为一个动态集合,该对象与Nodelist对象非常类似。例如,下列代码回取得页面中所有的<img>元素,并返回一个HTMLCollection。
    var images = document.getElementsByTagName("img");
    这行代码会将一个HTMLCollection对象保存在images变量中。与Nodelist对象类似,可以使用方括号语法或item()方法来访问对象中的项。而这个对象中元素的数量则可以通过其length属性取得。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="myDiv"></div>
        <script>
            var divs = document.getElementsByTagName("div");
            console.log(divs.length);       // 1    输出div的数量
            console.log(divs[0].id);            // myDiv    输出第一个div元素的ID特性
            console.log(divs.item(0).id);   // myDiv    输出第一个div元素的ID特性
        </script>
    </body>
    </html>
    

    HTMLCollection对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的那么name特性取得集合中的项。例如,假设上面提到的页面中包含如下<div>元素
    <div id="myDiv" name="myDiv">
    那么可以通过如下方式从divs变量中取得这个<div>元素。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="myDiv" name="myDiv"></div>
        <script>
            var divs = document.getElementsByTagName("div");
            var div = divs.namedItem("myDiv");
            console.log(div);       // div#myDiv
        </script>
    </body>
    </html>
    

    咋提供按索引访问项的基础上,HTMLCollection还支持按名称访问项,这就为我们取得实际想要的元素提供了便利。而且,对命名的项也可以使用方括号语法来访问。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="myDiv"></div>
        <script>
            var divs = document.getElementsByTagName("div");
            var div = divs["myDiv"];
            console.log(div);   // div#myDiv
    
        </script>
    </body>
    </html>
    

    第三个方法,也就是只有HTMLDocument类型才有的方法,是getElementsByName(),顾名思义,方法会返回带有给定name特性的所有元素。最常使用getElementsByName()方法的情况是取得单选按钮。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <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>
    
        <script>
            var radios = document.getElementsByName("color");
        </script>
    </body>
    </html>
    

    所有的单选按钮的name特性值相同都是color,但它们的ID不同,ID作用在于将label元素应用到每个单选按钮,而name特性则用以确保三个值中只有一个被发送给浏览器。这样,我们取得所有单选按钮。

    特殊集合

    除了属性和方法,document对象还有一些特殊的集合,这些集合都是HTMLCollection对象,为访问文档常用的部分提供了快捷方式,包括:

    • document.anchors,包含文档中所有带name特性的<a>元素
    • document.applets,包含文档中所有的<applet>元素,很少使用。
    • document.forms,包含所有的<form>元素
    • document.images,包含所有的<img>元素
    • document.links,包含文档中所有的带href特性的<a>元素。

    Element类型

    访问元素的标签名,使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值,使用后者主要是为了清晰可见。
    <div id="myDiv"></div>
    可以先这样取得元素及其标签名:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="myDiv"></div>
        <script>
            var div = document.getElementById("myDiv");
            console.log(div.tagName);           // "DIV"
            console.log(div.tagName === div.nodeName);  // true
        </script>
    </body>
    </html>
    

    所有的HTML元素都由HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。

    • id,元素在文档中的唯一标识符
    • title,元素附加说明信息,通过工具提示条显示
    • lang,元素语言代码
    • dir,语言方向,ltr(left to right),rtl(right to left),很少使用。
    • className,与元素class属性对应。
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    
        <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
    
        <script>
            var div = document.getElementById("myDiv");
    
            console.log(div.id);            // myDiv
            console.log(div.className);     // bd
            console.log(div.title);         // Body text
            console.log(div.lang);          // en
            console.log(div.dir);           // ltr
        </script>
    </body>
    </html>
    

    也可以为每个属性赋值,修改对应属性的特性。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    
        <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
    
        <script>
            var div = document.getElementById("myDiv");
    
            div.id = "someOtherId";
            div.className = "ft";
            div.title = "Some other text";
            div.lang = "fr";
            div.dir = "rtl";
    
            console.log(div.id);        
            console.log(div.className); 
            console.log(div.title);     
            console.log(div.lang);      
            console.log(div.dir);           
            /*
            someOtherId
            ft
            Some other text
            fr
            rtl
            */
        </script>
    </body>
    </html>
    

    *取得特性

    getAttribute()

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    
        <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
    
        <script>
            var div = document.getElementById("myDiv");
    
            console.log(div.getAttribute("id"));    
            console.log(div.getAttribute("class")); 
            console.log(div.getAttribute("title"));     
            console.log(div.getAttribute("lang"));      
            console.log(div.getAttribute("dir"));           
            /*
            myDiv
            bd
            Body text
            en
            ltr
            */
        </script>
    </body>
    </html>
    

    传递给方法的特性名与实际的特性名相同,因此要想得到class的特性值,应该传入class而不是className,后者只有在通过对象属性访问特性时采用。如果给定名称特性名不存在,则返回null。

    当然,你可以添加自定义特性,标准HTML中没有的特性。
    <div id="myDiv" my_special_attrbute="hello!"></div>
    这个元素包含一个名为my_special_attrbute的自定义特性,值为hello!,可以像取得其他特性一样取得这个值。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
    
        <div id="myDiv" my_special_attrbute="hello!"></div>
    
        <script>
            var div = document.getElementById("myDiv");
            var value = div.getAttribute("my_special_attrbute");
            console.log(value);     // hello!
        </script>
    </body>
    </html>
    

    不过,只有公认的特性才会以属性的形式添加到DOM对象中
    <div id="myDiv" align="left" my_special_attribute="hello!"></div>
    因为id和align在HTML中是<div>的公认特性,因此该元素的DOM对象中也将存在对应的属性。不过,自定义特性my_special_attribute在Safari、Opera、Chrome及Firefox是不存在的;但IE却会为自定义特性也创建属性。

            console.log(div.id);                    // "myDiv"
            console.log(div.my_special_attribute);  // undefined(IE除外)
            console.log(div.align);                 // "left"
    

    有两类特殊的特性,它们虽然有对应的属性名,但属性值与通过getAttribute()返回的值并不相同,第一类是style,通过CSS为元素指定形式。通过getAttribute()访问时,返回的style特性值中包含的是CSS文本,而通过属性来访问它则返回一个对象。由于style属性用于以编程方式访问元素样式的,因此并没有映射到style属性。

    第二类与众不同的特性时onclick这样的事件处理程序,当在元素上使用时,onclick特性中包含的是JavaScript代码,如果通过getAttribute()访问,则会返回相应的代码的字符串。而在访问onclick属性时,则会返回一个JavaScript函数(如果未指定,返回null)这是因为onclick及其他事件处理程序属性本身就应该被赋予函数值。

    由于这些差别,通过JavaScript编程方式操作DOM时,开发者经常不使用getAttribute(),而是只使用对象属性,只有在取得自定义特性值情况下,才会使用getAttribute()方法。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <style>
    
    </style>
    <body>
    
        <div id="myDiv" style="background-color:red;width:100px;height:100px"></div>
    
        <script>
            var div = document.getElementById("myDiv");
            console.log(div.getAttribute("style"));     // background-color:red;width:100px;height:100px
            console.log(div.style);// CSSStyleDeclaration {0: "background-color", 1: "width", 2: "height", alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
        </script>
    </body>
    </html>
    

    设置特性
    与getAttribute()对应的方法是setAttribute(),这个方法接受两个参数:要设置的特性名和值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <style>
    
    </style>
    <body>
    
        <div></div>
    
        <script>
            var div = document.getElementsByTagName("div")[0];
    
            div.setAttribute("id","someOtherId");
            div.setAttribute("class","ft");
            div.setAttribute("title","Some other text");        
            div.setAttribute("lang","fr");
            div.setAttribute("dir","rtl");
        </script>
    </body>
    </html>
    

    通过方法既可以操作HTML特性,也可以操作自定义特性。通过这个方法设置的特性名会统一转换为小写形式,即ID最终会成为id。

    不过,添加自定义属性,该属性不会自动成为元素的特性。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <style>
    
    </style>
    <body>
    
        <div></div>
    
        <script>
            var div = document.getElementsByTagName("div")[0];
            
            div.mycolor = "red";
            console.log(div.getAttribute("mycolor"));       // null
        </script>
    </body>
    </html>
    

    attributes属性

    Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与Nodelist类似,也是一个动态集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap拥有下列方法:

    • getNamedItem(name):返回nodeName属性等于name的节点;
    • removeNamedItem(name):从列表中移除nodeName属性等于name的节点;
    • setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引;
    • item(pos):返回位于数字pos位置处的节点。

    attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是特性的值。要取得元素的id特性,可以:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <style>
    
    </style>
    <body>
    
        <div id="myDiv"></div>
    
        <script>
            var div = document.getElementById("myDiv");
            var id = div.attributes.getNamedItem("id").nodeValue;
            console.log(id);    // myDiv
        </script>
    </body>
    </html>
    

    或者使用方括号语法通过特性名称访问节点的简写方式。
    var id = div.attributes["id"].nodeValue;
    也可以通过以上的方法设置特性的值。
    div.attributes["id"].nodeValue = "someOtherId";

    一般来说,attributes的方法不够方便,更多的会使用getAttribute()、removeAttribute()和setAttribute()方法。
    不过,如果想要遍历元素特性,attributes属性可以派上用场。在需要将DOM结构序列化为XML或者HTML字符串时,多数都会涉及遍历元素特性。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <style>
    </style>
    
    <body>
    
        <div id="myDiv" class="div_class" align="left" name="DIV"></div>
    
        <script>
    
            var div = document.getElementById("myDiv");
    
            function outputAttributes(element) {
                var attrName;
                var attrValue;
                var pairs = [];
                var len = element.attributes.length;
    
                for (var i = 0; i < len; i++) {
                    attrName = element.attributes[i].nodeName;
                    attrValue = element.attributes[i].nodeValue;
                    pairs.push(attrName + '=\"' + attrValue + '\"');
                }
                return pairs;
            }
            
            console.log(outputAttributes(div));
        </script>
    </body>
    </html>
    

    针对attributes对象中的特性,不同浏览器返回的顺序不同。这些特性在XML和HTML代码中出现的先后顺序,不一定与它们出现在attributes对象中的顺序一致。
    IE7及更早版本会返回HTML元素中所有可能的特性,包括没有指定的特性。换句话说,返回100多个特性的情况会很常见。

    创建元素
    var div = document.createElement("div");
    使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性。此时,还可以操作元素的特性,为它添加更多子节点,以及执行其他操作。

            div.id = "myNewDiv";
            div.className = "box";
    

    然后添加到文档数中。可以使用appendChild()、insertBefore()、replaceChild()方法。

    document.body.appendChild(div);

    相关文章

      网友评论

          本文标题:DOM(Document Object Model)

          本文链接:https://www.haomeiwen.com/subject/awecqftx.html