Revamp event listeners

pull/2/head
paldepind 10 years ago
parent 1c509cd653
commit c2b6e7c325

@ -12,7 +12,6 @@
'use strict';
var isArr = Array.isArray;
function isString(s) { return typeof s === 'string'; }
function isPrimitive(s) { return typeof s === 'string' || typeof s === 'number'; }
function isUndef(s) { return s === undefined; }
@ -30,8 +29,6 @@ var emptyNode = VNode(undefined, {style: {}, class: {}}, [], undefined);
var frag = document.createDocumentFragment();
var eventSelectors = [];
function h(selector, b, c) {
var props = {}, children, tag, text, i;
if (arguments.length === 3) {
@ -60,11 +57,12 @@ function h(selector, b, c) {
return VNode(tag, props, children, text, undefined);
}
function arrInvoker(arr) {
return function() { arr[0](arr[1]); };
}
function updateProps(elm, oldVnode, vnode) {
var key, val, name, cur, pushed = false,
oldProps = oldVnode.props, props = vnode.props,
selectorChanged = oldProps.className !== props.className ||
oldVnode.tag !== vnode.tag;
var key, val, name, cur, old, oldProps = oldVnode.props, props = vnode.props;
for (key in props) {
val = props[key];
if (key === 'style' || key === 'class') {
@ -74,46 +72,32 @@ function updateProps(elm, oldVnode, vnode) {
if (key === 'style') {
elm.style[name] = cur;
} else {
selectorChanged = true;
elm.classList[cur ? 'add' : 'remove'](name);
}
}
}
} else if (key === 'on') {
pushed = true;
eventSelectors.push(props.on);
} else if (key !== 'key' && (key[0] !== 'o' || key[1] !== 'n')) {
elm[key] = val;
}
}
if (selectorChanged) toggleListeners(elm, oldVnode === emptyNode, pushed);
return pushed;
}
function toggleListeners(elm, justCreated, pushed) {
var i, obj, evSel, parts, start = eventSelectors.length - 1;
for (i = start; 0 <= i; --i) {
obj = eventSelectors[i];
for (evSel in obj) {
parts = evSel.split(' ');
if (parts.length === 2) {
if (elm.matches(parts[1])) {
elm.addEventListener(parts[0], obj[evSel]);
} else if (!justCreated) {
elm.removeEventListener(parts[0], obj[evSel], false);
} else if (key[0] === 'o' && key[1] === 'n') {
name = key.slice(2);
if (name !== 'insert' && name !== 'remove') {
old = oldProps[key];
if (isUndef(old)) {
elm.addEventListener(name, isArr(val) ? arrInvoker(val) : val);
} else if (isArr(old)) {
old[0] = val[0]; // Deliberately modify old array since it's
old[1] = val[1]; // captured in closure created with `arrInvoker`
}
} else if (justCreated && i === start && pushed) {
elm.addEventListener(parts[0], obj[evSel]);
}
} else if (key !== 'key') {
elm[key] = val;
}
}
}
function createElm(vnode) {
var elm, children, pushedSelectors;
var elm, children;
if (!isUndef(vnode.tag)) {
elm = vnode.elm = document.createElement(vnode.tag);
pushedSelectors = updateProps(elm, emptyNode, vnode);
updateProps(elm, emptyNode, vnode);
children = vnode.children;
if (isArr(children)) {
for (var i = 0; i < children.length; ++i) {
@ -122,7 +106,6 @@ function createElm(vnode) {
} else if (isPrimitive(vnode.text)) {
elm.textContent = vnode.text;
}
if (pushedSelectors) eventSelectors.pop();
if (vnode.props.oninsert) vnode.props.oninsert(vnode);
} else {
elm = vnode.elm = document.createTextNode(vnode.text);
@ -230,14 +213,13 @@ function updateChildren(parentElm, oldCh, newCh) {
}
function patchVnode(oldVnode, newVnode) {
var elm = newVnode.elm = oldVnode.elm, pushedSelectors;
if (!isUndef(newVnode.props)) pushedSelectors = updateProps(elm, oldVnode, newVnode);
var elm = newVnode.elm = oldVnode.elm;
if (!isUndef(newVnode.props)) updateProps(elm, oldVnode, newVnode);
if (isUndef(newVnode.text)) {
updateChildren(elm, oldVnode.children, newVnode.children);
} else if (oldVnode.text !== newVnode.text) {
elm.textContent = newVnode.text;
}
if (pushedSelectors) eventSelectors.pop();
return newVnode;
}

@ -408,82 +408,54 @@ describe('snabbdom', function() {
});
});
describe('event handling', function() {
it('attaches click event handler to children', function() {
it('attaches click event handler to element', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode = h('div', {on: {'click a.me': clicked}}, [
h('a.me', 'Click me'),
h('span', 'Not me'),
var vnode = h('div', {onclick: clicked}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode);
var a = elm.children[0];
var span = elm.children[1];
a.click();
span.click();
a.click();
assert.equal(2, result.length);
elm.click();
assert.equal(1, result.length);
});
it('attaches click event handler self', function() {
it('does not attach new listener', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode = h('div', {on: {'click': clicked}}, [
//function clicked(ev) { result.push(ev); }
var vnode1 = h('div', {onclick: function(ev) { result.push(ev); }}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode);
var a = elm.children[0];
var vnode2 = h('div', {onclick: function(ev) { result.push(ev); }}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode1);
patch(vnode1, vnode2);
elm.click();
assert.equal(1, result.length);
});
it('attaches click event handler to subchilden', function() {
it('does calls handler for function in array', function() {
var result = [];
function clicked(ev) { result.push(ev); }
function noop() { }
var vnode = h('div', {on: {'click .nothing': noop}}, [
h('span', {on: {'click a': clicked}}, [h('a', 'Click me')]),
h('span', 'Not me'),
var vnode = h('div', {onclick: [clicked, 1]}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode);
var a = elm.children[0].children[0];
a.click();
a.click();
assert.equal(2, result.length);
});
it('attaches event handler when class is added to child', function() {
var a, result = [];
function clicked(ev) { result.push(ev); }
var vnode1 = h('div', {on: {'click .btn': clicked}}, [
h('span', [h('a', 'Click me')]),
h('span', 'Not me'),
]);
var vnode2 = h('div', {on: {'click .btn': clicked}}, [
h('span', [h('a.btn', 'Click me')]),
h('span', 'Not me'),
]);
var elm = createElm(vnode1);
a = elm.children[0].children[0];
a.click();
patch(vnode1, vnode2);
a = elm.children[0].children[0];
a.click();
assert.equal(1, result.length);
elm.click();
assert.deepEqual(result, [1]);
});
it('removes event handler from children', function() {
it('handles changed value in array', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode1 = h('div', {on: {'click a.me': clicked}}, [
h('a.me', 'Click me'),
h('span', 'Not me'),
var vnode1 = h('div', {onclick: [clicked, 1]}, [
h('a', 'Click my parent'),
]);
var vnode2 = h('div', {on: {'click a.yes.me': clicked}}, [
h('a.yes', 'Click me'),
h('span', 'Not me'),
var vnode2 = h('div', {onclick: [clicked, 2]}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode1);
var a = elm.children[0];
a.click();
elm.click();
patch(vnode1, vnode2);
a.click();
assert.equal(1, result.length);
elm.click();
assert.deepEqual(result, [1, 2]);
});
});
describe('custom events', function() {

Loading…
Cancel
Save