New implementation of eventlisteners module

pull/146/head
K. 9 years ago
parent fd539435f6
commit 8ca33c916b

@ -1,62 +1,79 @@
var is = require('../is'); function invokeHandler(handler, vnode, event) {
if (typeof handler === "function") {
function arrInvoker(arr) { // call function handler
return function() { handler.call(vnode, event);
if (!arr.length) return; } else if (typeof handler === "object") {
// Special case when length is two, for performance // call handler with arguments
arr.length === 2 ? arr[0](arr[1]) : arr[0].apply(undefined, arr.slice(1)); if (typeof handler[0] === "function") {
}; // special case for single argument for performance
handler.length === 2 ?
handler[0].call(vnode, handler[1], event) :
handler[0].apply(vnode, handler.slice(1).concat(event));
} else {
// call multiple handlers
for (var i = 0; i < handler.length; i++) {
invokeHandler(handler[i]);
}
}
}
} }
function fnInvoker(o) { function handleEvent(event, vnode) {
return function(ev) { var name = event.type,
if (o.fn === null) return; on = vnode.data.on;
o.fn(ev);
}; // call event handler(s) if exists
if (on && on[name]) {
invokeHandler(on[name], vnode, event);
}
}
function createListener() {
return function handler(event) {
handleEvent(event, handler.vnode);
}
} }
function updateEventListeners(oldVnode, vnode) { function updateEventListeners(oldVnode, vnode) {
var name, cur, old, elm = vnode.elm, var oldOn = oldVnode.data.on,
oldOn = oldVnode.data.on, on = vnode.data.on; oldListener = oldVnode.listener,
oldElm = oldVnode.elm,
if (!on && !oldOn) return; on = vnode && vnode.data.on,
on = on || {}; elm = vnode && vnode.elm;
oldOn = oldOn || {};
// improvement for immutable handlers
for (name in on) { if (oldElm === elm && oldOn === on) {
cur = on[name]; return;
old = oldOn[name]; }
if (old === undefined) {
if (is.array(cur)) { // remove existing listeners which no longer used
elm.addEventListener(name, arrInvoker(cur)); if (oldOn && oldListener) {
} else { for (var _name in oldOn) {
cur = {fn: cur}; // remove listener if element was changed or existing listener(s) removed
on[name] = cur; if (elm !== oldElm || !on || !on[_name]) {
elm.addEventListener(name, fnInvoker(cur)); oldElm.removeEventListener(_name, oldListener, false);
} }
} else if (is.array(old)) {
// Deliberately modify old array since it's captured in closure created with `arrInvoker`
old.length = cur.length;
for (var i = 0; i < old.length; ++i) old[i] = cur[i];
on[name] = old;
} else {
old.fn = cur;
on[name] = old;
} }
} }
if (oldOn) {
for (name in oldOn) { // add new listeners which has not already attached
if (on[name] === undefined) { if (on) {
var old = oldOn[name]; // reuse existing listener or create new
if (is.array(old)) { var listener = vnode.listener = oldVnode.listener || createListener();
old.length = 0; // update vnode for listener
} listener.vnode = vnode;
else {
old.fn = null; for (var name in on) {
} // add listener if element was changed or new listener(s) added
if (elm !== oldElm || !oldOn || !oldOn[name]) {
elm.addEventListener(name, listener, false);
} }
} }
} }
} }
module.exports = {create: updateEventListeners, update: updateEventListeners}; module.exports = {
create: updateEventListeners,
update: updateEventListeners,
destroy: updateEventListeners
};

@ -69,7 +69,7 @@ describe('event listeners', function() {
}); });
it('handles changed several values in array', function() { it('handles changed several values in array', function() {
var result = []; var result = [];
function clicked() { result.push([].slice.call(arguments)); } function clicked() { result.push([].slice.call(arguments, 0, arguments.length-1)); }
var vnode1 = h('div', {on: {click: [clicked, 1, 2, 3]}}, [ var vnode1 = h('div', {on: {click: [clicked, 1, 2, 3]}}, [
h('a', 'Click my parent'), h('a', 'Click my parent'),
]); ]);
@ -87,4 +87,61 @@ describe('event listeners', function() {
elm.click(); elm.click();
assert.deepEqual(result, [[1, 2, 3], [1, 2], [2, 3]]); assert.deepEqual(result, [[1, 2, 3], [1, 2], [2, 3]]);
}); });
it('detach attached click event handler to element', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode1 = h('div', {on: {click: clicked}}, [
h('a', 'Click my parent'),
]);
elm = patch(vnode0, vnode1).elm;
elm.click();
assert.equal(1, result.length);
var vnode2 = h('div', {on: {}}, [
h('a', 'Click my parent'),
]);
elm = patch(vnode1, vnode2).elm;
elm.click();
assert.equal(1, result.length);
});
it('multiple event handlers for same event on same element', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode1 = h('div', {on: {click: [[clicked], [clicked], [clicked]]}}, [
h('a', 'Click my parent'),
]);
elm = patch(vnode0, vnode1).elm;
elm.click();
assert.equal(3, result.length);
var vnode2 = h('div', {on: {click: [[clicked], [clicked]]}}, [
h('a', 'Click my parent'),
]);
elm = patch(vnode1, vnode2).elm;
elm.click();
assert.equal(5, result.length);
});
it('access to virtual node in event handler', function() {
var result = [];
function clicked(ev) { result.push(this); }
var vnode1 = h('div', {on: {click: clicked }}, [
h('a', 'Click my parent'),
]);
elm = patch(vnode0, vnode1).elm;
elm.click();
assert.equal(1, result.length);
assert.equal(vnode1, result[0]);
});
it('shared handlers in parent and child nodes', function() {
var result = [];
var sharedHandlers = {
click: function(ev) { result.push(ev); }
};
var vnode1 = h('div', {on: sharedHandlers}, [
h('a', {on: sharedHandlers}, 'Click my parent'),
]);
elm = patch(vnode0, vnode1).elm;
elm.click();
assert.equal(1, result.length);
elm.firstChild.click();
assert.equal(3, result.length);
});
}); });

Loading…
Cancel
Save