类数组:NodeList和 HTMLCollection的区别

172 阅读2分钟

概念和区别

NodeList 对象是节点的集合,通常是由属性,如Node.childNodes 和 方法,如document.querySelectorAll 返回的。 备注:  NodeList 不是一个数组,是一个类似数组的对象 (Like Array Object)。虽然 NodeList 不是一个数组,但是可以使用 forEach() 来迭代。

HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(与 arguments 相似的类数组 (array-like) 对象),还提供了用来从该集合中选择元素的方法和属性。备注: HTML DOM 中的 HTMLCollection 是即时更新的(live);当其所包含的文档结构发生改变时,它会自动更新。因此,最好是创建副本(例如,使用 Array.from)后再迭代这个数组以添加、移动或删除 DOM 节点。

两者的属性和方法,如下图:

image.png

image.png

具体用法

个人理解:

NodeList:只有querySelectorAll返回的是NodeList,这不是数组而是类数组,适用for循环和forEach方法,而适用forEach就一定会适用for in和 for of。

HTMLCollection:除了querySelectorAll返回的是NodeList,其余都是HTMLCollection,与NodeList接口不同的是:HTMLCollection没有forEach方法,只能使用for循环遍历。

例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<div id="box">
    <div class="a">aaa</div>
    <div class="a">bbb</div>
</div>
<script>
    // 1. HTMLCollection形式
    let element1 = document.getElementsByClassName(
        'a'
    );
    console.log(element1); // HTMLCollection

    // 报错
    // element1.forEach(function (ele) {
    //     console.log(ele);
    // });

    // 优化1
    [].forEach.call(element1, function (el) {
        console.log(el, '优化1');
    });

    // 优化2
    Array.from(element1).forEach(function (el) {
        console.log(el, '优化2');
    })

    // 优化3
    for (let index = 0; index < element1.length; index++) {
        // const element = array[index];
        console.log(element1[index], '优化3');
    }

    // 2. NodeList形式
    let element2 = document.querySelectorAll('.a')
    console.log(element2); // NodeList

    // 方法1,不报错
    element2.forEach(function (ele) {
        console.log(ele);
    });
    // 方法2
    for (const ele of element2) {
        console.log(ele);
    }
    // 方法3
    for (let index = 0; index < element2.length; index++) {
        // const element = array[index];
        console.log(element2[index], '方法3');
    }

    // ...
</script>

<style>
    #a {
        width: 10vw;
        height: 10vh;
        background-color: antiquewhite;
    }
</style>

</html>
// 报错
element1.forEach(element => {
    console.log(element);
});

// 不报错

[].forEach.call(element1, function (el) {
    console.log(el, '优化1');
});

个人理解:element1是一种无法用forEach的类数组(并不是类数组就无法forEach,比如:Nodelist)。不报错的写法中,调用空数组的 forEach 方法,第1个参数:element1是把[]的this指向指给element1,第2个参数:是把函数当成参数传入forEach的回调函数中

产生的联想

  • nextElementSibling 和 nextSibling的区别

  • Children 和 Childnodes的区别

  • parentNode 和 parentElement的区别

  • ....

元素节点(HTMLCollection):nextElementSibling 和 Children适用,(包括文本节点、注释节点即回车、换行、空格、文本等等)

节点(Nodelist):nextSibling 和 Childnodes适用,(不包括文本节点、注释节点等等)

image.png

Tips:由上图可以看出,HTMLCollection是Nodelist集合的一种。

例子: js节点问题:交换两个节点

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<div class="box">
    <div id="a">aaa</div>
    <div id="b">bbb</div>
</div>
<script>
    var a = document.getElementById("a");
    var b = document.getElementById("b");
    const swap = function (nodeA, nodeB) {
        const parentA = nodeA.parentNode;
        console.log(nodeB.nextElementSibling); // 理解了nextSibling,就可以用nextElementSibling来简化了
        if (nodeB.nextElementSibling === nodeA) {
            parentA.insertBefore(nodeA, nodeB);
        } else {
            parentA.insertBefore(nodeB, nodeA);
        }
    };
    swap(a, b)
</script>


</html>