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

如何实现点击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);
  }
});

关键功能说明

  1. 选中状态可视化:新增金色边框的.selected类,清晰标记已选中图片
  2. 选择逻辑控制:限制最多选中4张,点击已选中图片可取消选择
  3. 状态同步:弹窗打开时自动同步当前coin容器的图片状态,保证修改连贯性
  4. 多组件隔离:每个.mdlsc组件拥有独立的选中数据和操作逻辑,互不干扰
  5. 重复修改支持:弹窗关闭时重置选中状态,下次打开可重新选择替换

内容的提问来源于stack exchange,提问作者NAYMUR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 01:25:23