使用insertAdjacentHTML插入元素后无法选中及绑定事件的问题
问题原因
你遇到的问题是异步执行顺序导致的:getData()是异步函数,发起API请求后会继续执行后续代码,而document.querySelectorAll(".btn")在元素还没通过insertAdjacentHTML插入DOM时就运行了,所以拿到的是空NodeList,无法绑定事件。
解决方案
有两种常用的解决方式:
方式1:事件委托(推荐)
利用事件冒泡机制,把点击事件监听器绑定到已存在的父元素(比如.container)上,这样不管按钮什么时候动态插入,点击事件都会被父元素捕获并处理。
修改后的JS代码:
'use strict'; const quesContainer = document.querySelector('.container'); const renderData = function(mainTitle, quesTitle, tags, link1, link2, ytlink) { const html = `<button class="btn">hi</button> <div class="panel"> <div class="topic"> <div class="question"> <p> <div class="mainTitle"><h1>${mainTitle}</h1></div> <div class="ques-title">${quesTitle}</div> <div class="link-1"><a href="${link1}">${link1}</a></div> <div class="link-2"><a href="${link2}">${link2}</a></div> <div class="tags">${tags}</div> <div class="yt-link"><a href="${ytlink}">Youtube Reference</a></div> </p> </div> </div> </div><br><br>`; quesContainer.insertAdjacentHTML('beforeend', html); }; const getData = async function() { const data = await fetch("https://test-data-gules.vercel.app/data.json"); const res = await data.json(); const finalData = res.data; finalData.forEach(element => { element.ques.forEach(function(el) { renderData(element.title, el.title, el.tags, el.p1_link, el.p2_link, el.yt_link); }) }); } getData(); // 使用事件委托绑定点击事件 quesContainer.addEventListener('click', function(e) { // 确认点击的是.btn元素 if (e.target.classList.contains('btn')) { const btn = e.target; console.log("clicked"); btn.classList.toggle("active"); const panel = btn.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } } });
方式2:在数据渲染完成后绑定事件
把获取按钮和绑定事件的代码放在getData()函数内部,等所有元素都插入DOM后再执行:
修改后的JS代码:
'use strict'; const quesContainer = document.querySelector('.container'); const renderData = function(mainTitle, quesTitle, tags, link1, link2, ytlink) { const html = `<button class="btn">hi</button> <div class="panel"> <div class="topic"> <div class="question"> <p> <div class="mainTitle"><h1>${mainTitle}</h1></div> <div class="ques-title">${quesTitle}</div> <div class="link-1"><a href="${link1}">${link1}</a></div> <div class="link-2"><a href="${link2}">${link2}</a></div> <div class="tags">${tags}</div> <div class="yt-link"><a href="${ytlink}">Youtube Reference</a></div> </p> </div> </div> </div><br><br>`; quesContainer.insertAdjacentHTML('beforeend', html); }; const getData = async function() { const data = await fetch("https://test-data-gules.vercel.app/data.json"); const res = await data.json(); const finalData = res.data; finalData.forEach(element => { element.ques.forEach(function(el) { renderData(element.title, el.title, el.tags, el.p1_link, el.p2_link, el.yt_link); }) }); // 所有元素渲染完成后再获取按钮并绑定事件 const btns = document.querySelectorAll(".btn"); console.log(btns); btns.forEach(function(element) { element.addEventListener("click", function() { console.log("clicked"); element.classList.toggle("active"); var panel = element.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); }) } getData();
内容的提问来源于stack exchange,提问作者sjaini
相关产品推荐
相关产品推荐

