显示单击了哪个列表项

时间:2018-01-14 17:53:55

标签: javascript events closures listener

我一直试图围绕Javascript中的闭包以及如何执行(或者更确切地说 - 执行它们的顺序)。

这是我的例子:假设我有一个无序的项目列表,当我点击其中一个时,我想打印出哪个项目被点击了。我知道解决方案,但我不明白为什么它的工作方式。所以下面我将发布我的代码的一些变体,并给出我的相信它如何工作的想法。我希望有人对此发表评论。如果可能,请使用尽可能少的行话。想象一下,你正和一个5岁的孩子说话。

第一个例子 https://jsfiddle.net/wLpcpa5q/

我的HTML:

<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>Item 5</li>
</ul>
<span id="span"></span>

JS:

var items = document.getElementsByTagName('li');
var span = document.getElementById('span');

function clickHandler() {

    function changeHTML() {
        span.innerHTML = ('item ' + i + ' was clicked');
    }

    for (var i = 0, len = items.length; i < len; i++) {
        items[i].addEventListener("click", changeHTML);
    } 
}
clickHandler();

如果我运行它,无论我点击哪个项目,我都会得到'项目5被点击'。我相信它的发生是因为程序的流程如下:

  1. 循环遍历所有列表项并向其添加事件侦听器。 changeHTML尚未调用,因为它是对该函数的引用(最后没有())。

  2. i = 5循环结束且程序正在等待点击时。

  3. 当点击发生changeHTML时,无论点击哪个列表项,都会调用i = 5

  4. 这是对的吗?

    继续前进。

    第二个例子 https://jsfiddle.net/wLpcpa5q/1/

    而不是changeHTML我在循环中使用changeHTML()

    for (var i = 0, len = items.length; i < len; i++) {
        items[i].addEventListener("click", changeHTML());
    }
    

    这会打印出item 4 was clicked,而不会点击列表项。我很困惑。我怀疑它打印自己,因为我在函数的末尾添加了(),因此它被调用。问题:

    1. 这里的执行顺序是什么?

    2. 为什么在循环结束后调用changeHTML()?为什么不在每次迭代后调用它?

    3. 为什么不等待点击?

    4. 第三个例子 https://jsfiddle.net/wLpcpa5q/2/

      这是一个使用闭包的工作解决方案,这个概念我真的不明白:

      function clickHandler() {
      
          function wrap(i) {
              return function changeHTML() {
                  span.innerHTML = 'item ' + (i + 1) + ' was clicked';
              }
          }
      
          for (var i = 0, len = items.length; i < len; i++) {
              items[i].addEventListener("click", wrap(i));
          }
      
       }
      
       clickHandler();
      

      所以,我不明白为什么它以这种方式正常工作。

      1. 现在在循环内部我正在调用wrap(i),因为(),它看起来像第二个例子。但与第二个例子不同的是,该功能正在等待点击。这是为什么?
      2. 2.我猜一切正常,因为现在我正在返回内部函数changeHTML而不是仅仅将它放在clickHandler中。但我不明白返回changeHTML意味着什么。

        1. 所以,再次 - 我需要有人来解释这里执行的事情的顺序,在哪个时间调用哪些函数,它与循环迭代的关系等等。
        2. 谢谢。

1 个答案:

答案 0 :(得分:0)

首先在点击之前完成循环完成,因此i = 5 只有在单击

时才会调用changehtml

第二个例子是在每个循环中调用changehtml而不是点击,你添加一个空的监听器,因为添加的监听器是changehtml将返回的,你不会返回任何函数引用。

那么为什么第三个工作呢? 因为这次你真的添加了一个函数作为监听器,是的,你返回一个函数。 点击后会发生什么:

  • 调用changeHTML,它查找最接近我声明的内容,这里我是一个参数,所以他接受了作为参数传递的'i'。

在第一种情况下它无法工作,因为我在clickHandler中是相同的,最后一个值是5.

在最后一种情况下,您创建了5个不同的函数,这些函数都有自己的i