简体   繁体   English

JavaScript addEventListener循环中

[英]JavaScript addEventListener in loop

I'm trying to work through Udacity's JavaScript Design Patterns course . 我正在尝试完成Udacity的JavaScript设计模式课程 One of the practice projects is to create a page with a couple of photos of cats. 练习项目之一是创建一个包含几张猫照片的页面。 Each photo has its own title and counter indicating how many times each photo has been clicked on. 每张照片都有其自己的标题和计数器,以指示每张照片被点击了多少次。 I started off with having everything hard coded in html, but I'm now trying to make it more general and easier to update by generating most of the page from JavaScript. 我首先将所有内容都用html进行了硬编码,但现在我想通过从JavaScript生成大部分页面来使其变得更通用,更容易更新。 I've been able to get all the text and images to show up, and I've got the second image's counter increasing when it's clicked, but I can't figure out why the first image isn't updating its counter. 我已经能够显示所有文本和图像,并且单击第二个图像时其计数器增加了,但我不知道为什么第一个图像未更新其计数器。 Clicking on it doesn't even seem to fire the 'click' EventListener. 单击它似乎甚至不会触发“单击” EventListener。

HTML: HTML:

<html>
<body>
<div id="cats"></div>
</body>
<script src="catclick.js"></script>
</html>

JS: JS:

"use strict";

var cat1 = {};
cat1.photo = "cat.jpg";
cat1.name = "Whiskers";
cat1.count = 0;

var cat2 = {};
cat2.photo = "cat2.jpg";
cat2.name = "Socks";
cat2.count = 0;

var cats = [cat1, cat2];

var catDiv = document.getElementById('cats');

function increaseCount(cat, span) {
    cat.count++;
    span.innerHTML = cat.count;
}

for (var i=0; i<cats.length; i++) {
    (function () {
        var currCat = cats[i];

        var existingHTML = catDiv.innerHTML;
        var newHTML = '<h3>' + currCat.name + '</h3><img id="' + currCat.name + '-photo" src="' + currCat.photo + '">';
        newHTML += '<p>' + currCat.name + ' has been clicked <span id="' + currCat.name + '-count">0</span> times.</p>';    
        catDiv.innerHTML = existingHTML + newHTML;

        var photoObj = document.getElementById(currCat.name + "-photo");
        var countSpan = document.getElementById(currCat.name + "-count");

        photoObj.addEventListener('click', function(){increaseCount(currCat, countSpan);}, false);
    }())
}

I make an array of two cat objects, get a reference to the div in my html, define the increaseCount function that updates the span containing a given cat photo's click count, and then loop through each cat object in my array, generating the html for their title, image, and counter before adding an event listener for when their image is clicked. 我制作了两个对象的数组,在我的html中获得了对div的引用,定义了increaseCount函数来更新包含给定猫照片的点击次数的span ,然后循环遍历数组中的每个猫对象,从而生成html它们的标题,图像和计数器,然后添加单击事件的侦听器。

The second image works as expected, but the first one doesn't even fire the event listener. 第二个图像按预期工作,但第一个图像甚至不触发事件侦听器。 I thought it might be related to closure , but I've wrapped everything in the loop in a function. 我以为这可能与闭包有关,但是我已经将循环中的所有内容包装在一个函数中。

Update 更新

With Pointy's suggestion, I updated the loop as follows, and it's working as expected. 在Pointy的建议下,我按如下所示更新了循环,并按预期工作。 I've also added i to the function as suggested, though I'm not sure it's actually needed in this case. 我还按照建议将i添加到了函数中,尽管我不确定在这种情况下实际上是否需要它。

for (var i=0; i<cats.length; i++) {
    (function (i) {
        var currCat = cats[i];

        var newDiv = document.createElement("div"); // div for this cat

        var catTitle = document.createElement("h3"); // cat name 
        catTitle.appendChild(document.createTextNode(currCat.name));
        newDiv.appendChild(catTitle);

        var catPhoto = document.createElement("img"); // cat photo
        catPhoto.setAttribute("id", currCat.name + "-photo");
        catPhoto.setAttribute("src", currCat.photo);
        newDiv.appendChild(catPhoto);

        var catPara = document.createElement("p"); // cat click count
        var catSpan = document.createElement("span");
        catSpan.setAttribute("id", currCat.name + "-count");
        catSpan.appendChild(document.createTextNode("0"));
        catPara.appendChild(document.createTextNode(currCat.name + " has been clicked "));
        catPara.appendChild(catSpan);
        catPara.appendChild(document.createTextNode(" times."));
        newDiv.appendChild(catPara);

        catDiv.appendChild(newDiv);

        catPhoto.addEventListener('click', function(){increaseCount(currCat, catSpan);}, false);
    }(i));
}

There's a couple of things. 有几件事。

First you need to pass in i to the function: 首先,您需要将i传递给函数:

(function (i) {
  var currCat = cats[i];
  ...
}(i));

But the second point is more tricky. 但是第二点比较棘手。 You're creating elements on the fly and trying to attach event listeners to them which doesn't work. 您正在动态创建元素,并尝试将事件侦听器附加到它们上,但这是行不通的。 What you need to take advantage of is event delegation - attaching an event listener to a parent element and catching the events that bubble up from the attached elements. 您需要利用event delegation -将事件侦听器附加到父元素,并捕获从附加元素冒出的事件。

Some changes need to be made to your code to make this work. 为了使此工作有效,需要对代码进行一些更改。

1) Add a couple of data attributes to the image elements to reference the index of the cat, and the cat name: 1)在图像元素上添加几个数据属性,以引用cat的索引和cat名称:

<img data-name="' + currCat.name + '" data-index="' + i + '" + id="' + currCat.name + '-photo" src="' + currCat.photo + '">

2) Create the new event listener on the catDiv element, picking up the values from the clicked element's data set, passing in those values to inceaseCount . 2)在catDiv元素上创建新的事件侦听器,从单击的元素的数据集中获取值,然后将这些值传递给inceaseCount

catDiv.addEventListener('click', function(e) {
  if (e.target && e.target.nodeName == "IMG") {
    var data = e.target.dataset;
    increaseCount(data.index, data.name + '-count');
  }
});

3) Rewrite the increaseCount function to account for those changes. 3)重写increaseCount功能以考虑这些变化。

function increaseCount(index, span) {
  cats[index].count++;
  document.getElementById(span).innerHTML = cats[index].count;
}

Working example 工作实例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM