在setTimeout()中使用$(this)

6
我希望能够动态设置jQuery的超时时间。必须使用$("this")来定义动态设置的超时函数,但是我似乎无法让它正常工作。
一个例子:
$("div").each(function(){
    var content = $(this).attr('data-content')
    setTimeout("$(this).html('"+content+"')",$(this).attr('data-delay'));
});​

http://jsfiddle.net/qmhmQ/

如何最好地实现这个?


8
始终传递一个函数(闭包),而不是字符串。这可以确保所有内容都在作用域内,并且更易于阅读、调试等。 - Felix Kling
不要使用setTimeout("..."),而应该使用setTimeout(function(){...}) - Christoph
如果你想操作 data-,那么你应该使用 $.data(),不是吗? - Quentin
@ONOZ。有很多!阅读data文档。 - gdoron
1
自jQuery 1.4.3 版本起,HTML5 data-属性将自动被引入到jQuery的数据对象中。 - Quentin
显示剩余2条评论
6个回答

17
$("div").each(function(){
    var content = $(this).attr('data-content'),
        $this = $(this); // here $this keeps the reference of $(this)
    setTimeout(function() {

      // within this funciton you can't get the $(this) because
      // $(this) resides within in the scope of .each() function i.e $(this)
      // is an asset of .each() not setTimeout()
      // so to get $(this) here, we store it within a variable (here: $this) 
      // and then using it

        $this.html(content);
    }, $this.attr('data-delay'));
});​

DEMO


6
解释一下你所做的更改以及为什么会使这个答案变得合理。一大堆代码并不能帮助任何人学到什么东西。 - Quentin
@Quentin,感谢您的建议,我尝试解释代码,请查看并进行必要的编辑。 - thecodeparadox

3

使用闭包(一些 教程)。

使用字符串与 setTimeout 不是一个好主意。同时要注意 this,因为如果在闭包中使用,它的上下文可能会改变(例如调用位置)。

如果使用数据属性,你可以使用 jQuery 的 data 函数。

$("div").each(function() {
  var instance = $(this);
  var content = instance.data('content');
  var method = function() {
    instance.html(content);
  };
  setTimeout(method, instance.data('delay'));
});
div {
  border: 1px solid black;
  margin: 5px;
  height: 1.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-content="fus" data-delay="1000"></div>
<div data-content="ro" data-delay="2000"></div>
<div data-content="dah" data-delay="5000"></div>


能否简单解释一下闭包?我以前从未见过它们。 - ONOZ
1
我已经添加了一些教程链接。 - m0sa

3
你的代码应该像这样:

你的代码应该像这样:

  1. pass a function instead of a string. Explanation: When passing a string to setTimeout you get problems, because it runs in a different scope than you original one, and thus you get errors.

  2. use the jQuery data()method

    $("div").each(function(){
         var content = $(this).attr('data-content'),
             $el = $(this),
             setContent = function(){
                $el.html(content);
             }
         setTimeout(setContent,$el.data('delay'));
    });​
    
您可以将函数分配给变量,并将该变量作为参数传递给setTimeout,这是最清晰的方法。

2

我将扩展上面的第一个答案,

  1. 在JavaScript中使用class或id来引用div。这样可以避免页面中进一步的标签名称冲突。

因此,您更新后的HTML为,

<div data-content="fus" data-delay="1000" class="dv"></div>
<div data-content="ro" data-delay="2000" class="dv"></div>
<div data-content="dah" data-delay="5000" class="dv"></div>​

现在您更新后的 JavaScript 代码是:

$(".dv").each(function(){
    var content = $(this).attr('data-content'),
    $this = $(this);
    setTimeout(function() {
       $this.html(content);
    }, $this.attr('data-delay'));
});​

主要代码如下:

$this = $(this);

这里我们将当前元素分配给在setTimeout函数中使用的变量。

请参考此链接


我能否使用像div[data-content^=]这样的CSS选择器来避免在每个div上添加类? - ONOZ
1
抱歉耽搁了,是的,您也可以使用选择器。请参考此链接:http://jsfiddle.net/qmhmQ/9/。 - Umesh Aawte

1

将$(this)从settimeout中取出,并在$("div").each(function(){这一行之后,将其保存在本地变量中,例如'self'

var self=$(this);

并进一步利用那个自身


setTimeout传递的字符串会导致eval破坏作用域,这样行不通吧? - Quentin

1
以下看起来是空格、可读性和揭示意图的一个很好的折衷方案。
$('div').each(function(){
    var element = $(this)
    var content = element.attr('data-content')
    var delayms = element.attr('data-delay')
    var action = function() { element.html(content) }

    setTimeout(action, delayms)
})

参见:http://jsfiddle.net/wilmoore/LSs6g/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接