DOM

作者: _ClariS_ | 来源:发表于2019-08-05 21:09 被阅读5次

    DOM 全称为 Document Object Model(文档对象模型)
    其中,Document 特指 XML 或 HTML
       Object 是指把文档变成对象
       Model 文档和对象之间的映射关系叫做模型

    DOM经常用来进行以下操作

    • 获取元素
    • 动态创建元素
    • 对元素进行操作(设置其属性或调用其方法)
    • 事件(什么时机做相应的操作)

    DOM树(tree)

    DOM又称为文档树模型

    文档树

    节点(Node)

    JS中的对象继承自 Object
    页面(document)中的对象继承自 Node(Node 是一个函数)


    DOM 的最小组成单位就是 Node
    节点(Node):网页中的所有内容都是节点,包含以下内容:

    • 文档(Document):一个网页可以称为文档
    • 元素(Element):网页中的标签
    • 文本(Text):空格、回车、文字等内容

    以及其他不重要的,例如属性(href)、注释(comment)等

    document.body
    typeof document.body // "object"
    console.dir(document.body)
    // 根据打印出的结果发现,document.body 的原型链为
    // HTMLBodyElement <-- HTMLElement <-- Element  <-- Node  <-- EventTarget <-- Object
    
    typeof document // "object"
    console.dir(document)
    // 根据打印出的结果发现,document.body 的原型链为
    // HTMLDocument <-- Document <-- Node  <-- EventTarget <-- Object
    

    Document、Element、Text 的原型都指向 Node,最终 Node 的原型指向 Object

    问:DOM 是如何操作节点的?

    答:页面中的节点实际上是通过 Element、Text、Document、Comment 等构造函数构造出对应的对象,我们想操作哪个对象就调用其对应的API就可以了

    Node 的接口

    1. 属性

    childNodes、firstChild、innerText、lastChild、nextSibling、nodeName、nodeType、nodeValue、outerText、ownerDocument、parentElement、parentNode、previousSibling、textContent

    将以下单词组合就可以得到上面的属性

    • child / children / parent
    • node
    • first / last
    • next / previous
    • sibling / siblings
    • type
    • value / text / content
    • inner / outer
    • element

    关于 Node 属性,你需要注意以下几点:

    1. childNodes 和 children 的区别:
    • childNodes返回的是节点的子节点集合,包括元素节点、文本节点(回车也属于文本节点)还有属性节点等
    • children返回的只是节点的元素(标签)节点集合
    1. nodeName
    1. nodeType
      节点类型常量
    1. ownerDocument 与 iframe 结合起来考虑
    2. innerText 与 textContent 的细微区别
    • textContent 会获取所有元素的内容,包括 <script> 和 <style> 元素,然而 innerText 不会。
    • 由于 innerText 受 CSS 样式的影响,它会触发重排(性能很低),但textContent 不会。
      其他区别详见 textContent MDN
    1. nextSibling、previousSibling 可能会获取到文本
      另外,如果你不知道该使用 innerText 还是 textContent,可采取以下写法:
      'textContent' in document.body ? document.body.textContent : document.body.innerText
    2. childNodes 与 querySelectorAll
    var parent = document.getElementById('parent');
    parent.childNodes.length // 2
    parent.appendChild(document.createElement('div'));
    parent.childNodes.length // 请问现在 length 是多少?
    答案是 3
    
    var allDiv = document.querySelectorAll('div')
    allDiv.length // 假设是 2
    document.body.appendChild(  document.createElement('div')  )
    allDiv.length // 请问现在 length 的值是多少?
    答案是 2
    

    问:为什么childNodes 的 length 会动态变化,而querySelectorAll 的 length 却不会动态变化?

    答:

    • parent.childNodes 是动态集合。所谓动态集合就是一个活的集合,DOM树删除或新增一个相关节点,都会立刻反映在NodeList接口之中。
    • document.querySelectorAll方法返回的是一个静态集合。DOM内部的变化,并不会实时反映在该方法的返回结果之中。
    2. 方法(如果一个属性是函数,那么这个属性就也叫做方法;换言之,方法是函数属性)
    • appendChild()
    • cloneNode()
    • contains()
    • hasChildNodes()
    • insertBefore()
    • isEqualNode()
    • isSameNode()
    • removeChild()
    • replaceChild()
    • normalize()

    关于 Node 方法,你需要注意以下几点:

    1. cloneNode(true) 表示深克隆,cloneNode(false) 表示浅克隆
      如果为 true,则该节点的所有后代节点也都会被克隆;如果为 false,则只克隆该节点本身。
    2. isEqualNode() 与 isSameNode() 的区别
    • isEqualNode() 只是看起来相等
    • isSameNode() 是真的相等(同一个),对于节点来说,isSameNode() 等价于 ===
    1. removeChild() 只是将子节点从页面中移除,使你看不见,但其实它依然存在于内存中。同样地,用 replaceChild() 将一个节点将另一个节点替换后,被替换的节点依然存在于内存中
    2. normalize() // 常规化
    var wrapper = document.createElement("div");
    
    wrapper.appendChild(document.createTextNode("Part 1 "));
    wrapper.appendChild(document.createTextNode("Part 2 "));
    
    // 这时(规范化之前),wrapper.childNodes.length === 2
    // wrapper.childNodes[0].textContent === "Part 1 "
    // wrapper.childNodes[1].textContent === "Part 2 "
    
    wrapper.normalize();
    // 现在(规范化之后), wrapper.childNodes.length === 1
    // wrapper.childNodes[0].textContent === "Part 1 Part 2"
    

    在标签里添加文本的方法:

      <div id="div1">
        <span>123</span>
      </div>
    

    div1.innerText = 'hello' // hello
      此方法会直接覆盖 div 中原有的内容
    div1.appendChild(document.createTextNode('hello')) // 123 hello
      此方法不会覆盖 div 中原有的内容,只是在 div 中追加内容

    Document 的接口

    1. 属性
      anchors、body、characterSet、childElementCount、children、doctype、documentElement、domain、fullscreen、head、hidden、images、links、location、onxxxxxxxxx(事件监听)、origin、plugins、readyState、referrer、scripts、scrollingElement、styleSheets、title、visibilityState
    2. 方法
    • close()
    • createDocumentFragment()
    • createElement()
    • createTextNode()
    • execCommand() (当你想写一个富文本编辑器时使用)
    • exitFullscreen()
    • getElementById()
    • getElementsByClassName()
    • getElementsByName()
    • getElementsByTagName()
    • getSelection()
    • hasFocus()
    • open()
    • querySelector()
    • querySelectorAll()
    • registerElement()
    • write()
    • writeln()

    关于 Document 方法,你需要注意以下几点:

    1. querySelector() 与 querySelectorAll() 的区别
      querySelector() 返回一个元素
      querySelectorAll() 返回多个元素组成的伪数组(即使只有一个元素,依然会返回一个伪数组)

    通过 DOM 的 API 获取到的 elements 都是伪数组

    1. close() 与 open()
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>Document</title>
    </head>
    <body>
      <script>
        document.write(1)
        document.write(2)
        setTimeout(() => {
          document.write(3)
        }, 1000) // 会输出 1 2 3 吗?
      </script>
    </body>
    </html>
    

    答案是不会,输出结果是先打印出 1 2,然后打印出 3 并覆盖掉 1 2。

    问:为什么不是像我们所期望的一样,先打印出 1 2,然后再打印出 1 2 3 呢?

    答:Document 从页面第一个标签开始就会自动进入 open 状态,然后进入 write 状态,当<script></script>标签里的内容执行完毕后就进入 close 状态(close状态无法写入)。上述代码中设置了定时器,使document.write(3) 在 1 秒后才执行,1 秒后早已进入 close 状态,此时要写入 3 就必须重新进入 open 状态(相当于刷新了 Document),因此会覆盖掉 1 2

    写 document.write() 时要注意不要出现在有延时性或者异步的操作中。

    1. innerText() 与 innerHTML() 的区别
      http://js.jirengu.com/povuqolipi/1/edit?html,js,output

    Element 的接口

    Element MDN

    总结

    1. DOM API 无外乎「增删改查」,大概知道如何对 div 进行增删改查即可
    2. 不需要死记硬背,用到的时候只需要查 MDN 即可

    相关文章

      网友评论

        本文标题:DOM

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