DOM中历史遗留的那些天坑 ...

即时到了DOM3.0时代, 为了同时满足浏览器的向下兼容和ES6的最新街口, DOM还是保留了很多古老的,极易和新类型引起混淆的类比如HTMLCollection vs. NodeList, 这篇文章来回顾下这些类的区别, 看完就忘了吧, 下一代dom应该会统一类型的...吧?


>>> 引文

这篇博客起源于我对一道作业题的思考,在DOM课程中,第二道作业题是:

elem.childrenelem.childNodes的区别?

那么这两者的区别究竟是什么呢?当时我在回答的时候写了这样一段代码(这段代码只得到表面上的答案,是浅层次理解)。

<div id="ct">
    <p class="para">Lyndon</p>
    <p class="attr">123<span>dozz</span></p>
</div>
<script>
    var ct = document.getElementById("ct");
    console.log(ct.children);
    console.log(ct.childNodes);
</script>

返回的结果是:

可以看出,当我用getElementById方法匹配到id = "ct"的元素节点后

  • ct.children返回的是一个HTMLCollection(图中已用红框标出),其中包含的两个元素是p.para以及p.attr
  • ct.childNodes返回的是一个NodeList(图中已用红框标出),其中包含的元素稍微多些,有5项:text, p.para, text, p.attr, text

每一个元素不断展开,会发现有很多的属性,零零碎碎的,这时候我发现一个比较明显的区别是textContent的不同:

  • HTMLCollection
    • p.paratextContent"Lyndon"
    • p.attrtextContent"123dozz"
  • NodeList
    • texttextContent"↵ "
    • p.paratextContent"Lyndon"
    • texttextContent"↵ "
    • p.attrtextContent"123dozz"
    • texttextContent"↵ "

究竟为何两个方法会返回不一样的结果?老师的课件归纳如下:

两者的不同点在于:

  1. HTMLCollection对象具有namedItem()方法,可以传递id或name获得元素;
  2. HTMLCollection的item()方法和通过属性获取元素(document.forms.f1)可以支持id和name,而NodeList对象只支持id

但是我并没有完全看懂,大概掌握程度是0.6左右,于是我觉得解决这些疑惑的终极方法,应该是去深入了解:HTMLCollectionNodeList本质上的不同


>>> 首先,参考stack overflow上的回答

我翻译了一下Vote数最高的答案:

HTMLCollectionNodeList都是DOM节点的集合,两者都属于Collections范畴,两者的区别在于:

  • 方法略有差异HTMLCollectionNodeList多了一个namedItem方法,其他方法保持一致
  • 包含节点类型不同NodeList可以包含任何节点类型,HTMLCollection只包含元素节点(ElementNode)

Collections的出现场景?

  • 当返回多个节点(如:getElementByTagName)或者得到所有子元素(如:element.childNodes)时,Collections就会出现,这时候就有可能返回HTMLCollection或者NodeList

>>> 其次参考W3的文档(MDN上也有详细解释):

HTMLCollection是以节点为元素的列表,可以凭借索引、节点名称、节点属性来对独立的节点进行访问。HTML DOM中的Collections是实时变动的,当原始文件变化,Collections也会随之发生变化。

  • 属性:length(返回的是列表的长度)
  • 方法1:item(通过序号索引来获取节点,参数是索引值,超过索引值返回null)
<div id="ct">
    <p class="para">Lyndon</p>
    <p class="attr">123<span>dozz</span></p>
    <form action="" method="get" name="apply">
        <input type="text" name="username" placeholder="用户名">
        <input type="password" name="password" placeholder="密码">
    </form>
</div>
<script>
    var ct = document.getElementById("ct");
    var a = ct.children;
    var b = ct.childNodes;
    console.log(a);
    console.log(b);
</script>
  • 方法2:namedItem(用名字来返回一个节点,首先搜寻是否有匹配的id属性,如果没有就寻找是否有匹配的name属性,如果都没有,返回null)
<div id="ct">
    <p class="para">Lyndon</p>
    <p class="attr">123<span>dozz</span></p>
    <form action="" method="get" name="apply">
        <input type="text" name="username" placeholder="用户名">
        <input type="password" name="password" placeholder="密码">
    </form>
</div>
<script>
    var ct = document.getElementById("ct");
    var a = ct.children;
    var b = ct.childNodes;
    console.log(a);
    console.log(b);
</script>

NodeList返回节点的有序集合,DOM中的NodeList也是实时变动的

  • 属性:length(列表中节点的数量)
  • 方法:item(返回集合中的元素,如果超过范围返回null)
<div id="ct">
    <p class="para">Lyndon</p>
    <p class="attr">123<span>dozz</span></p>
    <form action="" method="get" name="apply">
        <input type="text" name="username" placeholder="用户名">
        <input type="password" name="password" placeholder="密码">
    </form>
</div>
<script>
    var ct = document.getElementById("ct");
    var a = ct.children;
    var b = ct.childNodes;
    console.log(a);
    console.log(b);
</script>

>>> Element与Node

