009 Node 类型

作者: 柏丘君 | 来源:发表于2017-08-15 18:01 被阅读13次

    DOM 是也叫文档对象模型,是 HTML 和 XML 文档的一个 API,其描述了一个层次节点树,允许开发人员对文档树进行操作。

    Node 接口

    DOM 树是由一个一个的节点构成的,DOM1 级中定义了一个 Node 接口,这个接口会由页面中的一个个节点去实现。
    在 JavaScript 中,Node 是一个内置对象:

    Node
    // ƒ Node() { [native code] }
    

    Node 节点共分为 12 种类型,每一种类型都由 Node 对象上的一些常量来表示:

    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
    

    JavaScript 中每个节点对象都有一个 nodeType 属性,该属性的值对应着上面的 12 个常量:

    document.nodeType //9
    document.body.nodeType //1
    

    注:由于 IE 中的所有 DOM 对象都是以 COM 对象呈现的,并且 IE 没有公开 Node 对象的构造函数,因此在 IE 中使用 Node 对象上的节点类型常量可能会发生错误,因此最好使用节点对象的 nodeType 属性,以保证兼容性。
    注(2017-08-17):我测试了一下,在 IE9 以下访问 Node 构造函数会抛出错误,在 IE9 以上可以正常使用 Node 的构造函数。

    nodeName 和 nodeValue

    节点的 nodeName 属性表示元素的标签名,nodeValue 属性表示元素的节点值。对于元素节点 nodeValue 属性将返回 null

    document.body.nodeName // "BODY"
    document.body.nodeValue // null
    

    childNodes

    每个节点都有一个 childNodes 属性,里面保存了一个 NodeList 对象,该对象是一个类数组对象,用来保存一组有序节点。
    该对象是一个动态查询的结果,因此节点的变化将会自动在 NodeList 对象中更新。该对象有个 length 属性,表示对象中保存的节点数量,同样,每当节点发生变化时,该属性也会自动变化。

    let div = document.createElement("div")
    let nodeLists = document.body.childNodes //[]
    document.body.appendChild(div)
    nodeLists // [div]
    nodeList.length // 1
    document.body.removeChild(div)
    nodeList // [div]
    nodeList.length // 1
    

    上面的结果表明:节点 childNodes 属性中保存的 NodeLists 对象是随节点的变化动态更新的。
    获取 NodeLists 中的节点可以使用 [] 语法或者 item 方法:

    const node1 = nodeLists[1]
    const node2 = nodeLists.item(1)
    

    firstChild 和 lastChild

    firstChildlastChild 分别指向 NodeLists 中的第一个和最后一个节点,其值分别对应于 NodeLists[0] 以及 NodeLists[NodeLists.length - 1]
    注:firstChildlastChild 是父节点的属性,而非 NodeLists 集合的属性。

    const body = document.body
    body.firstChild === body.childNodes[0] // true
    ...
    

    perviousSibling 和 nextSibling

    每个节点都有一个 previousSiblingnextSibling 属性,分别指向其前一个和后一个兄弟节点。如果元素没有前一个或后一个兄弟节点,那么其 previousSiblingnextSibling 的值为 null

    const body = document.body
    body.previousSibling // <head>...</head>
    body.nextSibling // null
    

    hasChildNodes 和 ownerDocument

    每个节点都一个 hasChildNodes() 方法,用来检验该节点是否有子节点(NodeLists 的长度是否大于 1).

    const body = document.body
    body.hasChildNodes() // true
    

    同时,每个节点都拥有一个 ownerDocument 属性,该属性执行表示该文档的节点(document),这个属性的意义表示任何节点都属于其所在的文档。

    const body = document.body
    body.ownerDocument === document // true
    

    操作节点

    1.appendChild
    该方法用来想父元素的 NodeLists 集合中追加元素,接受一个 Node 对象作为参数。调用该方法返回被追加的节点。

    const firstDiv = docuemnt.createElement("div")
    const body = document.body
    body.appendChild( firstDiv )
    body.childNodes // [div]
    

    另外,由于相同的节点不能出现在 DOM 树的多个位置中,因此这里如果重复追加 firstDiv,NodeLists 中的子节点数目不变:

    body.appendChild( firstDiv )
    body.appendChild( firstDiv )
    body.appendChild( firstDiv )
    body.childNodes // [div]
    

    同时,如果将已存在的子节点追加到 NodeLists 中,其将会被挪动到 NodeLists 集合的最后一项,其之后的节点将会顶替该节点先前的位置,这个过程同样遵循“同一个节点不能出现 DOM 树的多个位置中”这一原则。

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

    上面引用自 《JavaScript 高级程序设计(第三版)》。
    2.insertBefore
    appendChild,该方法同样也由父节点调用,该方法接受两个参数:

    • 待插入的节点
    • 参照节点

    调用该方法时,将会把带插入的节点插入到参照节点之前,如果该方法的最后一个参数为 null,那个调用这个方法的表现和 appendChild 相同,都是将节点追加到 NodeLists 集合的末尾。调用该方法返回被插入的节点。

    const div = document.createElement("div")
    const p = document.createElement("p")
    const body = document.body
    body.appendChild(div)
    body.insertBefore(p,div)
    body.childNodes // [p, div]
    

    同样,该方法遵循“同一个节点不能出现 DOM 树的多个位置中”原则:

    body.insertBefore(p,null)
    body.childNodes // [div, p]
    

    3.replaceChild
    该方法用来对节点进行替换,接受两个参数:

    • 新节点
    • 被替换的节点

    调用这个方法时,被替换的节点将被移除,并由新的节点代替:

    const div = document.createElement("div")
    const p = document.createElement("p")
    const a = document.createElement("a")
    const body = document.body
    body.appendChild(div)
    body.appendChild(p)
    body.childNodes // [div, p]
    // 替换节点
    body.replaceChild(a,body.firstChild)
    body.childNodes // [a, p]
    

    4.removeChild
    如果只想从文档树中移除某个节点,需要调用 removeChild 方法,该方法接受需要被移除的节点作为参数,返回被移除的节点:

    const div = document.createElement("div")
    const p = document.createElement("p")
    const body = document.body
    body.appendChild(div)
    body.appendChild(p)
    body.childNodes // [div, p]
    // 移除节点
    body.removeChild(p) // p
    body.childNodes // [div]
    

    5.cloneNode
    该方法用来对节点进行复制,接受一个标志参数 true 或者 false,表示是否执行深复制,当执行深复制时,会复制节点本身以及其所有的子节点。当进行浅复制时,仅复制节点本身。

    const div = document.createElement("div")
    const p = document.createElement("p")
    const body = document.body
    body.appendChild(div)
    body.appendChild(p)
    // 进行浅复制
    body_shallowcpy = body.cloneNode()
    // 进行深复制
    body_deepcpy = body.cloneNode(true)
    body_shallowcpy .childNodes // []
    body_deepcpy .childNodes // [div, p]
    

    总结

    本文主要介绍了 Node 类型以及操作 DOM 的几个方法。总结一下:

    • Node 是一个接口,规定了 DOM 节点的一些实现
    • 所有的节点都会实现 Node 接口,每个节点都有一个 nodeType 属性,和 Node 对象中的一些常量一一对应,表示该节点的类型
    • 由于 IE 的 DOM 不是使用原生 JavaScript 实现的,因此无法获取到 Node 对象上的这些常量,因此最好使用数字值进行节点类型的判断,以提高兼容性
    • nodeNamenodeValue 属性
    • childNodes 是一个包含了元素所有子节点的一个集合,该集合是动态的,DOM 树中的任何变化都会反映到这个集合中
    • firstChildlastChild
    • previousSiblingnextSibling
    • hasChildNodes() 可以用来检验元素是否有子节点
    • 每个节点元素都拥有一个 ownerDocument 属性,指向文档节点,该属性的意义表示任何节点都属于其所在的文档
    • 操作节点的几个方法
    • cloneNode() 方法用来复制节点,有深复制和浅复制之分

    使用 JavaScript 操作 DOM 可以让网页出现多种多样的变化,JavaScript 中与 DOM 相关的方法很多很多,也是使大多数前端er 们感到陌生或者似懂非懂的地方(正经脸:)),因为我们平时写代码时大多用的是一些 DOM 操作的封装,比如 jQuery 库,甚至如果你使用 Vue 或者 React 之类的框架,那操作 DOM 的机会更少了。不过学习这块的知识可以扎实我们的基础,虽然不一定有太多的使用机会,但你可以推测这些框架或者库是怎么实现这些 DOM 操作的,对技术和思维上的提升会有不少的帮助。
    还是那句话:如果哪天遗忘了,请随时回过头来看看~

    完。

    相关文章

      网友评论

        本文标题:009 Node 类型

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