获取script标签HTMLCollection后长度不一致问题咨询
问题解析与解决方案
哈哈,这个问题我之前帮好几个开发者排查过,本质是HTMLCollection的特性加Chrome控制台的小坑导致的,我给你掰扯清楚~
核心原因:动态集合+控制台的惰性求值
你遇到的这个矛盾现象,其实是两个点叠加出来的:
- HTMLCollection是「动态实时」的集合:它和普通数组不一样,每次你访问它的
length或者尝试取元素时,都会重新去DOM里查一遍。也就是说,它永远反映的是当前DOM的最新状态。 - Chrome控制台的「惰性求值」:当你直接打印
scriptNodes的时候,控制台只会记录下那一刻的length快照给你看;但等你点击展开箭头想细看里面的元素时,控制台会重新去DOM里查一遍这个集合的最新状态——这中间说不定DOM已经变了,比如又加载了两个新的script标签,或者之前的某些script被删掉了。
这就解释了为什么打印时显示长度是10,展开后却看到length变成12:两个数字对应的是不同时间点的DOM状态。
为啥访问索引10、11会返回undefined?
当你写代码去访问scriptNodes[10]、scriptNodes[11]的时候,当前的动态集合里已经没有这些元素了:
- 要么是那两个“额外的script标签”已经被从DOM里移除了(比如有些动态加载的script执行完会自动删掉自己);
- 要么是这些标签根本不在主DOM树里(比如藏在shadow DOM里,
getElementsByTagName默认查不到); - 还有一种可能:控制台展开时看到的“元素”其实是集合的命名属性(比如某个script有
id或name,会被作为集合的属性挂上去),不是能通过数字索引访问的元素。
怎么解决这个问题?
如果你想要一个稳定、不会随DOM变化的script列表,把动态的HTMLCollection转成静态数组就行,有好几种方法:
// 方法1:用Array.from const scriptArray = Array.from(document.getElementsByTagName('script')); // 方法2:扩展运算符更简洁 const scriptArray = [...document.getElementsByTagName('script')]; // 方法3:老浏览器兼容写法 const scriptArray = Array.prototype.slice.call(document.getElementsByTagName('script'));
转成数组之后,它就保存了转换那一刻的所有script元素,不会再跟着DOM变了,你随便访问哪个索引都没问题,控制台显示也不会再出现前后不一致的情况。
另外,要是你想排查那些“看不见”的script,也可以用document.querySelectorAll('script')——它返回的是静态的NodeList,和HTMLCollection不一样,是一次性查询的结果,对比这两个集合的结果,就能看出DOM到底发生了啥变化。
内容的提问来源于stack exchange,提问作者David Casanellas
相关产品推荐
相关产品推荐

