22

目标

我想将事件处理程序动态分配给整个站点页面上的某些 div。

我的方法

我使用 jQuery 将匿名函数绑定为选定 div 事件的处理程序。

问题

该代码迭代了一个 div 名称和关联 url 的数组。div 名称用于设置绑定目标,即将此事件处理程序附加到此 div 事件。

虽然事件处理程序已成功绑定到每个 div 事件,但由这些事件处理程序触发的操作仅针对数组中的最后一项。

所以想法是,如果用户将鼠标悬停在给定的 div 上,它应该为该 div 运行滑出动画。但是,将鼠标悬停在 div1 (rangeTabAll) 上会触发 div4 (rangeTabThm) 的滑出动画。div 2、3 等也是如此。顺序并不重要。更改数组元素,事件将始终以数组中的最后一个元素 div4 为目标。

我的代码 - (使用 jQuery)

var curTab, curDiv;
var inlineRangeNavUrls=[['rangeTabAll','range_all.html'],['rangeTabRem','range_remedial.html'],
                ['rangeTabGym','range_gym.html'],['rangeTabThm','range_thermal.html']];
        for (var i=0;i<inlineRangeNavUrls.length;i++)
        {
            curTab=(inlineRangeNavUrls[i][0]).toString();
            curDiv='#' + curTab;
            if  ($(curDiv).length)
            {
                $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
                $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
            }
        }

我的理论

我要么没有看到明显的语法错误,要么没有看到引用传递问题。最初我有以下语句来设置 curTab 的值:

curTab=inlineRangeNavUrls[i][0];

因此,当问题发生时,我认为当我更改(通过 for 循环迭代)对 curTab 的引用时,我实际上正在将所有以前的匿名函数事件处理程序的引用更改为新的 curTab 值......这就是为什么事件处理程序始终以最后一个 div 为目标。

所以我真正需要做的是将 curTab传递给匿名函数事件处理程序,而不是 curTab对象引用。

所以我认为:

curTab=(inlineRangeNavUrls[i][0]).toString();

可以解决问题,但不能。同样的交易。所以很明显我错过了一些关于这个问题的关键知识,而且可能是非常基本的知识。谢谢。

4

4 回答 4

23

您需要在每次通过循环时创建一个新变量,以便在您为事件处理程序创建的闭包中捕获它。

然而,仅仅将变量声明移动到循环中并不能做到这一点,因为JavaScript 没有为任意块引入新的作用域

强制引入新作用域的一种简单方法是使用另一个匿名函数:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  curDiv='#' + inlineRangeNavUrls[i][1];
  if ($(curDiv).length)
  {
    (function(curTab)
    {
      $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
      $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
    })(inlineRangeNavUrls[i][0]); // pass as argument to anonymous function - this will introduce a new scope
  }
}

正如 Jason 建议的那样,您实际上可以使用 jQuery 的内置hover()函数来清理它:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  (function(curTab) // introduce a new scope
  {
  $('#' + inlineRangeNavUrls[i][1])
    .hover(
      function(){showHideRangeSlidingTabs(curTab, true);},
      function(){showHideRangeSlidingTabs(curTab, false);} 
    );
  // establish per-loop variable by passsing as argument to anonymous function
  })(inlineRangeNavUrls[i][0]); 
}
于 2009-07-30T01:03:53.900 回答
19

这里发生的事情是你的匿名函数正在形成一个闭包,并带着它们的外部范围。这意味着当您在异常函数中引用 curTab 时,当事件处理程序运行该函数时,它将在您的外部范围内查找 curTab 的当前值。这将是您最后分配给 curTab 的任何内容。(不是您绑定函数时分配的内容)

你需要做的是改变这个:

$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );

对此:

$(curDiv).bind("mouseover", 
    (function (mylocalvariable) { 
        return function(){
            showHideRangeSlidingTabs(mylocalvariable, true);
        } 
    })(curTab) 
);

这会将 curTab 的值复制到外部函数的范围内,内部函数将使用它。这种复制发生在您将内部函数绑定到事件处理程序的同时,因此“mylocalvariable”反映了当时 curTab 的值。然后在下一次循环中,将创建一个具有新范围的新外部函数,并将 curTab 的下一个值复制到其中。

shog9 的回答基本上完成了同样的事情,但他的代码更加简朴。

这有点复杂,但如果你仔细想想,它是有道理的。闭包很奇怪。

编辑:哎呀,忘了返回内部函数。固定的。

于 2009-07-30T01:08:42.360 回答
1

我认为你让这变得比它需要的更复杂。如果您所做的只是在鼠标悬停/移出时分配滑动效果,请尝试使用 jquery悬停效果。

$("#mytab").hover(function(){
    $(this).next("div").slideDown("fast");},
  function(){
    $(this).next("div").slideUp("fast");
});

如果您发布了完整的 HTML,我可以告诉您确切的操作方法 :)

于 2009-07-30T01:00:54.130 回答
1

您可以将变量的值放入不存在的标签中,稍后您可以从那里读取它们。这个片段是循环体的一部分:

 s = introduction.introductions[page * 6 + i][0]; //The variables content
 $('#intro_img_'+i).attr('tag' , s);              //Store them in a tag named tag
 $('#intro_img_'+i).click( function() {introduction.selectTemplate(this, $(this).attr('tag'));}  );  //retrieve the stored data
于 2012-03-26T21:13:39.020 回答