8000 jQuery Tiny Pub/Sub: A really, really, REALLY tiny pub/sub implementation for jQuery. · GitHub
[go: up one dir, main page]

Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active February 27, 2025 12:24
Show Gist options
  • Save cowboy/661855 to your computer and use it in GitHub Desktop.
Save cowboy/661855 to your computer and use it in GitHub Desktop.
jQuery Tiny Pub/Sub: A really, really, REALLY tiny pub/sub implementation for jQuery.
/*!
* jQuery Tiny Pub/Sub - v0.6 - 1/10/2011
* http://benalman.com/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($){
// Create a "dummy" jQuery object on which to bind, unbind and trigger event
// handlers. Note that $({}) works in jQuery 1.4.3+, but because .unbind on
// a "plain object" throws errors in older versions of jQuery, an element is
// used here.
var o = $('<b/>');
// Subscribe to a topic. Works just like bind, except the passed handler
// is wrapped in a function so that the event object can be stripped out.
// Even though the event object might be useful, it is unnecessary and
// will only complicate things in the future should the user decide to move
// to a non-$.event-based pub/sub implementation.
$.subscribe = function( topic, fn ) {
// Call fn, stripping out the 1st argument (the event object).
function wrapper() {
return fn.apply( this, Array.prototype.slice.call( arguments, 1 ) );
}
// Add .guid property to function to allow it to be easily unbound. Note
// that $.guid is new in jQuery 1.4+, and $.event.guid was used before.
wrapper.guid = fn.guid = fn.guid || ( $.guid ? $.guid++ : $.event.guid++ );
// Bind the handler.
o.bind( topic, wrapper );
};
// Unsubscribe from a topic. Works exactly like unbind.
$.unsubscribe = function() {
o.unbind.apply( o, arguments );
};
// Publish a topic. Works exactly like trigger.
$.publish = function 10000 () {
o.trigger.apply( o, arguments );
};
})(jQuery);
/*
* jQuery Tiny Pub/Sub - v0.6 - 1/10/2011
* http://benalman.com/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($){var a=$("<b/>");$.subscribe=function(b,c){function d(){return c.apply(this,Array.prototype.slice.call(arguments,1))}d.guid=c.guid=c.guid||($.guid?$.guid++:$.event.guid++);a.bind(b,d)};$.unsubscribe=function(){a.unbind.apply(a,arguments)};$.publish=function(){a.trigger.apply(a,arguments)}})(jQuery);
@a-laughlin
Copy link

Nice, Ben. Very elegant. Here's the same with a tiny bit more shaved off.

var o=$({}); $.each( {on:'subscribe',off:'unsubscribe',trigger:'publish'} ,function(k,v){ $[v]=function(){o[k].apply(o,arguments)}; });

@MarcusJT
Copy link

A handy little plugin, thanks Ben! And even littler thanks to a-laughlin, nice work! :)

JSFiddled here: http://jsfiddle.net/HvAJf/46/

@mahemoff
Copy link

a-laughlin's is 132 characters in case anyone else is wondering!

@stof
Copy link
stof commented Dec 14, 2011

@cowboy the minified version is wrong. it uses , instead of ; between the different functions

@cowboy
Copy link
Author
cowboy commented Dec 14, 2011

@stof, no. Read this.

@cowboy
Copy link
Author
cowboy commented Dec 14, 2011

@mahemoff note that @a-laughlin's code, once wrapped in an IIFE (152 bytes minified), is 138 bytes gzipped, while mine (185 bytes minified) is 121 bytes gzipped.

@stof
Copy link
stof commented Dec 14, 2011

@cowboy I figured after commenting that what really broke the plugin was the missing ; at then end, which caused issues when concatenating files together

@cowboy
Copy link
Author
cowboy commented Dec 14, 2011

Your concatenating utility could join JavaScript source files on ";" instead of "".

@drewwells
Copy link

God help me how do I stop getting notifications on this thing?

@paulirish
Copy link

works in IE9+, or with Modernizr 2.5 or standalone Function.prototype.bind polyfill

(function($) {
  var o         = $({});
  $.subscribe   = o.on.bind(o);
  $.unsubscribe = o.off.bind(o);
  $.publish     = o.trigger.bind(o);
}(jQuery));

@eschwartz
Copy link

Does anyone have any thoughts on using this pattern with Zepto? I've found that Zepto doesn't like $({}), though it's fine with $('<b/>'). Is this a significant performance hit to use $('<b/>'), or is there a better way to port this plugin to Zepto?

@rwaldron
Copy link

@eschwartz $({}) is just an empty jQuery object, substitute it for whatever is the equivalent in Zepto

@eschwartz
Copy link

@rwldrn Thanks, but my issue is that I haven't been able to figure out how to create an empty element in Zepto. I suppose I should pose that issue on a Zepto forum, but I thought someone might have a workaround here.

@javascripter
Copy link

Zepto only makes use of the DOM-based event system so there's no way to get a real empty element to work as the global pub/sub object. We might as well use ("").

@kodi
Copy link
kodi commented Oct 12, 2012

I wrapped this into AMD module to use it with require.js

define(function () {

    "use strict";

    /**
     *    Events. Pub/Sub system for Loosely Coupled logic.
     *    Based on Peter Higgins' port from Dojo to jQuery
     *    https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js
     *
     *    Re-adapted to vanilla Javascript
     *
     *    ----------------------------------------------------------
     *    And then wrapped to AMD Module by Dragan Bajcic @kodisha
     *
     *    @class Events
     */
    return {
        cache : {},
        /**
         *    Events.publish
         *    e.g.: Events.publish("/Article/added", [article], this);
         *
         *    @class Events
         *    @method publish
         *    @param topic {String}
         *    @param args    {Array}
         *    @param scope {Object=} Optional
         */
        publish : function (topic, args, /** {Object=} */ scope) {

            console.log('publish',topic, args);
            if (this.cache[topic]) {
                var thisTopic = this.cache[topic],
                    i = thisTopic.length - 1;

                for (i ; i >= 0 ; i -= 1) {
                    thisTopic[i].apply(scope || this, args || []);
                }
            }
        },
        /**
         *    Events.subscribe
         *    e.g.: Events.subscribe("/Article/added", Articles.validate)
         *
         *    @class Events
         *    @method subscribe
         *    @param topic {String}
         *    @param callback {Function}
         *    @return Event handler {Array}
         */
        subscribe : function (topic, callback) {

            console.log('subscribe', topic, callback);
            if (!this.cache[topic]) {
                this.cache[topic] = [];
            }
            this.cache[topic].push(callback);
            return [topic, callback];
        },
        /**
         *    Events.unsubscribe
         *    e.g.: var handle = Events.subscribe("/Article/added", Articles.validate);
         *        Events.unsubscribe(handle);
         *
         *    @class Events
         *    @method unsubscribe
         *    @param handle {Array}
         *    @param completly {Boolean}
         *    @return {type description }
         */
        unsubscribe : function (handle, completly) {
            var t = handle[0],
                i = this.cache[t].length - 1;

            if (this.cache[t]) {
                for (i ; i >= 0 ; i -= 1) {
                    if (this.cache[t][i] === handle[1]) {
                        this.cache[t].splice(this.cache[t][i], 1);
                        if (completly) {
                            delete this.cache[t];
                        }
                    }
                }
            }
        }
    };

});

