在处理事件侦听器时,如何让 IIFE 记住变量的**每个**实例?

How do you make an IIFE remember **each** instance of a variable when dealing with event listeners?

我正在处理数量可能会发生变化的图像列表,因此固定 ID 和事件侦听器不实用。下面的代码使用正确的 ID 生成正确数量的按钮,但只有最后一个具有功能性事件侦听器。

for (var i = 0; i < amount; i++) {
    !function(index) {

        if (items[index].classList.contains('current')) {
            document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ⬤ <span class=\"offscreen\">Item ' + i + '</span></button>';
        } 
        else
        {
            document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ◯ <span class=\"offscreen\">Item ' + i + '</span></button>';
        }

        document.getElementById('bitems' + index).addEventListener("click", function(ev) {
        alert("clicked");
        });

    }(i);
}

显然 IIFE 没有像预期的那样存储单个变量,但我不明白为什么。毕竟,这就是循环中 IIFE 的全部目的。

如有任何帮助,我们将不胜感激。

IIFE 运行良好。实际上,每次更新 selectButtons 的 innerHTML 时,都会重新创建 DOM,并且附加到它的所有事件都消失了!

无需在每次迭代中更新 innerHTML,您可以将按钮附加到它,例如:

for (var i = 0; i < 10; i++) {
  !function(index) {
    var button = document.createElement("BUTTON");
    var t = document.createTextNode("Button" + index);
    button.appendChild(t);
    document.getElementById('selectButtons').appendChild(button);

    button.addEventListener("click", function(ev) {
      alert("clicked +" +index);
    });

  }(i);
}

请务必添加您需要的条件。

每次执行 innerHTML += 时,您都在替换整个 HTML,这会删除所有以前安装的事件处理程序。这是不将 HTML 视为您 innerHTML 到页面上的一堆字符串的一个很好的理由。正如在另一个答案中那样,而不是字符串,而是考虑元素。那么你也不需要像穷人的"variable name"一样使用ID来引用元素;您可以只使用元素本身。

您不需要笨拙的 IIFE。这就是 let 的目的。

这是您的代码的 cleaned-up 版本:

var buttons = document.getElementById('selectButtons');

for (let i = 0; i < amount; i++) {
  var current = items[i].classList.contains('current');
  var button = document.createElement("BUTTON");
  var bullet = document.createTextNode(current ? '◯' : '⬤')
  var span = document.createElement('span');
  van spanText = `Item ${i}`;

  span.className = 'offscreen';
  span.appendChild(spanText);
  button.appendChild(bullet);
  button.appendChild(span);
  buttons.appendChild(button);

  button.addEventListener('click', () => alert(`clicked ${i}`));
}

如果你想节省一两行,你可以利用 appendChild returns 附加的 child 和链:

buttons.appendChild(button).appendChild(span).appendChild(spanText);

如果您要做很​​多这样的事情,最好创建一些小的实用程序:

function createElementWithText(tag, text) {
  var b = document.createElement(tag);
  var t = document.createTextNode(text);

  b.appendChild(t);
  return b;
}

function button(text) { return createElementWithText('button', text); }
function span(text)   { return createElementWithText('span', text); }

现在您可以更简洁地编写代码:

var buttons = document.getElementById('selectButtons');

for (let i = 0; i < amount; i++) {
  var current = items[i].classList.contains('current');
  var button  = button(current ? '◯' : '⬤');
  var span    = span(`Item ${i}`);

  span.className = 'offscreen';
  buttons.appendChild(button).appendChild(span);

  button.addEventListener('click', () => alert(`clicked ${i}`));
}

实际上,创建一个 文档片段 比较好,预先将所有按钮添加到其中,然后将其一次性插入 DOM 中。

然而,在实践中,您最好使用某种模板语言,您可以在其中编写如下内容:

<div id="selectButtons">
  {{for i upto amount}}
    <button {{listen 'click' clicked}}>
      {{if items[i] hasClass 'current'}}◯{{else}}⬤{{endIf}}
      <span class="offscreen">Index {{i}}</span>
    </button>
  {{endFor}}
</div>

推荐特定的模板语言超出了本答案的范围。那里有很多好东西,例如 Mustache,google 可以帮助您找到,搜索 "javascript templating languages".