使用template literals渲染JSON数据后,如何访问动态元素并实现交互
问题
通过fetch获取JSON数据,用模板字符串渲染到页面后数据正常显示,但用querySelectorAll访问动态生成的元素时返回空NodeList。最终需求是:点击对应区域的按钮时,仅展示该区域的对应数据,并支持数据更新。
原代码如下:
// to fetch data from backend. // const link = 'https://jsonplaceholder.typicode.com/users'; const link = './data copy.json'; // const container = document.getElementById('.item-container'); fetch(link) .then(res => { return res.json(); }) .then(data => { console.log(data); data.forEach( gun => { const template = ` <div class="ticket"> <div class="id card"><p>${gun.gunId}</p> </div> <div class="location card"> <p class="gun-id">${gun.location}</p> </div> <div class="description card"> <button class="view" id="${gun.gunId}" onclick="modalForm(),updateForm(this.id)">Details</button> <div class="modal"> <form class="modal-content" id ="form-data"onsubmit="event.preventDefault(); validateForm();"> <div class="form-header"> <h1 id="" class="gun-form-name"></h1> <span id="" class="close" onclick="closeModal()">×</span> <span id="" class="back-arrow fa-lg" type="button" onclick="goBack()" role="button" aria-label="Go Back Button"><i class="fa-solid fa-arrow-left"></i> </span></div> <section class="main-content"> <div class="container"> <div class="card"> <div class="wrapper"> <h3 id="title-id">${gun.gunId}</h3> <div class="img"> <img class="gun-image"src="gun image.PNG" alt="test"> <button class="overlay-btn btn-1" type="button">${gun.partId.lightPipeId}</button> <button class="overlay-btn btn-2" type="button">${gun.partId.lightbugId}</button> <button class="overlay-btn btn-3" type="button">${gun.partId.batteryId}</button> <button class="overlay-btn btn-4" type="button">${gun.partId.triggerId}</button> <button class="overlay-btn btn-5" type="button">${gun.partId.triggerPcb}</button> <button class="overlay-btn btn-6" type="button">${gun.partId.triggerSpring}</button> <button class="overlay-btn btn-7" type="button">${gun.partId.grayCable}</button> <button class="overlay-btn btn-8" type="button">${gun.partId.powerCableid}</button> <button class="overlay-btn btn-9" type="button">${gun.partId.stockPcb}</button> </div> <div class="details"> <p class="info">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi ut iure molestiae dolores commodi amet reiciendis recusandae voluptatem porro cumque fuga excepturi eum necessitatibus, vel, assumenda alias voluptas debitis veniam.</p> <p class="info">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi ut iure molestiae dolores commodi amet reiciendis recusandae voluptatem porro cumque fuga excepturi eum necessitatibus, vel, assumenda alias voluptas debitis veniam.</p> <p class="info">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi ut iure molestiae dolores commodi amet reiciendis recusandae voluptatem porro cumque fuga excepturi eum necessitatibus, vel, assumenda alias voluptas debitis veniam.</p> <p class="info">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi ut iure molestiae dolores commodi amet reiciendis recusandae voluptatem porro cumque fuga excepturi eum necessitatibus, vel, assumenda alias voluptas debitis veniam.</p> <p class="info">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi ut iure molestiae dolores commodi amet reiciendis recusandae voluptatem porro cumque fuga excepturi eum necessitatibus, vel, assumenda alias voluptas debitis veniam.</p> </div> <div class="btn"> <button class="close-ticket"type="button">Close Ticket</button> </div> </div> </div> </div> </section> </form> </div> </div> <div class="close-bt card"> <button class="delete-button"id="${gun.gunId}">Close</button> </div> </div> </div>` document.getElementById('item-container').insertAdjacentHTML('beforeend', template); }); document.getElementById('open-tickets').innerHTML=data.length; }) .catch(error=> console.log(error)); const modal = document.querySelectorAll(".modal"); console.log(modal);
最优实现方案
1. 解决动态元素获取为空的问题
fetch是异步操作,你把querySelectorAll(".modal")写在fetch外部时,DOM还未完成渲染,所以返回空NodeList。必须将获取动态元素的代码移到fetch的then回调内部,确保DOM渲染完成后执行:
fetch(link) .then(res => res.json()) .then(data => { // 渲染DOM的代码... // 这里才能正确获取到动态生成的.modal元素 const modal = document.querySelectorAll(".modal"); console.log(modal); })
2. 用事件委托处理动态元素点击
模板内写onclick属性不利于维护,且动态元素直接绑定事件容易失效,推荐用事件委托:给静态父元素(如#item-container)绑定点击事件,通过判断目标元素类名处理不同按钮逻辑。
3. 优化模态框展示逻辑
当前每个ticket都渲染一个模态框会造成DOM冗余,推荐只保留一个全局模态框,点击按钮时根据gunId匹配对应数据,再填充模态框内容,提升性能。
4. 数据更新实现
更新数据时,先同步修改数据源(如全局存储的gunData数组),再局部更新对应DOM内容,避免全量重渲染。
修改后的完整代码示例
const link = './data copy.json'; const container = document.getElementById('item-container'); // 全局模态框(提前写在HTML中,避免动态生成多个) const modal = document.getElementById('global-modal'); const modalContent = modal.querySelector('.modal-content'); // 事件委托处理所有按钮点击 container.addEventListener('click', (e) => { const target = e.target; // 处理Details按钮点击 if (target.classList.contains('view')) { const gunId = target.id; const gun = window.gunData.find(item => item.gunId === gunId); if (gun) { fillModal(gun); modal.style.display = 'block'; } } // 处理Close按钮点击 else if (target.classList.contains('delete-button')) { const gunId = target.id; // 删除对应ticket const ticket = target.closest('.ticket'); ticket.remove(); // 更新数据源和计数 window.gunData = window.gunData.filter(item => item.gunId !== gunId); document.getElementById('open-tickets').textContent = window.gunData.length; } // 处理模态框关闭按钮 else if (target.classList.contains('close')) { modal.style.display = 'none'; } }); // 填充模态框数据 function fillModal(gun) { modalContent.querySelector('#title-id').textContent = gun.gunId; modalContent.querySelector('.gun-form-name').textContent = `Gun ${gun.gunId}`; // 更新零件按钮内容 const overlayBtns = modalContent.querySelectorAll('.overlay-btn'); overlayBtns[0].textContent = gun.partId.lightPipeId; overlayBtns[1].textContent = gun.partId.lightbugId; overlayBtns[2].textContent = gun.partId.batteryId; overlayBtns[3].textContent = gun.partId.triggerId; overlayBtns[4].textContent = gun.partId.triggerPcb; overlayBtns[5].textContent = gun.partId.triggerSpring; overlayBtns[6].textContent = gun.partId.grayCable; overlayBtns[7].textContent = gun.partId.powerCableid; overlayBtns[8].textContent = gun.partId.stockPcb; } // 数据更新函数(示例:修改某个零件ID) function updateGunPart(gunId, partKey, newValue) { const gun = window.gunData.find(item => item.gunId === gunId); if (gun) { gun.partId[partKey] = newValue; // 如果模态框正展示该gun,同步更新模态框 if (modal.style.display === 'block' && modalContent.querySelector('#title-id').textContent === gunId) { fillModal(gun); } } } // 初始化数据 fetch(link) .then(res => res.json()) .then(data => { window.gunData = data; // 全局存储数据,方便后续操作 data.forEach(gun => { const template = ` <div class="ticket"> <div class="id card"><p>${gun.gunId}</p></div> <div class="location card"><p class="gun-id">${gun.location}</p></div> <div class="description card"> <button class="view" id="${gun.gunId}">Details</button> </div> <div class="close-bt card"> <button class="delete-button" id="${gun.gunId}">Close</button> </div> </div> `; container.insertAdjacentHTML('beforeend', template); }); document.getElementById('open-tickets').textContent = data.length; }) .catch(error => console.log(error));
对应的HTML需提前添加全局模态框:
<div class="modal" id="global-modal"> <form class="modal-content" id="form-data" onsubmit="event.preventDefault(); validateForm();"> <div class="form-header"> <h1 class="gun-form-name"></h1> <span class="close">×</span> <span class="back-arrow fa-lg" type="button" onclick="goBack()" role="button" aria-label="Go Back Button"> <i class="fa-solid fa-arrow-left"></i> </span> </div> <section class="main-content"> <div class="container"> <div class="card"> <div class="wrapper"> <h3 id="title-id"></h3> <div class="img"> <img class="gun-image" src="gun image.PNG" alt="test"> <button class="overlay-btn btn-1" type="button"></button> <button class="overlay-btn btn-2" type="button"></button> <button class="overlay-btn btn-3" type="button"></button> <button class="overlay-btn btn-4" type="button"></button> <button class="overlay-btn btn-5" type="button"></button> <button class="overlay-btn btn-6" type="button"></button> <button class="overlay-btn btn-7" type="button"></button> <button class="overlay-btn btn-8" type="button"></button> <button class="overlay-btn btn-9" type="button"></button> </div> <div class="details"> <p class="info">Lorem ipsum dolor sit amet...</p> <!-- 其他详情文本 --> </div> <div class="btn"> <button class="close-ticket" type="button">Close Ticket</button> </div> </div> </div> </div> </section> </form> </div>
内容的提问来源于stack exchange,提问作者Jhona
相关产品推荐
相关产品推荐