@stsvilik
Copy link

I have only one concern/suggestion to this relatively simple PubSub - it doesn't take into account published events that have already happened (in the past). Why is this important? Assume for a second that I want to subscribe to an event that had already happened, or I dont know that it happened, but still want my new subscriber to be triggered with the last-published values? My suggestion is to add something like this:

(function($) {

var o = $({}), pastEvents = {};

$.subscribe = function() {
var type = arguments.slice(0, 1)[0],
handler = arguments.slice(-1)[0];
//Fire your subscribe handler if event has already happened
if(type in pastEvents) {
pastEvents[type].done(function() {
handler.apply(o, arguments);
});
}
//Subscribe to future events as well
o.on.apply(o, arguments);
};

$.unsubscribe = function() {
o.off.apply(o, arguments);
};

$.publish = function() {
var type = arguments.slice(0, 1)[0],
data = arguments.slice(1)[0];
//Preserve data for future subscribers to this event
pastEvents[type] = $.Deferred().resolve(data).promise();
o.trigger.apply(o, arguments);
};

}(jQuery));

@chrisclarke1977
Copy link

Even smaller just for sillyness

var o=$({}),s='subscribe';
$.each({on:s,off:'un'+s,trigger:'publish'},function(k,v){$[v]=function(){o[k].apply(o,arguments)};});

