XMLHttpRequest加载JSON时Bootstrap模态框显示首项数据传参问题
问题描述
当前通过XMLHttpRequest请求外部JSON文件数据填充Bootstrap卡片的功能已正常运行,所有卡片信息均可正确展示。现在需要实现点击卡片上的「More info」按钮后,弹出Bootstrap模态框展示对应条目的更多详情,但出现异常:点击任意条目的「More info」按钮,模态框始终加载第一个item1的数据。已知解决思路是需要将对应条目的id传递给模态框才能获取正确数据,但不清楚具体实现方式。
现有代码
JSON文件内容
[{ "id": 1, "name": "item1", "price": "€5", "size": "XS", "color": "Green" }, { "id": 2, "name": "item2", "price": "€10", "size": "S", "color": "Yellow" }, { "id": 3, "name": "item3", "price": "€15", "size": "M", "color": "Blue" }, { "id": 4, "name": "item4", "price": "€20", "size": "L", "color": "Red" }, { "id": 5, "name": "item5", "price": "€25", "size": "XL", "color": "Gray" }, { "id": 6, "name": "item6", "price": "€30", "size": "XXL", "color": "Black" }]
现有JS脚本
<script> const divRes = document.querySelector('#divResult'); myRequest = () => { let xhr = new XMLHttpRequest(); xhr.open('GET', 'files/elements.json', true); xhr.send(); xhr.onload = function() { if (xhr.readyState === xhr.DONE) { if (xhr.status != 200) { divRes.innerHTML = `Error ${xhr.status}: ${xhr.statusText}`; } else { const arrResponse = JSON.parse(xhr.responseText); divRes.innerHTML = createHTMLCard(arrResponse); } } }; xhr.onerror = function() { divRes.innerHTML = "Request failed"; }; } createHTMLCard = (arrObj) => { let res = ''; for (let i = 0; i < arrObj.length; i++) { res += `<div class="col-lg-4 col-md-6 col-sm-12"> <div class="card m-2"> <div class="card-body"> <h5 class="card-title">${arrObj[i].name}</h5> <p><strong>Prijs:</strong> ${arrObj[i].price}</p> <button id="moreInfo" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">More info</button> </div> </div> </div> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog modal-dialog-scrollable"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">${arrObj[i].name}</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p><strong>Price:</strong> ${arrObj[i].price}</p> <p><strong>Size:</strong> ${arrObj[i].size}</p> <p><strong>Color:</strong> ${arrObj[i].color}</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> </div> </div> </div> </div>`; } return res; } window.addEventListener('load', myRequest); </script>
效果截图
问题根因
- HTML规范要求页面内id属性全局唯一,循环里给每个模态框都设置了相同的id
exampleModal,所有按钮触发模态框时,永远只会匹配到页面上第一个该id的模态框,也就是item1对应的模态框 - 没必要为每个条目单独生成一个模态框,只需要保留一个全局模态框模板,点击按钮时动态填充对应数据即可,还能减少冗余DOM。
修复实现步骤
- 把模态框HTML从卡片渲染循环中抽离,只在页面中保留一份单独的模态框结构
- 渲染卡片按钮时,给按钮添加自定义属性
data-item-id,存储当前条目的id值 - 监听Bootstrap模态框的
show.bs.modal触发事件,从触发事件的按钮上拿到存储的条目id,从加载好的JSON数据中匹配到对应条目,把条目的各字段值填充到模态框的对应位置即可。
修复后完整代码
<!-- 页面中单独放一个全局模态框,不要放在循环里 --> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog modal-dialog-scrollable"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel"></h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p class="modal-price"></p> <p class="modal-size"></p> <p class="modal-color"></p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> </div> </div> </div> </div> <div id="divResult" class="row"></div> <script> const divRes = document.querySelector('#divResult'); // 存加载到的全部条目数据,方便后续模态框取数 let allItems = []; // 提前缓存模态框DOM元素 const infoModal = document.getElementById('exampleModal'); const modalTitle = infoModal.querySelector('.modal-title'); const modalPrice = infoModal.querySelector('.modal-price'); const modalSize = infoModal.querySelector('.modal-size'); const modalColor = infoModal.querySelector('.modal-color'); myRequest = () => { let xhr = new XMLHttpRequest(); xhr.open('GET', 'files/elements.json', true); xhr.send(); xhr.onload = function() { if (xhr.readyState === xhr.DONE) { if (xhr.status != 200) { divRes.innerHTML = `Error ${xhr.status}: ${xhr.statusText}`; } else { allItems = JSON.parse(xhr.responseText); divRes.innerHTML = createHTMLCard(allItems); } } }; xhr.onerror = function() { divRes.innerHTML = "Request failed"; }; } createHTMLCard = (arrObj) => { let res = ''; for (let i = 0; i < arrObj.length; i++) { res += `<div class="col-lg-4 col-md-6 col-sm-12"> <div class="card m-2"> <div class="card-body"> <h5 class="card-title">${arrObj[i].name}</h5> <p><strong>Prijs:</strong> ${arrObj[i].price}</p> <!-- 按钮上加data-item-id存当前条目id,去掉重复的id属性 --> <button class="btn btn-primary more-info-btn" data-bs-toggle="modal" data-bs-target="#exampleModal" data-item-id="${arrObj[i].id}">More info</button> </div> </div> </div>`; } return res; } // 监听模态框显示事件,动态填充数据 infoModal.addEventListener('show.bs.modal', function (event) { // 拿到触发模态框的按钮 const triggerBtn = event.relatedTarget; // 取按钮上存的条目id const itemId = Number(triggerBtn.getAttribute('data-item-id')); // 匹配对应条目数据 const currentItem = allItems.find(item => item.id === itemId); // 把数据填充到模态框 modalTitle.textContent = currentItem.name; modalPrice.innerHTML = `<strong>Price:</strong> ${currentItem.price}`; modalSize.innerHTML = `<strong>Size:</strong> ${currentItem.size}`; modalColor.innerHTML = `<strong>Color:</strong> ${currentItem.color}`; }) window.addEventListener('load', myRequest); </script>
内容的提问来源于stack exchange,提问作者Proger
相关产品推荐
相关产品推荐

