闭包-在Javascript onClick事件中传递参数

我正在尝试在onclick事件中传递参数。 下面是一个示例代码:

<div id="div"></div>

<script language="javascript" type="text/javascript">
   var div = document.getElementById('div');

   for (var i = 0; i < 10; i++) {
       var link = document.createElement('a');
       link.setAttribute('href', '#');
       link.innerHTML = i + '';
       link.onclick=  function() { onClickLink(i+'');};
       div.appendChild(link);
       div.appendChild(document.createElement('BR'));
       }

   function onClickLink(text) {
       alert('Link ' + text + ' clicked');
       return false;
       }
    </script>

但是,每当我单击任何链接时,警报始终显示“链接10已单击”!

谁能告诉我我在做什么错?

谢谢

fatcatz asked 2020-02-13T19:25:47Z
8个解决方案
53 votes

发生这种情况是因为一旦调用函数,i就会扩展作用域。 您可以使用闭包来避免此问题。

for (var i = 0; i < 10; i++) {
   var link = document.createElement('a');
   link.setAttribute('href', '#');
   link.innerHTML = i + '';
   link.onclick = (function() {
      var currentI = i;
      return function() { 
          onClickLink(currentI + '');
      }
   })();
   div.appendChild(link);
   div.appendChild(document.createElement('BR'));
}

或者,如果您想要更简洁的语法,建议您使用Nick Craver的解决方案。

Jamie Wong answered 2020-02-13T19:25:59Z
37 votes

发生这种情况是因为它们都引用相同的i变量,该变量在每个循环中都在变化,并且在循环结束时保留为this。 您可以使用像这样的闭包来解决它:

link.onclick = function(j) { return function() { onClickLink(j+''); }; }(i);

你可以在这里试试

或者,使this成为您在该处理程序中单击的链接,如下所示:

link.onclick = function(j) { return function() { onClickLink.call(this, j); }; }(i);

您可以在这里尝试该版本

Nick Craver answered 2020-02-13T19:26:33Z
3 votes
link.onclick = function() { onClickLink(i+''); };

是一个闭包,并存储对变量i的引用,而不是对创建函数时i所保存的值的引用。 一种解决方案是将for循环的内容包装在一个函数中,以执行以下操作:

for (var i = 0; i < 10; i++) (function(i) {
    var link = document.createElement('a');
    link.setAttribute('href', '#');
    link.innerHTML = i + '';
    link.onclick=  function() { onClickLink(i+'');};
    div.appendChild(link);
    div.appendChild(document.createElement('BR'));
}(i));
Ryan Tenney answered 2020-02-13T19:26:53Z
1 votes

尝试这个:

<div id="div"></div>

<script language="javascript" type="text/javascript">
   var div = document.getElementById('div');

   for (var i = 0; i < 10; i++) {
       var f = function() {
           var link = document.createElement('a');
           var j = i; // this j is scoped to our anonymous function
                      // while i is scoped outside the anonymous function,
                      //  getting incremented by the for loop
           link.setAttribute('href', '#');
           link.innerHTML = j + '';
           link.onclick=  function() { onClickLink(j+'');};
           div.appendChild(link);
           div.appendChild(document.createElement('br')); // lower case BR, please!
       }(); // call the function immediately
   }

   function onClickLink(text) {
       alert('Link ' + text + ' clicked');
       return false;
   }
</script>
Richard JP Le Guen answered 2020-02-13T19:27:13Z
1 votes

或者您可以使用以下行:

 link.setAttribute('onClick', 'onClickLink('+i+')');

而不是这个:

link.onclick=  function() { onClickLink(i+'');};
aniri answered 2020-02-13T19:27:38Z
1 votes

另一种简单的方法(可能不是最佳做法),但是就像魅力一样。 使用javascript动态地构建元素(hyperLink或Button)的HTML标签,并且还可以传递多个参数。

// variable to hold the HTML Tags 
var ProductButtonsHTML  ="";

//Run your loop
for (var i = 0; i < ProductsJson.length; i++){
// Build the <input> Tag with the required parameters for Onclick call. Use double quotes.

ProductButtonsHTML += " <input type='button' value='" + ProductsJson[i].DisplayName + "'  
onclick = \"BuildCartById('" + ProductsJson[i].SKU+ "'," + ProductsJson[i].Id + ")\"></input> ";

}

// Add the Tags to the Div's innerHTML.
document.getElementById("divProductsMenuStrip").innerHTML = ProductButtonsHTML;
Kiran Kumar Veerabatheni answered 2020-02-13T19:27:58Z
1 votes

创建专用函数来创建链接可能更好,这样就可以避免创建两个匿名函数。 从而:

<div id="div"></div>

<script>
  function getLink(id)
  {
    var link = document.createElement('a');
    link.setAttribute('href', '#');
    link.innerHTML = id;
    link.onclick = function()
    {
      onClickLink(id);
    };
    link.style.display = 'block';
    return link;
  }
  var div = document.getElementById('div');
  for (var i = 0; i < 10; i += 1)
  {
    div.appendChild(getLink(i.toString()));
  }
</script>

尽管在两种情况下都有两个函数,但我只是认为最好将其包装在一个语义上更容易理解的函数中。

Yeti answered 2020-02-13T19:28:23Z
1 votes

onclick与addEventListener。 可能是一个偏好问题(IE> 9)。

// Using closures
function onClickLink(e, index) {   
    alert(index);
    return false;
}

var div = document.getElementById('div');

for (var i = 0; i < 10; i++) {
    var link = document.createElement('a');

    link.setAttribute('href', '#');
    link.innerHTML = i + '';
    link.addEventListener('click', (function(e) {
        var index = i;
        return function(e) {
            return onClickLink(e, index);
        }
    })(), false);
    div.appendChild(link);
    div.appendChild(document.createElement('BR'));
}

仅仅使用一个普通的data- *属性,而不是像闭包那样酷,而是..

function onClickLink(e) {       
    alert(e.target.getAttribute('data-index'));
    return false;
}

var div = document.getElementById('div');

for (var i = 0; i < 10; i++) {
    var link = document.createElement('a');

    link.setAttribute('href', '#');
    link.setAttribute('data-index', i);
    link.innerHTML = i + ' Hello';        
    link.addEventListener('click', onClickLink, false);
    div.appendChild(link);
    div.appendChild(document.createElement('BR'));
}
Alex Nolasco answered 2020-02-13T19:28:48Z
translate from https://stackoverflow.com:/questions/3495679/passing-parameters-in-javascript-onclick-event