Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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() {
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);
@mahemoff
Copy link

mahemoff commented Nov 23, 2011

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

drewwells commented Dec 16, 2011

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

@paulirish
Copy link

paulirish commented Feb 10, 2012

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

eschwartz commented Mar 15, 2012

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

rwaldron commented Mar 15, 2012

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

@eschwartz
Copy link

eschwartz commented Mar 15, 2012

@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

javascripter commented Mar 26, 2012

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

stsvilik commented Oct 14, 2012

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

chrisclarke1977 commented Jan 15, 2013

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));

@connected
Copy link

connected commented Feb 14, 2013

Is there a way i can e.preventDefault() on passed event? I ve tried it, but it doesn't work...

@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

aghouseh commented Apr 12, 2013

@kodi thanks for the requirejs module implementation!

@mediafreakch
Copy link

mediafreakch commented Jul 10, 2014

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

Kiodaddy commented Jun 20, 2017

I am telling you this is working really good.

@ahmed-musallam
Copy link

ahmed-musallam commented Aug 31, 2017

@lanikane
Copy link

lanikane commented Aug 16, 2022

Your article is very helpful for me. I am very impressed with your article. It gives me a lot of interesting information about quordle moviedle . Thank you very much.

@Rosendamans
Copy link

Rosendamans commented Aug 22, 2022

I suppose I should pose that issue on a Zepto forum, but I thought someone might have a workaround here. awnings near me Toms River NJ

@joanadavidson
Copy link

joanadavidson commented Sep 15, 2022

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 ("").

Joana | peeling wallpaper

@biano22
Copy link

biano22 commented Sep 29, 2022

Thank you very much for your sharing, it has helped me in my great work, I have reduced my working time a lot because of you. I have coded some games like quordle , temple run .. if you are interested please connect with me.

@andrewmcmason
Copy link

andrewmcmason commented Nov 7, 2022

Very elegant and clean code! Good job Ben! I've been using it personally at both my fun projects over at Taylordle and also Quordle game

@andrewmcmason
Copy link

andrewmcmason commented Nov 7, 2022

I strongly believe that react is the way to go, especially when you are planning on having 1M+ visitors per day. A clear example is Wordle.gg which is a wordle game site and they are built using React

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