【问题标题】:Javascript variable scope issue with jquery click eventjquery点击事件的Javascript变量范围问题
【发布时间】:2011-11-23 17:54:26
【问题描述】:

我正在尝试将存储在数组中的一系列对象分配给 jquery click 事件处理程序。

问题是,当事件触发时,我只引用数组中的最后一个对象。

我整理了一个简单的例子来说明问题:

function dothis() {
    this.btns = new Array('#button1', '#button2');
}

// Add click handler to each button in array:
dothis.prototype.ClickEvents = function () {

    //get each item in array:
    for (var i in this.btns) {

        var btn = this.btns[i];
        console.debug('Adding click handler to button: ' + btn);

        $(btn).click(function () {
            alert('You clicked : ' + btn);
            return false;
        });
    }
}

var doit = new dothis();
doit.ClickEvents();

HTML 表单包含几个按钮:

<input type="submit" name="button1" value="Button1" id="button1" />
<input type="submit" name="button2" value="Button2" id="button2" />

当button1被点击时,它会说“你点击了#Button2”

似乎两个按钮单击处理程序都指向 var btn 中的同一个对象。

考虑到变量在for循环内,我不明白为什么。

有什么想法吗?

【问题讨论】:

  • JavaScript 只有函数作用域,没有块作用域。仅仅因为看起来该变量在您的循环中,它确实绑定到函数的范围。因此,当您的处理程序被调用时,btn 将指向循环结束时它指向的任何内容。匿名函数使ClickEvents 函数的作用域保持活动状态。一旦你理解了这一点,如何解决它应该是很明显的。
  • StackOverflow 上也有大量与此相关的问题/答案。在开始使用 JS 进行函数式编程时,它可能是最先让人们绊倒的事情之一。

标签: javascript jquery arrays object scope


【解决方案1】:

你的问题在这里:

alert('You clicked : ' + btn);

btn 保留上次在循环中调用时的值。从事件中的按钮读取值。

    $(btn).data('selector', btn).click(function () {
        alert('You clicked : ' + $(this).data('selector'));
        return false;
    });

http://jsfiddle.net/Mc9Jr/1/

【讨论】:

  • 我试图获取存储在数组中的值,而不是实际的按钮名称。在我的真实世界代码中,我将对象存储在数组中,并且我想在单击按钮时将其中一个对象传递给函数。
  • 将 .val() 更改为 .attr('id')。如果它们并不总是 id,则必须在按钮上放置一个带有值的数据项。
  • jsfiddle.net/Mc9Jr/1 获取数组中的值,不管它是什么选择器类型。
  • 无论您实际存储什么,数据方法都应该适合您。
  • 这在我的真实示例中不起作用,因为我试图获取数组中对象的值,而不是按钮名称。
【解决方案2】:

你必须为此使用闭包。 我不确定我是否记得正确的语法,但你可以试试这个:

$(btn).click(function () {
    return function() {
        alert('You clicked : ' + btn);
        return false;
    }
});

【讨论】:

    【解决方案3】:

    也许您只需要更改点击绑定:

    $(btn).click(function () {
        alert('You clicked : ' + $(this).attr('id'));
        return false;
    });
    

    【讨论】:

      【解决方案4】:

      你需要一个函数工厂来关闭循环变量,比如这样:

      //get each item in array:
      for (var i=0; i<this.btns.length; i++) {
      
          $(this.btns[i]).click(function(item) { 
              return function () {
                  alert('You clicked : ' + item);
                  return false;
              }
          }(this.btns[i]));
      
      }
      

      另一个不错的选择是让 jquery 帮助你。使用 jQuery.each()。此处的变量 btn 是处理函数的本地变量,因此不会在迭代之间重用。这允许您关闭它并让它保持其价值。

      $.each(this.btns, function() {
          var btn = this;
          $(this).click(function () {
              alert('You clicked : ' + btn);
              return false;
          }
      });
      

      【讨论】:

      • 谢谢,但是当我将真实的对象放入数组时,我无法让它工作。也许一个更好的例子是:jsfiddle.net/swVa7/2
      • 你真是个天才,非常感谢。现实世界的代码更容易大声笑。
      【解决方案5】:

      在事件处理程序中,“this”通常是指触发事件的元素,在这种情况下,它将是您的按钮

      所以解决问题的方法相当简单,我们无需引用 btn 变量,它位于更高的范围内并在事件处理程序触发之前很久就发生了变异,我们只需引用触发事件的元素并获取其 ID

      $(btn).click(function () {
        alert('You clicked : #' + this.id);
        return false;
      });
      

      注意:如果您的数组包含仅包含 ID 的其他选择器,这显然不会反映这一点,而只是继续显示 ID

      幸运的是,点击处理程序(以及所有其他事件处理程序 afaik)为 eventData 提供了一个额外的参数,如下所示:

      $(btn).click(btn, function (event) {
        alert('You clicked : #' + event.data);
        return false;
      });
      

      如果您要传递多个东西,请使用数组:

      $(btn).click(['foo', 'bar'], function (event) {
        alert('this should be "foo": ' + event.data[0]);
        alert('this should be "bar": ' + event.data[1]);
        return false;
      });
      

      【讨论】:

      • 这就是为什么仔细阅读文档很重要。感谢您的精彩解释
      猜你喜欢
      • 2013-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 2010-11-08
      • 1970-01-01
      • 2013-06-15
      相关资源
      最近更新 更多