event delegation foobar
by phloe
HTML
<div id="root"> <div>
<div>
</div><div>
<div>
<div class="red"></div>
</div>
</div>
</div><div>
<div>
<div class="green"><div class="blue">
<div></div></div></div>
</div><div>
</div>
<div class="blue">
<div></div></div>
</div>
</div>
CSS
div {
background-color: #EEE;
margin: 40px;
min-width: 200px;
min-height: 200px;
float: left;
}
div:after {
content: "";
display: table;
clear: both;
}
div > div {
background-color: #DDD;
}
div > div > div {
background-color: #CCC;
}
.red,
.blue,
.green {
min-width: 100px;
min-height: 100px;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.green {
background-color: green;
}
.blue > div {
min-width: 50px;
min-height: 50px;
background-color: white;
}
JavaScript
var html = document.documentElement;
var _matches = html.matches || html.matchesSelector ||
html.mozMatchesSelector || html.webkitMatchesSelector ||
html.msMatchesSelector || html.oMatchesSelector ||
function (selector) {
var elements = this.parentNode.querySelectorAll(selector);
var length = elements.length;
var i = 0;
while (i < length) {
if (elements[i++] === this) {
return true;
}
}
return false;
};
function matches (element, selector) {
return _matches.call(element, selector);
}
var elementEventProp = "__drc-util/dom/event/delegate";
var elementBindingProp = "__drc-util/dom/event/delegate_binding";
/*
element.__drc-util/dom/event/delegate: {
[event-selector]: {
wrappers: [],
listeners: []
},
...
}
*/
function matchAncestor(selector, element, root) {
root = root || document;
while (element && element != root && !matches(element, selector)) {
element = element.parentNode;
}
if (element != root) {
return element;
}
return null;
}
function getDeep (object, keys, defaults) {
var index = 0;
var length = keys.length;
var key;
while (index < length) {
key = keys[index];
if (arguments.length === 3 && index in defaults && !(key in object)) {
object[key] = defaults[index];
}
if (key in object) {
object = object[key];
}
else {
return null;
}
index++;
}
return object;
}
function wrap (element, selector, event) {
var target = matchAncestor(selector, event.target, element);
if (target) {
// @jase's hack to avoid paving over `this`...
target[elementBindingProp] = this;
target[elementBindingProp](event, target);
target[elementBindingProp] = null;
}
}
function add (element, event, selector, listener) {
if (typeof event === "object") {
var events = event;
for (event in events) {
for (var selector in events[event]) {
addSingle(element, event, selector, events[event][selector]);
}
}
}
else if (typeof listener === "function")...