开发者

My first jQuery plugin. Need help formatting and understanding how it works

I wrote my first plugin today: a simple tool to make the number in an element count upwards. It works great, but I built it following examples and some trial and error so I can't say I understand how it works fully.

I don't understand:

a) How I should include handy functions like the secondsToTime() function (supposing I need it to be in a function - I understand that in 开发者_开发百科this example it does not.) Why does it work here from within this.each block?

b) How are the variables I declared (_this, seconds, interval) scoped? They are all kept simultaneously for each element.

c) Could this plugin be structured better?

Code:

$(document).ready(function(){
  $('.ticker').countup();
});

(function($) {   
  $.fn.countup = function() {
    return this.each(function(){
      var _this = this,
      seconds = parseInt($(this).text()),
      interval = setInterval(updateTicker, 1000 );
      updateTicker();
      function updateTicker(){
        seconds += 1;
        time = secondsToTime(seconds);
        outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
        $(_this).text(outputtime);
      }   
      function secondsToTime(secs){
        var hours = Math.floor(secs / (60 * 60));
        var divisor_for_minutes = secs % (60 * 60);
        var minutes = Math.floor(divisor_for_minutes / 60);
        var divisor_for_seconds = divisor_for_minutes % 60; 
        var seconds = Math.ceil(divisor_for_seconds);
        var obj = { 
          "h": hours,
          "m": minutes,
          "s": seconds
        };  
        return obj;
      }   
    }); 
  };  
})(jQuery); 

Thanks for your feedback.


a) How I should include handy functions like the secondsToTime() function

I would move it out a level to within your (function($) { ... })(jQuery); function, since it doesn't have to be re-created for each element.

a) ...Why does it work here from within this.each block?

Because a function can be accessed from code defined at the same level of scope, or from a nested scope.

b) How are the variables I declared (_this, seconds, interval) scoped?

They're all specific to each call to the function you're passing into this.each. More specifically: When a function is called, an execution context for the call is created. That execution context has a variable object that contains the variables, function arguments, and functions declared within the function that was called (all of which are specific to the function call and so are created each time). Your updateTicker function (which is created for every call) is a closure over that variable object and so has an enduring reference to those variables. (More: Closures are not complicated.)

c) Could this plugin be structured better?

  1. See (a) above.
  2. I'd probably make my plug-in function a named function rather than an anonymous one. (More: Anonymouses anonymous) You already have your wrapper function (the one I mentioned in (a) above), so it would cost you nothing, but it makes debugging easier when functions actually have names.
  3. I'd probably create the jQuery object for this just once and then reuse it, rather than doing it twice at the outset and then again each time updateTicker runs. E.g., make var _this = this, => var _this = $(this), and use _this.text on the next line and within updateTicker.
  4. It's usually a good idea to force the radix on parseInt by supplying the second parameter (otherwise, weird things can happen with leading zeroes).
  5. You might consider using just one interval timer to update all of the elements, rather than an interval for each element.
  6. I'd add a way to stop the update.
  7. Be aware that timers are not precise at all, so your countdowns may drift. You might consider grabbing the starting time and calculating how long it's actually been, rather than decrementing the seconds value.

Here's a first pass implementing just #1 - #4 above, leaving the others for you to do:

(function($) {   

  $.fn.countup = MyNiftyPlugin_countup;               // #2

  function MyNiftyPlugin_countup() {                  // #2 cont'd
    return this.each(function(){
      var _this = $(this),                            // #3
          seconds = parseInt(_this.text(), 10),       // #3 cont'd, #4
          interval = setInterval(updateTicker, 1000 );

      updateTicker();

      function updateTicker(){
        seconds += 1;
        time = secondsToTime(seconds);
        outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
        _this.text(outputtime);                       // #3 cont'd
      }   
    }); 
  }

  function secondsToTime(secs){                       // #1 (moving this out a level)
    var hours = Math.floor(secs / (60 * 60));
    var divisor_for_minutes = secs % (60 * 60);
    var minutes = Math.floor(divisor_for_minutes / 60);
    var divisor_for_seconds = divisor_for_minutes % 60; 
    var seconds = Math.ceil(divisor_for_seconds);
    var obj = { 
      "h": hours,
      "m": minutes,
      "s": seconds
    };  
    return obj;
  }   
})(jQuery); 


Improving on the code sample @T.JCrowder gave.

Here were taking the interval outside the loop and only running it once. We're storing the time as an integer in the data so we only have to parseInt once. Were getting the toTime function to return the formatted string. and a couple of other minor improvements.

(function($) {   

    $.fn.countup = MyNiftyPlugin_countup;

    function MyNiftyPlugin_countup() {
        var that = this;
        function updateTicker(){
            that.each(updateNode);
        }      
        function updateNode() {
            var $this = $(this); // cache
            var seconds = $this.data("time") + 1; // get time from $.data
            $this.data("time", seconds);
            //var seconds = Date.now() // alternative get accurate time right _now_
            var time = secondsToTime(seconds)[1]; // get string from tuple
            $this.text(time).data("time", seconds);
        }
        setInterval(updateTicker, 1000);
        updateTicker();

        return this.each(function(){
            var $this = $(this); // cache
            $this.data("time", parseInt($this.text(), 10);
        }); 
    }

    function secondsToTime(secs){
        var hours = Math.floor(secs / (60 * 60));
        var divisor_for_minutes = secs % (60 * 60);
        var minutes = Math.floor(divisor_for_minutes / 60);
        var divisor_for_seconds = divisor_for_minutes % 60; 
        var seconds = Math.ceil(divisor_for_seconds);
        var time = { 
          "h": hours,
          "m": minutes,
          "s": seconds
        };  
        var outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
        return [time, outputtime];
    }   
})(jQuery); 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