Skip to content

Commit

Permalink
introducing namespaced events!
Browse files Browse the repository at this point in the history
  • Loading branch information
fat committed Apr 10, 2011
1 parent 9e3271d commit 4a7b157
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 46 deletions.
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -67,6 +67,20 @@ Or alternatively, you can pass an array of elements (this actually cuts down on

*(note: the focus, blur, and submit events will not delegate)*

Bean also now supports namespacing your events! This makes it much easier to target them down the line with things like remove or fire. To name space an event just add a dot followed by your unique name identifier:

bean.add(element, 'click.fat', fn);
bean.add(element, 'click.ded', fn);
bean.add(element, 'click', fn);

//later...
bean.fire(element, 'click.ded');
bean.remove(element, 'click.fat');

//alternatively you can specify mutliple remove handlers at once
bean.fire(element, 'click.ded.fat');
bean.remove(element, 'click.fat.ded');

remove()
------
<code>bean.remove()</code> is how you get rid of listeners once you no longer want them. It's also a good idea to call remove on elements before you remove elements from your dom (this gives bean a chance to clean up some things and prevents memory leaks)
Expand Down
19 changes: 12 additions & 7 deletions bean.js
Expand Up @@ -33,7 +33,7 @@
}

function retrieveUid(obj, uid) {
return (obj._uid = obj._uid || uid || _uid++);
return (obj._uid = uid || obj._uid || _uid++);
}

function listener(element, type, fn, add, custom) {
Expand All @@ -60,8 +60,10 @@
};
}

