$(this)在自定义jQuery函数中不起作用

时间:2017-04-28 15:11:12

标签: jquery momentjs

我使用了moment.js和moment-duration-format plugin制作了一个jQuery倒数计时器。

请注意,$('div#countdown')在函数内部是硬编码的。该功能如此处所示。但是,如果我将硬编码引用更改为$(this),它就不起作用。 console.log($this)返回一个空对象。

我已经尝试将$(this)设置为函数开头的变量,以防间隔创建范围问题,但没有区别。

令人困惑的部分是我之前使用过这种精确的$.fn.xxx = function语法来实现自定义jQuery函数,并且$(this)在这些函数中运行良好。关于这个特定功能的一些东西正在绊倒它。

<script>
    $.fn.countdown = function ( seconds, tFormat, stopAtZero ) {
        tFormat = (typeof tFormat !== 'undefined') ? tFormat : 'hh:mm:ss';
        stopAtZero = (typeof stopAtZero !== 'undefined') ? stopAtZero : true;
        var eventTime = Date.now() + ( seconds * 1000 );
        var diffTime = eventTime - Date.now();
        var duration = moment.duration( diffTime, 'milliseconds' );
        var interval = 0;
        var counter = setInterval(function () {
            $('div#countdown').text( moment.duration( duration.asSeconds() - ++interval, 'seconds' ).format( tFormat, { trim: false }) );
            if( stopAtZero && interval >= seconds ) clearInterval( counter );
        }, 1000);
    };

    $('div#countdown').countdown( 30*60, 'mm:ss' );
</script>

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

编辑:已解决。功能很好。它只需要在Div(或文档加载后):: headdesk ::

之后

3 个答案:

答案 0 :(得分:2)

问题是因为setTimeout()中的函数在与外部函数不同的范围内运行。因此,this不是您所期望的。您需要缓存this引用。

另请注意,您可以通过向包含选项的函数提供对象来稍微改善逻辑。然后,您可以使用$.extend来提供默认值。您还应该遍历this以使用户能够提供一组对象来初始化插件。试试这个:

$.fn.countdown = function(options) {
  var defaults = {
    seconds: 0,
    tFormat: 'hh:mm:ss',
    stopAtZero: true,
    complete: function() {}
  };
  var settings = $.extend(defaults, options);

  $(this).each(function() {
    var $el = $(this);
    var eventTime = Date.now() + (settings.seconds * 1000);
    var diffTime = eventTime - Date.now();
    var duration = moment.duration(diffTime, 'milliseconds');
    var interval = 0;
    var counter = setInterval(function() {
      $el.text(moment.duration(duration.asSeconds() - ++interval, 'seconds').format(settings.tFormat, {
        trim: false
      }));
      if (settings.stopAtZero && interval >= settings.seconds) {
        clearInterval(counter);
        settings.complete();
      }
    }, 1000);
  });
}

$('div').countdown({
  seconds: 10,
  complete: function() {
    console.log('finished!');
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-duration-format/1.3.0/moment-duration-format.min.js"></script>
<div></div>

答案 1 :(得分:1)

您需要在更高的范围内捕获元素并在间隔中使用它。

$.fn.countdown = function ( seconds, tFormat, stopAtZero ) {
    tFormat = (typeof tFormat !== 'undefined') ? tFormat : 'hh:mm:ss';
    stopAtZero = (typeof stopAtZero !== 'undefined') ? stopAtZero : true;

    //Capture this here, where it points at  $('div#countdown')
    var elem = $(this);
    //writes countdown
    console.log(elem.attr('id'));

    var eventTime = Date.now() + ( seconds * 1000 );
    var diffTime = eventTime - Date.now();
    var duration = moment.duration( diffTime, 'milliseconds' );
    var interval = 0;
    var counter = setInterval(function () {

        //this here points at window

        //writes countdown
        console.log(elem.attr('id'));

        //now use your captured element here.
        elem.text( moment.duration( duration.asSeconds() - ++interval, 'seconds' ).format( tFormat, { trim: false }) );
        if( stopAtZero && interval >= seconds ) clearInterval( counter );
    }, 1000);
};

$('div#countdown').countdown( 30*60, 'mm:ss' );

答案 2 :(得分:1)

这是一个工作示例(我删除了与问题无关的moment()和格式化代码;否则除了在适当的范围内捕获“this”之外,这是相同的。)

$.fn.countdown = function ( seconds, tFormat, stopAtZero ) {
        tFormat = (typeof tFormat !== 'undefined') ? tFormat : 'hh:mm:ss';
        stopAtZero = (typeof stopAtZero !== 'undefined') ? stopAtZero : true;
        var eventTime = Date.now() + ( seconds * 1000 );
        var diffTime = eventTime - Date.now();
        var duration = 5000;
        var interval = 0;
        var self = this; // <-- capture the DOM element here
        var counter = setInterval(function () {
            // and use it here:
            $(self).text((5000 - ++interval) + ' seconds');
            if( stopAtZero && interval >= seconds ) clearInterval( counter );
        }, 1000);
    };

    $('div#countdown').countdown( 30*60, 'mm:ss' );
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="countdown"></div>