如何选择动态生成的JavaScript DOM元素?innerHTML生成元素获取失败
动态添加DOM元素后无法获取?这几个办法帮你搞定
我懂你碰到的糟心事了——用innerHTML +=循环生成了一堆带.class类名的div,结果用document.querySelectorAll(".class")去拿的时候,返回的是空NodeList,完全找不到这些元素对吧?
先唠唠为啥会出现这种情况:
- 你用
innerHTML +=的时候,其实是把classesList原来的内容先销毁再重建了一遍。每次循环里的innerHTML +=都会重新解析整个HTML字符串,不仅效率拉胯,更关键的是:如果你的querySelectorAll代码是在map循环执行完之前就跑了,那这些动态元素还没真正被渲染到DOM里,自然拿不到。 - 就算循环执行完了,有时候浏览器的渲染队列还没来得及把这些元素更新到DOM树里,也会导致暂时拿不到。
接下来给你几个靠谱的解决方案:
方案1:改用createElement逐个创建元素(推荐)
放弃用innerHTML +=拼接字符串的方式,改用原生DOM API来创建元素,这样元素会直接被添加到DOM树里,而且能实时获取到:
data.forEach(_r => { // 创建外层div const classDiv = document.createElement('div'); classDiv.className = 'class'; classDiv.setAttribute('name', _r.name); // 创建top部分 const topDiv = document.createElement('div'); topDiv.className = 'top'; const h1 = document.createElement('h1'); h1.textContent = _r.name; const icon = document.createElement('i'); icon.className = 'fas fa-arrow-down'; topDiv.appendChild(h1); topDiv.appendChild(icon); // 创建bottom部分 const bottomDiv = document.createElement('div'); bottomDiv.className = 'bottom'; const p = document.createElement('p'); p.textContent = _r.count; bottomDiv.appendChild(p); // 组装并添加到容器 classDiv.appendChild(topDiv); classDiv.appendChild(bottomDiv); classesList.appendChild(classDiv); // 这里就能直接操作刚创建的classDiv了 // 比如 classDiv.addEventListener('click', () => { ... }) }); // 循环结束后再查询,肯定能拿到 const elements = document.querySelectorAll(".class"); console.log(elements); // 不会是空的了
方案2:确保在DOM更新完成后再查询元素
如果你还是想用innerHTML的方式,那一定要保证查询代码是在所有拼接完成且DOM更新后执行。不过要注意,innerHTML +=多次拼接效率很低,建议先把所有HTML字符串拼接好,一次性赋值:
// 先把所有HTML拼好 let htmlStr = ''; data.forEach(_r => { htmlStr += ` <div class="class" name=${_r.name} > <div class="top"> <h1>${_r.name}</h1> <i class="fas fa-arrow-down"></i> </div> <div class="bottom"> <p>${_r.count}</p> </div> </div> `; }); // 一次性赋值给innerHTML,减少DOM重绘 classesList.innerHTML = htmlStr; // 现在再查询,元素已经存在于DOM中了 const elements = document.querySelectorAll(".class"); console.log(elements);
方案3:使用MutationObserver监听DOM变化
如果你的场景是元素可能在任意时间被添加,那可以用MutationObserver来监听容器的变化,一旦有新元素添加进来就立刻处理:
// 创建观察者实例 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { // 检查是否有新节点添加 if (mutation.addedNodes.length > 0) { const newClassElements = mutation.target.querySelectorAll(".class"); newClassElements.forEach(el => { // 在这里操作新添加的元素 console.log('新元素:', el); }); } }); }); // 监听classesList的子节点变化 observer.observe(classesList, { childList: true, subtree: true }); // 然后执行你的innerHTML拼接代码 data.map((_r) => { classesList.innerHTML += ` <div class="class" name=${_r.name} > <div class="top"> <h1>${_r.name}</h1> <i class="fas fa-arrow-down"></i> </div> <div class="bottom"> <p>${_r.count}</p> </div> </div> `; });
最后提个醒:innerHTML虽然写起来快,但存在XSS风险(如果_r.name或_r.count是用户输入的内容的话),所以更推荐用createElement的方式,既安全又可靠。
内容的提问来源于stack exchange,提问作者Ssenoga Edward
相关产品推荐
相关产品推荐

