Minimalistic jQuery widget system

A really simple widget pattern for jQuery. Personally I would recommend using Angular, Ember, React, or something else that already has a good system for this, but sometimes you don't have a choice and this simple pattern has helped me many times.

by John-Philip Johansson

HTML

<p>
    <em>This is a minimalistic jQuery widget system. It's easy to introduce into any project and it keeps widgets isolated from each other by avoiding global scopes and being easy to initialize.</em>
</p>

<div class="w-sample-clicky-thing">
    <p>
        <strong>Instance 1</strong>
    </p>
    <p class="status">Click the button below.</p>
    <a href="#" class="button">Click me!</a>
</div>

<div class="w-sample-clicky-thing">
    <p>
        <strong>Instance 2</strong>
    </p>
    <p class="status">Click the button below.</p>
    <a href="#" class="button">Click me!</a>
</div>

JavaScript

// This is the basic principle of a minimalistic jQuery based widget system.
// Every widget has one root element, with a unique classname prefix. Here I used "w-".


// Put these two methods globally in any way you like. Here I put them on the jQuery object.
$ = $.extend($, {
    // This allows you to register a widget.
    registerWidget: function (selector, callback) {
        $(document).on('w-init', function (e) {
            $(selector, e.target).each(function () {
                var $this = $(this);
                callback($this);
                $this.addClass('w-debug-initialized');
            });
        });
    },

    // Initialize all widgets on the page.
    // Note: This MUST be called at some point. Preferrably on the document ready event.
    initializeAllWidgets: function () {
        $(document).trigger('w-init');
    },

    // Initialize widgets below a certain element. Useful for HTML added after initializeAllWidgets().
    initializeWidgets: function ($startElement) {
        $startElement.trigger('w-init');
    }
});


// I recommend initializing all widgets on document ready, but you can call it later although no widget will work until this has been called.
$(function () {
    $.initializeAllWidgets();
});


// Example widget:
$.registerWidget('.w-sample-clicky-thing', function ($root) {
    // Grab the elements you need.
    var $child = $root.find('.status'); // Always use .find() from $root or a child of $root, to avoid global selectors and potential bugs with one widget affecting another.
    var $button = $root.find('.button');
    var clickCounter = 0;

    // Declare any functions you'll use.
    var myClickHandler = function () {
        clickCounter++;
        $child.text('The button got clicked ' + clickCounter + ' times!');
    }

    // Attach any events you'll need.
    $button.on('click', myClickHandler);
});