This is now an actual repo:
-
-
Save cowboy/661855 to your computer and use it in GitHub Desktop.
/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011 | |
* http://benalman.com/ | |
* Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */ | |
(function($) { | |
var o = $({}); | |
$.subscribe = function() { | |
o.on.apply(o, arguments); | |
}; | |
$.unsubscribe = function() { | |
o.off.apply(o, arguments); | |
}; | |
$.publish = function() { | |
o.trigger.apply(o, arguments); | |
}; | |
}(jQuery)); |
/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011 | |
* http://benalman.com/ | |
* Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */ | |
(function(a){var b=a({});a.subscribe=function(){b.on.apply(b,arguments)},a.unsubscribe=function(){b.off.apply(b,arguments)},a.publish=function(){b.trigger.apply(b,arguments)}})(jQuery) |
@furf - Bindable is closer traditional event "binding" than actual pub/sub.
The pub/sub pattern consists of two things: topics and messages. Topics are subscribed to by arbitrary listeners, and messages are received when that topic publishes a message. With event systems, events are bound to and fired from contexts (such as HTML elements), like the Bindable example you linked to.
The whole point of pub/sub is that it doesn't assume a context for it's topics/messages. Think of it like an application-wide notification system.
@bentruyman understood. i was just posting as a comparison. personally i use bindable not only on classes for custom events, but also the application level for handling app-wide pub/sub. i prefer it because i often have multiple apps on the same page and i can scope the pub/sub to an individual app instead of the global (which this basic pub/sub does) without need for unnecessarily deep namespacing. think of it as a var for pub/sub.
This rules, thanks
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)};
});
A handy little plugin, thanks Ben! And even littler thanks to a-laughlin, nice work! :)
JSFiddled here: http://jsfiddle.net/HvAJf/46/
a-laughlin's is 132 characters in case anyone else is wondering!
@cowboy the minified version is wrong. it uses ,
instead of ;
between the different functions
@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.
@cowboy I figured after commenting that what really broke the plugin was the missing ;
at then end, which caused issues when concatenating files together
Your concatenating utility could join JavaScript source files on ";"
instead of ""
.
God help me how do I stop getting notifications on this thing?
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));
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?
@eschwartz $({})
is just an empty jQuery object, substitute it for whatever is the equivalent in Zepto
@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.
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 ("").
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];
}
}
}
}
}
};
});
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));
Even smaller just for sillyness
var o=$({}),s='subscribe';
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 - what's the default event you're trying to prevent? isn't this limited to the arbitrary hidden topic var o
? just curious.
@kodi thanks for the requirejs module implementation!
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?
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.
@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 />')
.
I am telling you this is working really good.
Made a Typescript version of this here: https://gist.github.com/ahmed-musallam/d12a71b8a3ec8cebf52c12f81f3da154
@cowboy this gist is getting spammed
ben - check out https://github.com/furf/jquery-enable/blob/master/src/bindable.js
it evolved from a similar pub/sub pattern to the one your using. the chief differences are:
note: contains a couple methods available in the containing library