Chrome 96环境下Element.getElementsByClassName返回节点数不一致问题咨询
环境信息
- Windows 10,Chrome 96.0.4664.45(正式版 64位)
- 该问题在Firefox 94.0.1(64位)版本中不会复现
问题描述
我在使用Element.getElementsByClassName时遇到了不符合预期的行为,已附上完整可复现问题的HTML文件内容,控制台输出如下:
this.node.getElementsByClassName("divIndicator").length = 0 this.node.getElementsByClassName("indicators")[0].getElementsByClassName("divIndicator").length = 3 this.node.tBodies[0].getElementsByClassName("divIndicator").length = 3 this.node.parentNode.getElementsByClassName("divIndicator").length = 3
我创建了一个JS对象维护组件的根DOM节点引用,从该根DOM节点调用getElementsByClassName时,浏览器无法获取类名为divIndicator的节点。
但从该根节点的子节点(或父节点)调用getElementsByClassName时,方法可以正确返回3个类名为divIndicator的DIV元素。
请问我的代码哪里存在问题?
<!DOCTYPE html> <html> <head> <script type="text/javascript"> window.onload = main function main() { var nav = new Navigation() document.body.appendChild(nav.node) nav.selectNavItem(0) } //Navigation更类似选项卡面板或选项卡容器 class Navigation { constructor() { this.node = this.initDom() this.node.model = this this.contentContainers = [] } initDom() { //创建DOM树 this.node = this.template() this.initNavItems() return this.node } initNavItems() { //关闭指示器 var indicators = this.node.getElementsByClassName("divIndicator") for (var indicator of indicators) { indicator.style.display = "none" } indicators[0].style.display = "initial" //为导航项绑定监听器 var navItems = this.node.getElementsByClassName("labels")[0].getElementsByTagName("td") for (var i=0; i < navItems.length; i++) { navItems[i].addEventListener("click", function(self, idx) { return function(evt) { self.selectNavItem(idx) } } (this, i)) } } selectNavItem(idx, evt) { //设置导航和内容容器的状态(遵循选项卡面板模式) var indicators = this.node.getElementsByClassName("divIndicator") console.log('this.node.getElementsByClassName("divIndicator").length = ' + indicators.length) indicators = this.node.getElementsByClassName("indicators")[0].getElementsByClassName("divIndicator") console.log('this.node.getElementsByClassName("indicators")[0].getElementsByClassName("divIndicator").length = ' + indicators.length) indicators = this.node.tBodies[0].getElementsByClassName("divIndicator") console.log('this.node.tBodies[0].getElementsByClassName("divIndicator").length = ' + indicators.length) indicators = this.node.parentNode.getElementsByClassName("divIndicator") console.log('this.node.parentNode.getElementsByClassName("divIndicator").length = ' + indicators.length) if (indicators.length == 0) { console.error("使用替代选择器获取divIndicator元素") indicators = this.node.tBodies[0].getElementsByClassName("divIndicator") } for (var i=0; i < indicators.length; i++) { if (i == idx) { //显示下箭头指示器 indicators[idx].style.display = "block" //显示对应内容容器 if (this.contentContainers[idx]) this.contentContainers[idx].node.style.display = "block" } else { //隐藏下箭头指示器 indicators[i].style.display = "none" //隐藏对应内容容器 if (this.contentContainers[i]) this.contentContainers[i].node.style.display = "none" } } } addContentContainer(cc) { this.contentContainers.push(cc) } template() { return new DOMParser().parseFromString(` <table id="tblNavigation" class="navigation font4" width="540" height="100"> <tr class="labels"> <td>餐饮</td> <td>食物</td> <td>食谱</td> </tr> <tr> <td colspan="3"> <div style="height: 2px; background-color: white;"> </td> </tr> <tr class="indicators"> <td><div class="divIndicator"><span>ᐯ</span></div></td> <td><div class="divIndicator"><span>ᐯ</span></div></td> <td><div class="divIndicator"><span>ᐯ</span></div></td> </tr> </table> `, "text/html").body.firstChild } } </script> </head> <body> </body> </html>
更新1
参考Randy Casburn的回答,进一步定位后发现,Chrome调试器显示该异常行为在调用document.body.appendChild()后立即出现,且仅当initNavItems()方法被调用且方法体内至少有一条语句时才会触发。对原initDom()方法做了小幅修改后问题似乎得到了解决,但暂不清楚背后的原因。
原initDom()方法:
initDom() { //创建DOM树 this.node = this.template() this.initNavItems() return this.node }
修改后:
initDom() { //创建DOM树 this.node = this.template() var self = this setTimeout(function() { self.initNavItems() }, 0) return this.node }
更新2
最终选择放弃使用DOMParser从字符串创建DOM元素的方案,目前至少有3种其他实现方案,最终选择使用<template>标签实现,修改后的template()函数如下:
template() { var template = document.createElement("template") template.innerHTML = ` <div class="divContentContainer" style="display: none;"> ...其他HTML内容... </div> ` return template.content.firstElementChild }
内容的提问来源于stack exchange,提问作者hotfix_cowboy
相关产品推荐
相关产品推荐

