Array.from转nodeList时报Cannot read 'style' of undefined错误
错误定位
- 你怀疑自己拿到的对象不是NodeList的判断是对的:用来转换的
bottles变量是document.getElementById("bottles")获取到的单个<ul>DOM节点,本身就不是可迭代的元素集合(NodeList/HTMLCollection)。 - 触发报错的直接原因:调用
Array.from(bottles)时传入的是这个单个ul节点,不是存放li的集合。Array.from()作用在单个DOM节点上时,只会遍历这个节点本身的可枚举属性,根本不会返回它内部嵌套的li元素列表,转换后的数组和你创建的li完全对不上,用bottlesArray[j]取到的自然是undefined,读取undefined的style属性就会触发报错。 - 你用扩展运算符
[...bottles]转换失败的原因和上面完全一致:单个DOM节点不是可迭代的元素集合,自然无法拆出你想要的li列表。 - 额外的逻辑冗余问题:你在每追加一个li的循环里,都重复执行一次全量数组转换,哪怕转的是正确的集合,这种写法也完全没必要——你手头本来就保存了刚创建好的
liElement引用,根本不需要之后再从DOM里捞元素。
修复方案
方案1(无冗余操作,推荐)
完全不需要执行NodeList转数组的操作:在创建li、拿到它的DOM引用时,直接根据索引设置样式即可,省掉不必要的DOM查询和重复转换开销:
const bottles = document.getElementById("bottles"); count = 99; var myArray = []; var j = 0; function bottlesOnTheWall() { while (count > 0) { if(count > 2) { myArray.push(`${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1} bottles of beer on the wall`) } else if (count === 2) { myArray.push(`${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1}bottle of beer on the wall`) } else if (count === 1) { myArray.push(`${count} bottle of beer on the wall, ${count} bottles of beers. No more bottle of beer on the wall`) } else { myArray.push(`No more bottles of beer on the wall. No more bottles of beer. Go to the shop and buy some ${count} more.`) } count-- } while (j < myArray.length) { var liElement = document.createElement("li"); var text = document.createTextNode(myArray[j]); liElement.appendChild(text); // 直接用持有的li引用设置样式,无需再转DOM集合查找 if(j % 2) { liElement.style.width = "300px"; } else { liElement.style.width = "500px"; } bottles.appendChild(liElement); j++; } } bottlesOnTheWall();
方案2(需要批量处理所有li时的写法)
如果确实需要把所有li转成数组统一处理,要等所有li渲染完成后,从ul容器上获取它的子元素集合(这时候拿到的才是真正的NodeList),再执行数组转换,绝对不要把ul容器本身当集合转换:
// 原循环仅负责创建、追加li,不提前做样式设置 while (j < myArray.length) { var liElement = document.createElement("li"); var text = document.createTextNode(myArray[j]); liElement.appendChild(text); bottles.appendChild(liElement); j++; } // 这里获取的才是真正的li集合(NodeList类型) const allLis = bottles.querySelectorAll('li'); // 两种数组转换方式都可以正常工作 const bottlesArray = Array.from(allLis); // 批量设置样式 bottlesArray.forEach((li, index) => { li.style.width = index % 2 ? "300px" : "500px"; })
这种场景下用扩展运算符转换也是可行的,因为
querySelectorAll()返回的是可迭代的NodeList:const bottlesArray = [...bottles.querySelectorAll('li')]
内容的提问来源于stack exchange,提问作者alwaysLearning
相关产品推荐
相关产品推荐

