javascriptjqueryjquery-ui-widget-factory

jQuery widget factory _destroy method


I am trying to create a timer widget that can be initialized with a number of seconds and then reset at any time by making a new call with a new 'number of seconds' to countdown from. So far I have this which creates the timer fine and counts down.

What I am trying to do is call the _destroy method first inside the _create method to remove the timer that is already on the element and also use window.clearInterval to stop the timer being re-added. this.interval is set in the _start method and then referenced in the _destroy method but I think the problem lies with the scope of this.interval.

This is what I have so far, but my _destroy method does not seem to be clearing the interval and I can't figure out why.

$.widget('time.countdown', {

    options : {
        remaining : (2 * 24 * 60 * 60 * 1000),
        updatefreq: 1000
    },

    _create : function () {
        'use strict';
        this._destroy();
    },

    _start: function () {
        'use strict';
        var secs = this.options.remaining;

        this.interval = window.setInterval((function (elem, secs) {

            return function () {

                secs -= 1;

                var days     = Math.floor(secs / (24 * 60 * 60)),
                    div_hr   = secs % (24 * 60 * 60),
                    hours    = Math.floor(div_hr / (60 * 60)),
                    div_min  = secs % (60 * 60),
                    minutes  = Math.floor(div_min / 60),
                    div_secs = div_min % 60,
                    seconds  = Math.ceil(div_secs),
                    time_parts = {
                        "d": days,
                        "h": hours,
                        "m": minutes,
                        "s": seconds
                    },
                    tstring = '',
                    c;

                for (c in time_parts) {
                    if (time_parts.hasOwnProperty(c)) {
                        tstring += time_parts[c] + c + ' ';
                    }
                }

                elem.html(tstring);
            };

        }(this.element, secs)), this.options.updatefreq);
    },

    _destroy: function() {
        'use strict';
        if (this.interval !== undefined) {
            window.clearInterval(this.interval);
        }
        this.element.html('');
    }

});

Can anyone shed some light on this?


Solution

  • Something strange with your code is that there is no call to the "private" _start() function, but nvm.

    You should add a public reset() function to :

    You should then call this reset() function when needed. Have a look in this code and explanations below :

    (function($) {
    
        $.widget('time.countdown', {
    
            options : {
                remaining : (2 * 24 * 60 * 60 * 1000),
                updatefreq: 1000
            },
    
            _create : function () {
                'use strict';
                this._start();
            },
    
            _setOption: function(key, value) {
                key == 'remaining' ?
                    this.reset(value) :
                    this._super(key, value);
            },
    
            _start: function () {
                'use strict';
                // your countdown code
            },
    
            reset: function(remaining) {
                // You should perform some checks on the given value
                this.options.remaining = remaining;
                this._destroy();
                this._start();
            },
    
            _destroy: function() {
                'use strict';
                if (this.interval !== undefined)
                    window.clearInterval(this.interval);
                this.element.html('');
            }
        });
    
    })(jQuery);
    

    Note that the _create() function is called only once, at creation time. So if you apply the plugin many times on the same element with no args like $('#timer').countdown(); then the _start() will be called only once (on first call).

    You can now reset the countdown with a new value in differents ways :

    // new value to set = 3600
    
    // Call to the "public" `reset()` function
    $('#timer').countdown('reset', 3600);
    
    // Call to the `_setOption()`
    $('#timer').countdown({remaining: 3600});
    
    // Call to the `_setOption()` 
    $('#timer').countdown('option', 'remaining', 3600);