function addListener(element, type, fn, args) {
var events = retrieveEvents(element), handlers = events[type] || (events[type] = {}), uid = retrieveUid(fn, type.replace(namespace, ''));
function addListener(element, orgType, fn, args) {
var type = orgType.replace(/\..*/, ''), events = retrieveEvents(element),
handlers = events[type] || (events[type] = {}),
uid = retrieveUid(fn, orgType.replace(namespace, ''));
if (handlers[uid]) {
return element;
}
Expand Down Expand Up @@ -150,16 +152,19 @@
function fire(element, type) {
var evt, k, i, types = type.split(' ');
for (i = types.length; i--;) {
type = types[i];
var isNative = nativeEvents.indexOf(type) > -1;
if (element[addEvent]) {
type = types[i].replace(/\..*/, '');
var isNative = nativeEvents.indexOf(type) > -1,
namespace = types[i].replace(namespace, ''),
handlers = retrieveEvents(element)[type];
if (namespace) {
handlers[namespace] && handlers[namespace]();
} else if (element[addEvent]) {
evt = document.createEvent(isNative ? "HTMLEvents" : "UIEvents");
evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, context, 1);
element.dispatchEvent(evt);
} else if (element[attachEvent]) {
isNative ? element.fireEvent('on' + type, document.createEventObject()) : element['_on' + type]++;
} else {
var handlers = retrieveEvents(element)[type];
for (k in handlers) {
handlers.hasOwnProperty(k) && handlers[k]();
}
Expand Down
2 changes: 1 addition & 1 deletion bean.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 43 additions & 25 deletions src/bean.js
@@ -1,7 +1,8 @@
!function (context) {
var _uid = 1, registry = {}, collected = {},
var __uid = 1, registry = {}, collected = {},
overOut = /over|out/,
namespace = /.*(?=\..*)\.|.*/,
namespace = /[^\.]*(?=\..*)\.|.*/,
stripName = /\..*/,
addEvent = 'addEventListener',
attachEvent = 'attachEvent',
removeEvent = 'removeEventListener',
Expand All @@ -23,7 +24,7 @@
}

function retrieveUid(obj, uid) {
return (obj._uid = uid || obj._uid || _uid++);
return (obj.__uid = uid || obj.__uid || __uid++);
}

function listener(element, type, fn, add, custom) {
Expand Down Expand Up @@ -51,7 +52,7 @@
}

function addListener(element, orgType, fn, args) {
var type = orgType.replace(/\..*/, ''), events = retrieveEvents(element),
var type = orgType.replace(stripName, ''), events = retrieveEvents(element),
handlers = events[type] || (events[type] = {}),
uid = retrieveUid(fn, orgType.replace(namespace, ''));
if (handlers[uid]) {
Expand All @@ -70,20 +71,25 @@
}
listener(element, isNative ? type : 'propertychange', fn, true, !isNative && true);
handlers[uid] = fn;
fn._uid = uid;
fn.__uid = uid;
return type == 'unload' ? element : (collected[retrieveUid(element)] = element);
}

function removeListener(element, type, handler) {
var events = retrieveEvents(element);
function removeListener(element, orgType, handler) {
var uid, names, uids, i, events = retrieveEvents(element), type = orgType.replace(stripName, '');
if (!events || !events[type]) {
return element;
}
handler = events[type][handler._uid];
delete events[type][handler._uid];
type = customEvents[type] ? customEvents[type].base : type;
var isNative = element[addEvent] || nativeEvents.indexOf(type) > -1;
listener(element, isNative ? type : 'propertychange', handler, false, !isNative && type);
names = orgType.replace(namespace, '');
uids = names ? names.split('.') : [handler.__uid];
for (i = uids.length; i--;) {
uid = uids[i];
handler = events[type][uid];
delete events[type][uid];
type = customEvents[type] ? customEvents[type].base : type;
var isNative = element[addEvent] || nativeEvents.indexOf(type) > -1;
listener(element, isNative ? type : 'propertychange', handler, false, !isNative && type);
}
return element;
}

Expand Down Expand Up @@ -115,19 +121,26 @@
return element;
}

function remove(element, events, fn) {
var k, type, isString = typeof(events) == 'string', rm = removeListener, attached = retrieveEvents(element);
if (isString && /\s/.test(events)) {
events = events.split(' ');
var i = events.length - 1;
while (remove(element, events[i]) && i--) {}
function remove(element, orgEvents, fn) {
var k, type, events,
isString = typeof(orgEvents) == 'string',
names = isString && orgEvents.replace(namespace, ''),
rm = removeListener,
attached = retrieveEvents(element);
if (isString && /\s/.test(orgEvents)) {
orgEvents = orgEvents.split(' ');
var i = orgEvents.length - 1;
while (remove(element, orgEvents[i]) && i--) {}
return element;
}
events = isString ? orgEvents.replace(stripName, '') : orgEvents;
if (!attached || (isString && !attached[events])) {
return element;
}
if (typeof fn == 'function') {
rm(element, events, fn);
} else if (names) {
rm(element, orgEvents);
} else {
rm = events ? rm : remove;
type = isString && events;
Expand All @@ -140,19 +153,24 @@
}

function fire(element, type) {

//todo make fire work!!
var evt, k, i, types = type.split(' ');
for (i = types.length; i--;) {
var isNative = nativeEvents.indexOf(type) > -1, orgType = type[i], isNamespace = orgType.test(/./);
if (element[addEvent] && !isNamespace) {
type = types[i].replace(/\..*/, '');
var isNative = nativeEvents.indexOf(type) > -1,
isNamespace = types[i].replace(namespace, ''),
handlers = retrieveEvents(element)[type];
if (isNamespace) {
isNamespace = isNamespace.split('.');
for (k = isNamespace.length; k--;) {
handlers[isNamespace[k]] && handlers[isNamespace[k]]();
}
} else if (element[addEvent]) {
evt = document.createEvent(isNative ? "HTMLEvents" : "UIEvents");
evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, context, 1);
element.dispatchEvent(evt);
} else if (element[attachEvent] && !isNamespace){
} else if (element[attachEvent]) {
isNative ? element.fireEvent('on' + type, document.createEventObject()) : element['_on' + type]++;
} else {
var handlers = retrieveEvents(element)[type];
for (k in handlers) {
handlers.hasOwnProperty(k) && handlers[k]();
}
Expand Down Expand Up @@ -229,7 +247,7 @@
var bean = { add: add, remove: remove, clone: clone, fire: fire };

var clean = function (el) {
var uid = remove(el)._uid;
var uid = remove(el).__uid;
if (uid) {
delete collected[uid];
delete registry[uid];
Expand Down
40 changes: 27 additions & 13 deletions tests/tests.js
Expand Up @@ -340,19 +340,33 @@ sink('add', function (test, ok) {
bean.fire(el1, 'click.fat');
});

// test('namespace: should be able to remove handlers based on name', 1, function () {
// var el1 = document.getElementById('foo');
// var el2 = document.getElementById('bar');
// bean.add(el1, 'click.ded', function () {ok(true, 'bubbles up dom')});
// Syn.click(el2);
// });
//
// test('namespace: should be able to remove multiple handlers based on name', 1, function () {
// var el1 = document.getElementById('foo');
// var el2 = document.getElementById('bar');
// bean.add(el1, 'click.fat', function () {ok(true, 'bubbles up dom')});
// Syn.click(el2);
// });
test('namespace: should be able to target multiple namespaced event handlers with fire', 2, function () {
var el1 = document.getElementById('foo');
bean.remove(el1);
bean.add(el1, 'click.fat', function () {ok(true, 'target multiple namespaced event handlers with fire')});
bean.add(el1, 'click.ded', function () {ok(true, 'targets multiple namespaced event handlers with fire')});
bean.add(el1, 'click', function () {ok(true, 'targets multiple namespaced event handlers with fire')});
bean.fire(el1, 'click.fat.ded');
});

test('namespace: should be able to remove handlers based on name', 1, function () {
var el1 = document.getElementById('foo');
bean.remove(el1);
bean.add(el1, 'click.ded', function () {ok(true, 'removes handlers based on name')});
bean.add(el1, 'click', function () {ok(true, 'removes handlers based on name')});
bean.remove(el1, 'click.ded');
Syn.click(el1);
});

test('namespace: should be able to remove multiple handlers based on name', 1, function () {
var el1 = document.getElementById('foo');
bean.remove(el1);
bean.add(el1, 'click.fat', function () {ok(true, 'removes multiple handlers based on name')});
bean.add(el1, 'click.ded', function () {ok(true, 'removes multiple handlers based on name')});
bean.add(el1, 'click', function () {ok(true, 'removes multiple handlers based on name')});
bean.remove(el1, 'click.ded.fat');
Syn.click(el1);
});

});

Expand Down

0 comments on commit 4a7b157

Please sign in to comment.