【问题标题】:jQuery: click function bind in for-loop with closure fixjQuery:在 for 循环中单击函数绑定并修复闭包
【发布时间】:2012-10-17 20:18:50
【问题描述】:

我被困在这里,任何提示都会很好。
我有一个 Objects objects[] 数组和一个 divnames[] 数组。我的对象具有 play() 和 stop() 之类的功能。对象及其功能在多种情况下进行了测试,它们正在工作。现在我正在尝试迭代 divnames[] 并将适当对象 [] 的操作分配给 mouseover、mouseout 和 click。
有一个关闭问题,我用我在 StackOverflow 上的另一个线程中找到的解决方案解决了这个问题。这样可行。但是剩下的问题是鼠标悬停等动作没有分配给稍后加载的div。他们正在处理从一开始就在页面上的对象。
这是我所拥有的:

$(function(){
    for (var i=0, len=divnames.length; i<len; i++) {
        if(divnames[i]){
            (function( ) { // anonymous function to fix closures
                var index = i; // also needed to fix closures
                $('#'+divnames[index]).on("click", function() {
                    objects[index].play();
                    loadContent(divnames[index]+".php");
                });
            })( ); // direct function execution to fix closures
        }
    }
});

如上所述,闭包问题由两个注释行解决,离开它们只会应用 for 循环的最后一次执行。就像现在一样,这是可行的。
但不起作用的是,点击功能也应该应用于匹配选择器的 div,但尚未加载。
但是,如果具有相同功能的代码没有在没有闭包修复的情况下在 for 循环中进行迭代,那确实有效,因为如果我理解正确,那是预期的 .on() 行为。

那么我如何让这两个所需的功能发挥作用呢?

提前感谢您的宝贵时间。

----编辑----

根据要求提供其他信息:

var divnames = [];
divnames[0] = "home";
divnames[1] = "about";
divnames[2] = "projects";

function SpriteAnim (options) {
var timerId = 0; 
    var i = 0;
    this.status = 0;
this.init = function () {
    var element = document.getElementById(options.elementId);
    element.style.width = options.width + "px";
    element.style.height = options.height + "px";
    element.style.backgroundRepeat = "no-repeat";
    element.style.backgroundImage = "url(" + options.sprite + ")";
};
this.showFrame = function (which) {
    if (which < options.frames) {
                    i = which;
        element = document.getElementById(options.elementId);
        element.style.backgroundPosition = "0px -" + which * options.height + "px";
    }
};
this.play = function () {
            this.status = 2;
    timerId = setInterval(function () {
        if (i < (options.frames - 1)) {
                            i++;
            element = document.getElementById(options.elementId);
            element.style.backgroundPosition = "0px -" + i * options.height + "px";
                    } else {
                        clearInterval(timerId);
                        this.status = 1;
                    }
    }, 100);
};
}

你可能已经猜到了,objects[] 数组在 objects[0]、objects[1]、objects[2] 中包含 3 个 SpriteAnim 对象。

objects[0] = new SpriteAnim({
    width: 7,
    height: 7,
    frames: 8,
    sprite: "myanim1.png",
    elementId: "anim0"
});
objects[1] = new SpriteAnim({
    width: 7,
    height: 7,
    frames: 8,
    sprite: "myanim1.png",
    elementId: "anim1"
});
objects[2] = new SpriteAnim({
    width: 7,
    height: 7,
    frames: 8,
    sprite: "myanim2.png",
    elementId: "anim2"
});

【问题讨论】:

  • 你能分享你的两个数组吗?这可能有助于我们为您提供更好的答案。

标签: javascript jquery arrays object closures


【解决方案1】:

您需要使用事件委托,并在父元素而不是元素本身上监听这些事件。这样,如果您以后添加新元素,就不需要重新绑定它们。

$("#buttons").on("click", ".button", function(){
    console.log("You clicked on a .button element.");
});

在这种情况下,我们绑定到#buttons 上的点击事件,但仅当调用元素与选择器.button 匹配时。您的标记将类似于以下内容:

<div id="buttons">
    <a class="button">Foo</a>
    <a class="button">Bar</a>
</div>

当点击任何.button 元素并冒泡到#buttons 父元素时,它将被拦截并触发处理程序。

【讨论】:

  • 如果这是问题所在,当它不在 for-loop & 闭包修复构造中时,为什么它可以完美地与单个元素选择器一起使用?我正在尝试应用您的解决方案,但由于每个 divname 都有一个对象,需要独立于其他对象做出反应,因此非常令人困惑。
  • @KarlKratzisheim 提供您的阵列,也许我可以为您提供更量身定制的解决方案。
【解决方案2】:

这是因为 index 是在您的绑定语句之外声明的。当点击进入该调用时,它不知道 objects[index] 是什么。

如果您想保持相同的结构,请像这样重写您的函数:

$(function(){
    for (var i=0, len=divnames.length; i<len; i++) {
        if(divnames[i]){
            (function( ) { // anonymous function to fix closures
                var index = i; // also needed to fix closures
                $('#'+divnames[index]).on("click", function(e) {
                    switch ($(e.id).attr('name')){
                        case 'home':
                            objects[0].play();
                            loadContent('home.php');
                            break;
                        case 'about':
                        // do same
                        case 'projects':
                        // do same
                        default:
                            break;
                    }
                });
            })( ); // direct function execution to fix closures
        }
    }
});

实际上,您应该这样做:

$(document).on('click','div[name=home],div[name=projects],div[name=about]', function(){
    var name = $(this).attr('name');
    switch (name){
        case 'home':
            objects[0].play();
            break;
            // and so on
    }
    loadContent(name + '.php');
});

编辑:

当你点击你的 div 时,它会意识到这一点:

objects[index].play();
loadContent(divnames[index]+".php");

【讨论】:

  • 我添加了 var index2 = index;在绑定内部,并且有 objects[index2].play();反而。仍然不起作用,匹配选择器的后期加载元素没有反应。还是我误解了解决方案的外观?
  • 你误会了。这里面的代码:$('#'+divnames[index]).on("click"... 是所有将被触发的代码,所以围绕它的循环永远不会在你的点击中被击中。您必须确定在适当的上下文中单击了哪个按钮。
  • 不要试图让你的解决方案适应问题,你应该专注于让正确的解决方案适应问题。正如我在第二个代码示例中向您展示的那样,正确的解决方案是绑定事件。我只是想向你解释为什么它不起作用。
  • 感谢您的帮助。我只有一个问题:如果我放置一个简单的 console.log("hello");在点击事件中,即使它永远不会出现,即使它不需要知道任何东西才能真正触发。我猜那是因为 for 循环中的 $('#'+divnames[index]) 也不知道它的点击索引?
  • 好吧,$('#'+divnames[index]) 只是一个选择器。这是针对选择要绑定点击事件的元素的 dom 的查询。所以,如果你只是用 .on('click') 来做,我想它应该会触发日志调用。
猜你喜欢
  • 2018-12-26
  • 2021-07-23
  • 2012-01-23
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 2012-10-29
  • 2022-08-17
  • 1970-01-01
相关资源
最近更新 更多