JQuery plugin scope with setInterval

I am working on writing a custom countdown timer in jQuery and I am trying to write it as a plugin. I wrote a plugin based on the code from http://jqueryboilerplate.com/ and https://github.com/zenorocha/jquery-plugin-patterns/ but I ran into a scope issue.

Here's my code for the countdown:

;(function ( $, window, document, undefined ) {
var pluginName = "countdown",
    defaults = {
        year: "",
        month: "",
        day: "",
        labels: true,
        includeMS: true
    },
    times = {
        ms: 0,
        sec: 0,
        min: 0,
        hr: 0,
        day: 0
    };

function Countdown( element, options ) {
    this.element = element;

    this.options = $.extend( {}, defaults, options );

    this._defaults = defaults;
    this._name = pluginName;

    this._times = times; // I wonder if this will cause multiple timers to fail.

    this.init();
}

Countdown.prototype = {

    init: function() {
        $container = $(document.createElement('div')).addClass('rd_countdown_container').append(
            $('<span class="days countdown_time"></span> <span class="day_label countdown_label">Days</span> <span class="hours countdown_time"></span> <span class="hour_label countdown_label">Hours</span><span class="minutes countdown_time"></span> <span class="minute_label countdown_label">Minutes</span><span class="seconds countdown_time"></span> <span class="second_label countdown_label">Seconds</span><span class="ms countdown_time"></span> <span class="ms_label countdown_label">ms</span>')
            );
        if(this.options.labels === false) { $container.children('.countdown_label').hide(); }
        if(this.options.includeMS === false) { $container.children('.ms, .ms_label').hide(); }
        $(this.element).html($container);
        this.options.endDate = new Date(this.options.year, this.options.month - 1, this.options.day);
        this.intervalTimer = setInterval(this._updateCountdown, 88);
    },

    _updateCountdown: function(e) {
        var x = 0;
        var ms = this.options.endDate - new Date();
        x = this._times.ms / 1000;
        var sec = Math.floor(x % 60);
        x = x / 60;
        var min = Math.floor(x % 60);
        x = x / 60;
        var hr = Math.floor(x % 24);
        var day = Math.floor(x / 24);

        $('.rd_countdown_container .days').text(day);
        $('.rd_countdown_container .hours').text(hr);
        $('.rd_countdown_container .minutes').text(min);
        $('.rd_countdown_container .seconds').text(sec);
        $('.rd_countdown_container .ms').text(this._pad(ms % 1000, 3));
    },

    _pad: function(num, size) {
      var s = "000000000" + num;
      return s.substr(s.length-size);
    },

    stop: function() {
        console.log("Testing stop");
        clearInterval(this.intervalTimer);
    }
};

$.fn[pluginName] = function ( options ) {
    var args = arguments;
    if (options === undefined || typeof options === 'object') {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName, new Countdown( this, options ));
            }
        });
    } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
        return this.each(function () {
            var instance = $.data(this, 'plugin_' + pluginName);
            if (instance instanceof Plugin && typeof instance[options] === 'function') {
                instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
            }
        });
    }
};
})( jQuery, window, document );

      

If I understand the template correctly, all plugin settings are stored in objects created in the DOM on the page.

The problem I'm running into is that the function being called in setInterval

doesn't have access to the rest of the plugin object, so it can't actually get endDate

to calculate the time difference. Also, when I call $('.countdown').countdown('stop')

, it doesn't clear the function setInterval

.

So, is there a pattern in the jQuery plugins that I am missing, or is it just ignoring something very basic? In addition, any suggestions for increasing productivity would be welcomed. Thank!

+3


source to share


1 answer


When setInterval is called _updateCountdown

, the scope is that window

, not the countdown plugin instance. To _updateCountdown

have access to the rest of the plugin, you need to proxy the call like this:



setInterval($.proxy(this._updateCountdown, this), 88);

      

+6


source







All Articles