【问题标题】:Add event listener for each element in snapshot Firestore为快照 Firestore 中的每个元素添加事件侦听器
【发布时间】:2020-11-22 01:54:20
【问题描述】:

我有以下代码,它从名为 banner 的 Firestore 集合中获取 snapshot。每个对象都有以下结构

{
   index: // each object has index
   image: // url of source image
   link: // link to be redirected when clicked the image
}

这是我从我的集合中动态添加元素到 HTML 的 HTML 代码,在这种情况下我有 5 图像

<div id="homepage-background" class="background">
      <img class="bg" src="https://firebasestorage.googleapis.com/v0/b....">
      <img class="bg" src="https://firebasestorage.googleapis.com/v0/b....">
      <img class="bg" src="https://firebasestorage.googleapis.com/v0/b....">
      <img class="bg" src="https://firebasestorage.googleapis.com/v0/b....">
      <img class="bg" src="https://firebasestorage.googleapis.com/v0/b....">
</div>

这里是函数

    let homepageBackground = document.getElementById("homepage-background");

    firebase
      .firestore()
      .collection("banner")
      .orderBy("index")
      .get()
      .then((snapshot) => {
        let array = snapshot.docs;

        for (let i = 0; i < array.length; i++) {
            console.log(array[i].data());
            console.log("i before " + i);

            let imgBackground = document.createElement("img");
            imgBackground.setAttribute("class", "bg");
            imgBackground.setAttribute("src", array[i].image);
            imgBackground.setAttribute("data-index", i);
            console.log("i after " + i);
  
            imgBackground.addEventListener("click", function () {
              const alertText = imgBackground.getAttribute("data-index");
              alert(alertText);
            });
            homepageBackground.appendChild(imgBackground);
        }
      })
      .catch((error) => {
        console.log(error);
      });

问题是我点击的每张图片都会提醒5,这是我收藏中最后一个元素的索引。我希望当我单击第一张图片时收到1 的警报,单击第二张图片,收到2 的警报,依此类推,直到第五张图片获得5 的警报。不知何故,集合中每个文档的index 属性都无法正确显示,因为无论我点击什么图片,我都会收到5 的警报,这是最后一个元素的索引。我认为问题出在 addEventListener() 函数上,该函数可能只分配给最后一个 HTML 元素,因为它总是显示 5

我不太确定为什么它不起作用以及我应该如何解决。

编辑

我在imgBackground.setAttribute("data-index", i);之前和之后添加了2个控制台日志

页面加载后显示如下

i before 0
i after 0
i before 1
i after 1
i before 2
i after 2
i before 3
i after 3
i before 4
i after 4

我的假设是所有图像都已经呈现到 HTML,因为它们正确显示在屏幕上。但只有在循环i 被设置为最后一个索引之后。我不得不提到我还有一个脚本文件负责在页面中滑动图像。它只是一个setInterval(),每 5 秒运行一次,并从该div 向每个img 添加show 类。以下是脚本。

  document.addEventListener(
    "load",
    function () {
      let slider = document.querySelector(".slider");
      let buttons = document.querySelectorAll(".btn");
      let slides = document.querySelectorAll(".img");
      let backgrounds = document.querySelectorAll(".bg");
      let options = document.querySelectorAll(".option");

      let element = document.getElementById("homepage-background");
      let numberOfChildren = element.getElementsByTagName("img").length;

      let index = 1;
      let op_index = 0;

      let size = slides[index].clientWidth;

      update();

      function update() {
        slider.style.transform = "translateX(" + -size * index + "px)";

        backgrounds.forEach((img) => img.classList.remove("show"));
        backgrounds[op_index].classList.add("show");

        options.forEach((op) => op.classList.remove("colored"));
        options[op_index].classList.add("colored");
      }

      function slide() {
        slider.style.transition = "transform .5s ease-in-out";
        update();
      }

      function btnCheck() {
        if (this.id === "prev") {
          index--;

          if (op_index === 0) {
            op_index = numberOfChildren - 1;
          } else {
            op_index--;
          }
        } else {
          index++;

          if (op_index === numberOfChildren - 1) {
            op_index = 0;
          } else {
            op_index++;
          }
        }
        slide();
      }

      slider.addEventListener("transitionend", () => {
        if (index >= numberOfChildren - 1) {
          return;
        }
        if (index <= 0) {
          return;
        }
        if (slides[index].id === "fist") {
          slider.style.transition = "none";
          index = slides.length - 2;
          slider.style.transform = "translateX(" + -size * index + "px)";
        } else if (slides[index].id === "last") {
          slider.style.transition = "none";
          index = 1;
          slider.style.transform = "translateX(" + -size * index + "px)";
        }
      });

      buttons.forEach((btn) => btn.addEventListener("click", btnCheck));
      options.forEach((option) => option.addEventListener("click", optionFunc));

      setInterval(btnCheck, 5000);
    },
    5000
  );

【问题讨论】:

  • 我可能只是在循环内的元素上设置一个data-属性,然后编写click函数来提醒事件目标的数据值。
  • 您希望这段代码有何不同之处?由于我们无法查看您的数据或知道您点击的是哪个元素,因此无法查看发生了什么。
  • @Marc 我的理解是我应该在循环内创建类似let currentImage = array[i].data(); 的东西并使用它而不是array[i].data();
  • @DougStevenson 我编辑了帖子并提供了有关该问题的更多详细信息。
  • 只是为了确认,src 在循环中被正确旋转,但索引只是被记录为 array.length - 1 基本上对吗?或者 src 也只是指向 array[4].image?

标签: javascript html firebase google-cloud-firestore


【解决方案1】:

到页面呈现时,循环已经终止并且i == 5,这就是为什么您的测试警报总是显示 5。

我同意@Marc 的观点,即您应该将元素与其索引相关联并将其存储在循环内的某个位置。

修改自here:

...
for (let i = 0; i < array.length; i++) {
  ...
  // SET attribute "data-index"
  imgBackground.setAttribute("data-index", i) // or array[i].data().index, which appears to be the same thing based on your description
  
  imgBackground.addEventListener("click", function () {
    // GET attribute "data-index"
    const alertText = imgBackground.getAttribute('data-index')
    alert(alertText);
    ...
  });
  ...
}
...

在开发者控制台中测试并正常工作。

【讨论】:

  • 感谢您的回答。我使用了您的建议,但这次它总是提醒 4 到每个元素,而之前的 5。我用新的更新代码编辑了帖子。
猜你喜欢
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
相关资源
最近更新 更多