到这一步,两者本质上的区别已经差不多分清楚了,那么现在就要进入第二个问题,为什么两个Element属性返回的结果(如:textContent)不一样呢?这里需要感谢同班同学 joyside,他推荐给我一篇文章《Element和Node的区别你造吗?》来理解Element和Node的区别。 文章中参考的是MDN:Node是一个基础类型,document, element, text, comment, DocumentFragment等都继承于Node. 在这篇文章最开始的测试中NodeList结果中有非常多的text,其实element, text, comment都是Node的子类,可以将它们视为:elementNode, textNode以及commentNode.平时在DOM中最常用的Element对象,其本质就是elementNode. 由于Node就是DOM的结构,代码内容经过解析后,Node与Node之间可以插入文本,文章最开头的截图中的"↵ "本质上就是Node之间的空隙,这种空隙的本质是textNode.


>>> 总结

综上所述,进行归纳,并回答文章开头提出的疑问。

  • HTMLCollectionNodeList的共同点显而易见:
    1. 都是类数组对象,都有length属性
    2. 都有共同的方法:item,可以通过item(index)或者item(id)来访问返回结果中的元素
    3. 都是实时变动的(live),document上的更改会反映到相关对象上(例外:document.querySelectorAll返回的NodeList不是实时的)
  • HTMLCollectionNodeList的区别是:
    1. NodeList可以包含任何节点类型,HTMLCollection只包含元素节点(elementNode),elementNode就是HTML中的标签
    2. HTMLCollectionNodeList多一项方法:namedItem,可以通过传递id或name属性来获取节点信息
  • 文章开头的疑问解答: 文章开头的代码实际上等价于:
这里是介于node与node之间的textNode
<div id="ct">
    这里是介于node与node之间的textNode
    <p class="para">Lyndon</p>
    这里是介于node与node之间的textNode
    <p class="attr">123<span>dozz</span></p>
这里是介于node与node之间的textNode
</div>
<script>
    var ct = document.getElementById("ct");
    console.log(ct.children);
    console.log(ct.childNodes);
</script>

由于NodeList包含任何节点类型,ct.childNodes会一并返回textNode, elementNode等,所以最终结果就是由text, p, text, p, text组成的类数组对象,这里的text只是换行符而已。 由于HTMLCollection仅包含elementNode,因此最终的结果就是由p.para, p.attr组成的类数组对象。当然,由于这里只返回直接的子元素,因此不会出现类数组对象中没有span,如果希望返回结果中有span,这样写就可以了:

<div id="ct">
    <p class="para">Lyndon</p>
    <p class="attr">
        <span>dozz</span>
    </p>
    <span>bilibili</span>
</div>
<script>
    var ct = document.getElementById("ct");
    console.log(ct.children);
    console.log(ct.childNodes);

老师在课件中还详细给出了有哪些具体的方法可以获取HTMLCollectionNodeList对象,如果要记住可能比较麻烦,每次在具体情况时参考控制台的输出,得知类型后只需要记住常用的方法区别就可以轻松地进行操作了。


>>> 参考资料

  1. Difference between HTMLCollection, NodeLists, and arrays of objects
  2. Interface NodeList
  3. NodeList and HTMLCollection
  4. Interface HTMLCollection
  5. Element和Node的区别你造吗?

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云霄雨霁

追踪收集解决方法

1740
来自专栏禅林阆苑

mysql学习总结02 — 数据类型

无符号:表示存储的数据在当前字段中,没有负数(只有正数,例如 tinyint 区间为 0~255)

1412
来自专栏desperate633

设计模式之原型模式(Prototype 模式)引入原型模式原型模式的实例小结为什么需要使用原型模式

联想到浏览器中,如果我们生成了一个button实例,这个button实例经过一系列操作,携带了各种信息,比如button加颜色,加背景图,加文字,加事件等等。如...

692
来自专栏Python爬虫与数据挖掘

Python正则表达式初识(一)

首先跟大家简单唠叨两句为什么要学习正则表达式,为什么在网络爬虫的时候离不开正则表达式。正则表达式在处理字符串的时候扮演着非常重要的角色,在网络爬虫的时候...

832
来自专栏青玉伏案

iOS可视化动态绘制八种排序过程(Swift版)

前面几篇博客都是关于排序的,在之前陆陆续续发布的博客中,我们先后介绍了冒泡排序、选择排序、插入排序、希尔排序、堆排序、归并排序以及快速排序。俗话说的好,做事儿要...

2259
来自专栏Golang语言社区

初学者需要注意的问题-变量的作用域

go语言支持多变量同时赋值或者初始化,这是一个很方便的特性。它也允许使用:=操作符同时声明部分新变量,并且给已有变量赋值,这会带来一些需要注意的问题,一不小心就...

4127
来自专栏编程坑太多

理解 JavaScript 的 async/await

1993
来自专栏前端桃园

看完这几道 Promise 面试题,还被面试官问倒算我输

最近在复习 Promise 的知识,所以就做了一些题,这里挑出几道题,大家一起看看吧。

1091
来自专栏python3

python3--变量,布尔值,用户交互 input,if语句,while循环

已经运行了第一个python程序,即:终端---->cmd----->python 文件路劲。回车搞定~

2162
来自专栏贾鹏辉的技术专栏@CrazyCodeBoy

ES6、ES7、ES8学习指南

ES全称ECMAScript,ECMAScript是ECMA制定的标准化脚本语言。目前JavaScript使用的ECMAScript版本为ECMAScript-...

1834

扫码关注云+社区

领取腾讯云代金券