You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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属性全局唯一,循环里给每个模态框都设置了相同的idexampleModal,所有按钮触发模态框时,永远只会匹配到页面上第一个该id的模态框,也就是item1对应的模态框
  • 没必要为每个条目单独生成一个模态框,只需要保留一个全局模态框模板,点击按钮时动态填充对应数据即可,还能减少冗余DOM。
修复实现步骤
  1. 把模态框HTML从卡片渲染循环中抽离,只在页面中保留一份单独的模态框结构
  2. 渲染卡片按钮时,给按钮添加自定义属性data-item-id,存储当前条目的id值
  3. 监听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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:03:33