• 网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。
  • HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。

1550970944363.png
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
1550970986988.png

1. 父级节点

  • 每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。
  • childNodes 中的所有节点都有同一个父元素,因此它们的 parentNode 属性都指向同一个节点。
  • 此外,childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点。

1550971196686.png

  1. <div class="demo">
  2. <div class="box">
  3. <span class="erweima">×</span>
  4. </div>
  5. </div>
  6. <script>
  7. // 1. 父节点 parentNode
  8. var erweima = document.querySelector('.erweima');
  9. // var box = document.querySelector('.box');
  10. // 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
  11. console.log(erweima.parentNode);
  12. </script>

2. 子节点

  • 每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例(伪数组)。 NodeList 是一个类数组对象,用于存储可以按位置存取的有序节点。
  • 注意,NodeList 并不是 Array 的实例,但可以使用中括号访问它的值,而且它也有 length 属性。
  • NodeList 对象独特的地方在于,它其实是一个对 DOM 结构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。
  • 我们通常说 NodeList 是实时的活动对象,而不是第一次访问时所获得内容的快照(即静态数组)。

1550971263925.png

1550971325828.png
如果元素的子节点类型全部是元素类型,那 children 和 childNodes 中包含的节点应该是一样的。可以像下面这样使用 children 属性:

  1. let childCount = element.children.length;
  2. let firstChild = element.children[0];

第一个子节点

1550971774758.png

最后一个子节点

1550971825493.png

第一个元素子节点

1550972014509.png

最后一个元素子节点

1550972106485.png
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性问题,

1550972648014.png

3. 兄弟节点

  • 每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。
  • childNodes 中的所有节点都有同一个父元素,因此它们的 parentNode 属性都指向同一个节点。
  • 此外,childNodes 列表中的每个节点都是同一列表中其他节点的兄弟节点。
  • 而使用 previousSibling 和 nextSibling 可以在这个列表的节点间导航。
  • 这个列表中第一个节点的 previousSibling 属性(q前一个兄弟节点)是 null,最后一个节点的nextSibling 属性(后一个兄弟节点)也是 null
    1. <div>我是div</div>
    2. <span>我是span</span>
    3. <script>
    4. var div = document.querySelector('div');
    5. // 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
    6. console.log(div.nextSibling);
    7. console.log(div.previousSibling);
    8. // 2. nextElementSibling 得到下一个兄弟元素节点
    9. console.log(div.nextElementSibling);
    10. console.log(div.previousElementSibling);
    11. </script>
    注意,如果 childNodes 中只有一个节点,则它的 previousSibling 和 nextSibling 属性都是null。

4. 创建节点

  1. let div = document.createElement("div");

1550975514321.png

5. 插入节点

1550975640170.png
appendChild(),用于在 childNodes 列表末尾添加节点。添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点。appendChild()方法返回新添加的节点

  1. let returnedNode = someNode.appendChild(newNode);
  2. alert(returnedNode == newNode); // true
  3. alert(someNode.lastChild == newNode); // true

如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。即使 DOM 树通过各种关系指针维系,一个节点也不会在文档中同时出现在两个或更多个地方。因此,如果调用 appendChild()传入父元素的第一个子节点,则这个节点会成为父元素的最后一个子节点,
如下所示:

  1. // 假设 someNode 有多个子节点
  2. let returnedNode = someNode.appendChild(someNode.firstChild); //将第一个节点插入到最后一个去
  3. alert(returnedNode == someNode.firstChild); // false 不在第一个了
  4. alert(returnedNode == someNode.lastChild); // true 确认在最后一个
  • 如果想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用insertBefore()方法
  • 这个方法接收两个参数:要插入的节点和参照节点。
  • 调用这个方法后,要插入的节点会变成参照节点的前一个兄弟节点,并被返回。
  • 如果参照节点是 null,则 insertBefore()与 appendChild()效果相同
    1. // 作为最后一个子节点插入
    2. returnedNode = someNode.insertBefore(newNode, null);
    3. alert(newNode == someNode.lastChild); // true
    4. // 作为新的第一个子节点插入
    5. returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
    6. alert(returnedNode == newNode); // true
    7. alert(newNode == someNode.firstChild); // true
    8. // 插入最后一个子节点前面
    9. returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
    10. alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true
    appendChild()insertBefore()在插入节点时不会删除任何已有节点。相对地,replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。

replaceChild()方法:

  1. // 替换第一个子节点
  2. let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
  3. // 替换最后一个子节点
  4. returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

使用 replaceChild()插入一个节点后,所有关系指针都会从被替换的节点复制过来。虽然被替换的节点从技术上说仍然被同一个文档所拥有,但文档中已经没有它的位置。


6. 删除节点

要移除节点而不是替换节点,可以使用removeChild()方法。这个方法接收一个参数,即要移除 的节点。被移除的节点会被返回,

  1. // 删除第一个子节点
  2. let formerFirstChild = someNode.removeChild(someNode.firstChild);
  3. // 删除最后一个子节点
  4. let formerLastChild = someNode.removeChild(someNode.lastChild);

7. 复制节点

  • 第一个是 cloneNode(),会返回与调用它的节点一模一样的节 点。
  • cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制, 即复制节点及其整个子 DOM 树。
  • 如果传入 false,则只会复制调用该方法的节点。
  • 当被复制节点改变时,复制节点跟着改变
  • 复制返回的节点属 于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。
  • 可以通过 appendChild()、 insertBefore()或 replaceChild()方法把孤儿节点添加到文档中。
    1. <ul>
    2. <li>item 1</li>
    3. <li>item 2</li>
    4. <li>item 3</li>
    5. </ul>
    6. //如果myList保存着对这个<ul>元素的引用,则下列代码展示了使用cloneNode()方法的两种方式:
    7. let deepList = myList.cloneNode(true); //深复制,复制ul里面的内容
    8. alert(deepList.childNodes.length); // 3(IE9 之前的版本)或 7(其他浏览器)
    9. let shallowList = myList.cloneNode(false);
    10. alert(shallowList.childNodes.length); // 0 //浅复制,只复制ul本省,里面的内容不复制
    注意 cloneNode()方法不会复制添加到 DOM 节点的 JavaScript 属性,比如事件处理程 序。这个方法只复制 HTML 属性,以及可选地复制子节点。除此之外则一概不会复制。 IE 在很长时间内会复制事件处理程序,这是一个 bug,所以推荐在复制前先删除事件处 理程序。


    节点操作

    1. // 已知儿子获取父元素
    2. son.parentNode //此处son指任意一个元素
    3. son.parentNode.parentNode //获取爷爷
    4. // 已知父亲获取儿子
    5. parent.children //获取所有子元素,此处parent指任意一个包含子元素的元素
    6. parent.childNodes //获取所有子节点,基本不用
    7. parent.children[0] //第1个子元素
    8. parent.firstElementChild//同上,兼容ie9及以上
    9. parent.children[parent.children.length-1] //最后一个子元素
    10. parent.lastElementChild //同上,兼容ie9及以上
    11. parent.firstChild //第一个子节点,基本不用
    12. parent.lastChild //最后一个子节点,基本不用
    13. // 获取已知元素的上一个及下一个兄弟元素
    14. element.nextElementSibling
    15. element.previousElementSibling
    16. //删除
    17. 父元素.removeChild(子元素) //将父元素中的子元素删除
    18. ele.remove()//自己删除自己,ie不兼容
    19. //克隆
    20. ele.cloneNode()//复制节点本身
    21. ele.cloneNode(true)//复制节点本身及其子节点