如何实现点击Modal内图片批量替换页面4张指定图片功能?
需求实现:多组件下的Modal图片选择替换功能
原需求说明
已通过gsap.js完成Modal弹窗的弹性显隐动画,且支持页面中多个相同组件场景。需补充以下功能:
- 点击“Edit Btn”弹出Modal后,用户可从9张图片中选择最多4张
- 点击“save edit”按钮,将页面“coin”容器内的4张图片替换为选中的图片
- 支持重复执行修改操作
原代码
HTML
<div class="main"> <div class="coinsc"> <div class="coin"> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-100.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-500.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-1000.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-3000.png" alt="" /> </div> <div class="mdlsc"> <div class="modal"> <div class="modalwrap antclr"> <div class="edtchp"> <h1>Edit Chip Rack</h1> <span>SELECT 4 CHIPS TO DISPLAY</span> </div> <div class="grypnk"> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-1.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-50.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-1000.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-5.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-100.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-3000.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-10.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-500.png" alt="" /> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-5000.png" alt="" /> </div> <a href="#" class="clsebtn" > <img src="https://polite-figolla-02d41d.netlify.app/img/saveedit.png" alt="" /> </a> </div> <div class="ovlybg"></div> </div> <button class="opnBtn"> <div class="imdwidth"> <img src="https://polite-figolla-02d41d.netlify.app/img/Chips/chip-editchip.png" alt="" /> </div> </button> </div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.3/gsap.min.js"></script>
CSS
body { width: 100%; overflow-x: hidden; color: #fff; background: url(https://polite-figolla-02d41d.netlify.app/img/wefdf.png) no-repeat; background-size: cover; background-position: center center; height: 100vh; } body img { width: 100%; } body button { background: transparent; border: none; cursor: pointer; } .main { width: 500px; margin: auto; } .modal { position: fixed; z-index: 9; left: 0%; top: 0%; width: 100%; height: 100vh; place-content: center; place-items: center; display: none; } .modal .ovlybg { position: absolute; background: rgba(0, 0, 0, 0.5); top: 0px; left: 0%; z-index: 1; width: 100%; height: 100%; } .modal .modalwrap { transform: scale(0); width: 400px; margin: auto; height: -webkit-fit-content; height: -moz-fit-content; height: fit-content; border: 5px solid #fff; border-radius: 77px; position: relative; z-index: 3; background: linear-gradient(to right, #400df0, #6203b3, #940599); text-align: center; text-transform: uppercase; font-weight: 600; padding: 45px 0px; } .edtchp { text-align: center; padding: 25px 0px 2px; } .edtchp span { font-size: 1em; margin: 0; } .edtchp h1 { font-size: 2.2em; color: #fff; margin: 0; transform: translate(0px, 0px); } .clsebtn img { width: 120px; } .mdlsc { margin-left: 10px; } .grypnk { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 18px 5px; width: 80%; margin: auto; } .grypnk img { width: 100% !important; } .coinsc { display: flex; align-items: center; width: 85%; margin: auto; gap: 8px; justify-content: center; margin: 10px auto; } .coinsc .imdwidth { display: flex; align-items: center; } .coinsc .imdwidth img { width: 45px; } .coin { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; align-items: center; }
JavaScript
const items = document.querySelectorAll(".mdlsc"); const modal = document.querySelector(".modal"); items.forEach((item, index) => { item.querySelector(".opnBtn").addEventListener("click", () => { item.querySelector(".modal").style.display = "grid"; gsap.to(item.querySelector(".modalwrap"), { scale: 1, ease: "elastic.out(1, 0.3)", }); }); item.querySelector(".ovlybg").addEventListener("click", () => { gsap.to(item.querySelector(".modalwrap"), { scale: 0, ease: "elastic.out(1, 0.3)", }); item.querySelector(".modal").style.display = "none"; }); });
完善后的完整代码
HTML
(与原HTML一致,无需修改)
CSS
新增选中状态样式,其余代码不变:
/* 新增:图片选中状态样式 */ .grypnk img.selected { border: 3px solid #ffd700; border-radius: 50%; box-sizing: border-box; } /* 原CSS内容 */ body { width: 100%; overflow-x: hidden; color: #fff; background: url(https://polite-figolla-02d41d.netlify.app/img/wefdf.png) no-repeat; background-size: cover; background-position: center center; height: 100vh; } body img { width: 100%; } body button { background: transparent; border: none; cursor: pointer; } .main { width: 500px; margin: auto; } .modal { position: fixed; z-index: 9; left: 0%; top: 0%; width: 100%; height: 100vh; place-content: center; place-items: center; display: none; } .modal .ovlybg { position: absolute; background: rgba(0, 0, 0, 0.5); top: 0px; left: 0%; z-index: 1; width: 100%; height: 100%; } .modal .modalwrap { transform: scale(0); width: 400px; margin: auto; height: -webkit-fit-content; height: -moz-fit-content; height: fit-content; border: 5px solid #fff; border-radius: 77px; position: relative; z-index: 3; background: linear-gradient(to right, #400df0, #6203b3, #940599); text-align: center; text-transform: uppercase; font-weight: 600; padding: 45px 0px; } .edtchp { text-align: center; padding: 25px 0px 2px; } .edtchp span { font-size: 1em; margin: 0; } .edtchp h1 { font-size: 2.2em; color: #fff; margin: 0; transform: translate(0px, 0px); } .clsebtn img { width: 120px; } .mdlsc { margin-left: 10px; } .grypnk { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 18px 5px; width: 80%; margin: auto; } .grypnk img { width: 100% !important; } .coinsc { display: flex; align-items: center; width: 85%; margin: auto; gap: 8px; justify-content: center; margin: 10px auto; } .coinsc .imdwidth { display: flex; align-items: center; } .coinsc .imdwidth img { width: 45px; } .coin { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; align-items: center; }
JavaScript
const items = document.querySelectorAll(".mdlsc"); items.forEach((item, index) => { // 获取当前组件内的DOM元素 const modal = item.querySelector(".modal"); const modalWrap = item.querySelector(".modalwrap"); const openBtn = item.querySelector(".opnBtn"); const overlayBg = item.querySelector(".ovlybg"); const saveBtn = item.querySelector(".clsebtn"); const chipOptions = item.querySelectorAll(".grypnk img"); const coinContainer = item.closest(".coinsc").querySelector(".coin"); const coinImages = coinContainer.querySelectorAll("img"); // 存储当前选中的图片src let selectedChips = []; // 弹窗打开事件 openBtn.addEventListener("click", () => { modal.style.display = "grid"; gsap.to(modalWrap, { scale: 1, ease: "elastic.out(1, 0.3)" }); // 同步当前coin容器的图片状态到弹窗选择项 selectedChips = Array.from(coinImages).map(img => img.src); chipOptions.forEach(chip => { chip.classList.toggle("selected", selectedChips.includes(chip.src)); }); }); // 弹窗关闭事件(点击遮罩) overlayBg.addEventListener("click", closeModal); // 图片选择逻辑 chipOptions.forEach(chip => { chip.addEventListener("click", () => { const isSelected = chip.classList.contains("selected"); if (isSelected) { // 取消选中 chip.classList.remove("selected"); selectedChips = selectedChips.filter(src => src !== chip.src); } else { // 最多选4张 if (selectedChips.length < 4) { chip.classList.add("selected"); selectedChips.push(chip.src); } } }); }); // 保存修改事件 saveBtn.addEventListener("click", (e) => { e.preventDefault(); // 阻止a标签默认跳转 if (selectedChips.length === 4) { // 替换coin容器内的图片 coinImages.forEach((img, idx) => { img.src = selectedChips[idx]; }); closeModal(); } else { alert("请选择正好4张图片"); } }); // 关闭弹窗的通用函数 function closeModal() { gsap.to(modalWrap, { scale: 0, ease: "elastic.out(1, 0.3)" }); setTimeout(() => { modal.style.display = "none"; // 清空选中状态 chipOptions.forEach(chip => chip.classList.remove("selected")); selectedChips = []; }, 300); } });
关键功能说明
- 选中状态可视化:新增金色边框的
.selected类,清晰标记已选中图片 - 选择逻辑控制:限制最多选中4张,点击已选中图片可取消选择
- 状态同步:弹窗打开时自动同步当前coin容器的图片状态,保证修改连贯性
- 多组件隔离:每个
.mdlsc组件拥有独立的选中数据和操作逻辑,互不干扰 - 重复修改支持:弹窗关闭时重置选中状态,下次打开可重新选择替换
内容的提问来源于stack exchange,提问作者NAYMUR
相关产品推荐
相关产品推荐