@zaus
Copy link
zaus commented Feb 11, 2013

If we're talking about relative sizes, why use "publish/subscribe" nomenclature? Just stick with "on/off/trigger" (or my preferred "on/off/do").

UPDATE I just found out why the "pros" stick to trigger instead of do (boo <IE9).

Example: https://gist.github.com/zaus/4756518

/* jQuery Tinier Pub/Sub - v0.9b - "on/off/do version" - 2013-02-11
 * original by http://benalman.com/ 10/27/2011
 * Original Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */

(function($) {

  // "topic" holder
  var o = $({});

  // attach each alias method
  $.each({on:0,off:0,"go":'trigger'}, function(alias,method) {
    $[alias] = function(topic, callbackOrArgs) {
        o[method || alias].apply(o, arguments);
    }
  });

}(jQuery));

@zaus
Copy link
zaus commented Feb 19, 2013

@connected - what's the default event you're trying to prevent? isn't this limited to the arbitrary hidden topic var o? just curious.

@aghouseh
Copy link

@kodi thanks for the requirejs module implementation!

@mediafreakch
Copy link

How would you add support for wildcards in the topic name? Does it even make sense as jQuery doesn't support wildcards for event names? Or is using a standalone pub/sub library the better approach?

@gmanish
Copy link
gmanish commented Dec 4, 2015

I know some javascript and little of jQuery (been playing with Ember, just for fun). I understand the on off methods, but I do not understand what var o = $({}); does. Can anyone please explain?

As always, google doesn't consider these braces and the $ in its search results.

@shshaw
Copy link
shshaw commented Apr 14, 2016

@gmanish var o = $({}) simply creates a jQuery collection with an empty object {} that becomes the recipient of all the event triggers. As other comments show, the recipient could be anything like $('<b />').

@Kiodaddy
Copy link

I am telling you this is working really good.

@ahmed-musallam
Copy link

@ionutzp
Copy link
ionutzp commented Apr 1, 2023

@cowboy this gist is getting spammed

@PestControlNumpa2
Copy link
PestControlNumpa2 commented Feb 6, 2025

At Pest Control Ann Arbor, we provide top-quality pest control solutions to protect homes and businesses from unwanted pests. Our experienced team offers safe and effective treatments for cockroaches, rodents, termites, mosquitoes, and more. We use eco-friendly methods to ensure long-term pest prevention. Customer satisfaction and a pest-free environment are our top priorities. Trust Pest Control Ann Arbor for reliable, professional, and affordable pest control services.

@bentruyman
Copy link

@PestControlNumpa2 wow that sounds like a great deal. Thank you for this information. Does your pest control work on GitHub repo comments?

@cowboy
Copy link
Author
cowboy commented Feb 10, 2025

Man why do I even bother with open source

@alyxjame
Copy link

At Zap It Pest Control, we provide top-quality solutions to keep homes and businesses free from unwanted pests. Our experienced team offers safe and effective treatments for cockroaches, rodents, termites, mosquitoes, and more. Using eco-friendly methods, we ensure long-term pest prevention. Customer satisfaction and a pest-free environment are our top priorities. Trust Pest Control Melbourne for reliable, professional, and affordable pest control services.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
0