Extending jQuery

A couple of months ago, I was on a “Update All The Things” spree with my websites. In one of them, the blog I have for my Volvo 1800S, I’m using a purchased theme that I’ve left pretty much alone. It’s fine as far as it goes, a bit inefficient, but it did serve as a basis for my articles here on speeding up web pages (1, 2, 3). After I had written those it was time to fix the problems with the theme.

Plan A was to upgrade it to the developer’s latest version. Except it was no longer available.  (The developer had dropped it, essentially, for a more complete WordPress theme instead. Oh well.) Since I had all the (non-minified) source files, it was onto plan B instead.

Plan B involved me updating the whole shebang myself. Hey, it’s only JavaScript, right? First was to move off jQuery 1.8.2 (eek!) to the latest, 1.11.3. And, BOOM, the slideshow on the main page stopped. OK, I was starting to get the picture. It was obviously time to refresh all the other open-source libraries the theme was using, drop those my site wasn’t, and fix the theme-specific JavaScript as well. I also set into place a simple build process to minify and concatenate the script files. After a few hours work it was all done.

Anyway, during all this work I came across a bit of code from the theme developer that gave me pause and caused me to do a bit of research into jQuery.

Here’s the Julian-modified code in question:

// if the document is smaller than the viewport, force the footer to 
// the bottom of the viewport.
(function ($) {
  "use strict";

  $.fn.extend({
    fixfooter: function () {
      var fix = $(this).appendTo("div#content");
      $.extend({
        refixfooter: function () {
          var
            documentHeight = $(document.body).height() - fix.height(),
            windowHeight = $(window).height();
          if (documentHeight < windowHeight) {
            fix.height(windowHeight - documentHeight);
          }
        }
      });

      $.refixfooter();
      $(window).bind("load resize scroll", $.refixfooter);
    }
  });
}(jQuery));

And it is called later on like this:

  $("div#fix-footer").fixfooter();

All it does is to ensure that the footer of the HTML document (it has the id “fix-footer”) is forced to the bottom of the viewport, should the document be smaller in height than the viewport height. And it makes sure that this behavior survives resizing the browser window, zooming, and scrolling . Nothing too contentious. But…

Look again. There are two calls to a function called extend here: the first on the fn property of the jQuery function, and second on jQuery itself. What’s the difference?

Here’s the declaration of extend:

jQuery.extend = jQuery.fn.extend = function() { … };

So it is the very same function. (In this form, it essentially adds the properties of the parameter object to the object calling extend. It, ahem, extends it.) What about fn?

jQuery.fn = jQuery.prototype = { … };

As you can see, fn is just an alias for the jQuery prototype object.

(Aside: the jQuery function itself, the one that gets used when you call it on a selector, like the one on “#fix-footer” above, looks like this:

  jQuery = function( selector, context ) {
    return new jQuery.fn.init(selector, context);
  },

and, as you can see, all it does is to create a new jQuery object and return it. All of the properties that have been added to the jQuery prototype will then be available to the newly created jQuery object that wraps that selector. And, yes, I know the code declaration is self-referencing.)

So, in the end, the fixfooter function is added to the jQuery prototype, so it is available to all jQuery objects. The refixfooter function is not. It is only available on that one and only instance of jQuery, the global one, the function we all call to wrap selectors and the like.

Of course, there is no earthly reason why refixfooter was added to the jQuery function at all. It can quite easily be declared inside that fixfooter function and be available as part of the closure:

  $.fn.extend({
    fixfooter: function() {
      var 
        fix = $(this).appendTo("div#content"),
        refixfooter = function() {
          var 
            documentHeight = $(document.body).height() - fix.height(),
            windowHeight = $(window).height();
          if (documentHeight < windowHeight) {
            fix.height(windowHeight - documentHeight);
          }
        };
        
      refixfooter();
      $(window).bind("load resize scroll", refixfooter);
    }
  });

Mind you, if it had been coded that way in the first place, I wouldn’t have gone down this particular rabbit hole.

Macaron City

Album cover for What Have I Done to Deserve This?Now playing:
Pet Shop Boys - What Have I Done to Deserve This? [Extended Mix]
(from What Have I Done to Deserve This?)

 

Loading similar posts...   Loading links to posts on similar topics...

No Responses

Feel free to add a comment...

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response