Add and remove event listeners based on selectors

pull/2/head
paldepind 10 years ago
parent cf8b0291d8
commit 1de8e0b9a8

@ -30,6 +30,8 @@ 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) {
@ -58,8 +60,11 @@ function h(selector, b, c) {
return VNode(tag, props, children, text, undefined);
}
function updateProps(elm, oldProps, props) {
var key, val, name, on;
function updateProps(elm, oldVnode, vnode) {
var key, val, name, on, pushed = false,
oldProps = oldVnode.props, props = vnode.props,
selectorChanged = oldProps.className !== props.className ||
oldVnode.tag !== vnode.tag;
for (key in props) {
val = props[key];
if (key === 'style') {
@ -73,22 +78,45 @@ function updateProps(elm, oldProps, props) {
for (name in val) {
on = val[name];
if (on !== oldProps.class[name]) {
selectorChanged = true;
elm.classList[on ? 'add' : 'remove'](name);
}
}
} else if (key !== 'key') {
} else if (key === 'on') {
pushed = true;
eventSelectors.push(vnode.props.on);
} else if (key !== 'key' && key !== 'on') {
elm[key] = val;
}
}
if (selectorChanged === true) 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 (justCreated && i === start && pushed) {
elm.addEventListener(parts[0], obj[evSel]);
}
}
}
}
function createElm(vnode) {
var elm, children;
var elm, children, pushedSelectors;
if (!isUndef(vnode.tag)) {
elm = document.createElement(vnode.tag);
if (!isUndef(vnode.props)) {
updateProps(elm, emptyNode.props, vnode.props);
}
pushedSelectors = updateProps(elm, emptyNode, vnode);
children = vnode.children;
if (isArr(children)) {
for (var i = 0; i < children.length; ++i) {
@ -97,6 +125,7 @@ function createElm(vnode) {
} else if (isPrimitive(vnode.text)) {
elm.textContent = vnode.text;
}
if (pushedSelectors === true) eventSelectors.pop();
} else {
elm = document.createTextNode(vnode.text);
}
@ -200,18 +229,17 @@ function updateChildren(parentElm, oldCh, newCh) {
}
function patchVnode(oldVnode, newVnode) {
var elm = newVnode.elm = oldVnode.elm;
if (!isUndef(newVnode.props)) {
updateProps(elm, oldVnode.props, newVnode.props);
}
var elm = newVnode.elm = oldVnode.elm, pushedSelectors;
pushedSelectors = 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 === true) eventSelectors.pop();
return newVnode;
}
return {h: h, createElm: createElm, patchElm: patchVnode, patch: patchVnode, emptyNodeAt: emptyNodeAt, emptyNode: emptyNode};
return {h: h, createElm: createElm, patch: patchVnode, emptyNodeAt: emptyNodeAt, emptyNode: emptyNode};
}));

@ -3,7 +3,7 @@ var shuffle = require('knuth-shuffle').knuthShuffle;
var snabbdom = require('../snabbdom');
var createElm = snabbdom.createElm;
var patchElm = snabbdom.patchElm;
var patch = snabbdom.patch;
var h = snabbdom.h;
function prop(name) {
@ -113,7 +113,7 @@ describe('snabbdom', function() {
var vnode1 = h('i', {class: {i: true, am: true, horse: true}});
var vnode2 = h('i', {class: {i: true, am: true, horse: false}});
var elm = createElm(vnode1);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert(elm.classList.contains('i'));
assert(elm.classList.contains('am'));
assert(!elm.classList.contains('horse'));
@ -122,7 +122,7 @@ describe('snabbdom', function() {
var vnode1 = h('i', {class: {i: true, am: true, horse: true}});
var vnode2 = h('i', {class: {i: true, am: true, horse: false}});
var elm = createElm(vnode1);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert(elm.classList.contains('i'));
assert(elm.classList.contains('am'));
assert(!elm.classList.contains('horse'));
@ -134,10 +134,10 @@ describe('snabbdom', function() {
var elm = createElm(vnode1);
assert.equal(elm.style.fontSize, '14px');
assert.equal(elm.style.display, 'inline');
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.style.fontSize, '12px');
assert.equal(elm.style.display, 'block');
patchElm(vnode2, vnode3);
patch(vnode2, vnode3);
assert.equal(elm.style.fontSize, '10px');
assert.equal(elm.style.display, 'block');
});
@ -149,7 +149,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 3].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 1);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 3);
assert.equal(elm.children[1].innerHTML, '2');
assert.equal(elm.children[2].innerHTML, '3');
@ -159,7 +159,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 2);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']);
});
it('add elements in the middle', function() {
@ -167,7 +167,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 4);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']);
});
it('add elements at begin and end', function() {
@ -175,7 +175,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 3, 4, 5].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 3);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['1', '2', '3', '4', '5']);
});
it('adds children to parent with no children', function() {
@ -183,7 +183,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', {key: 'span'}, [1, 2, 3].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 0);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['1', '2', '3']);
});
it('removes all children from parent', function() {
@ -191,7 +191,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', {key: 'span'});
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['1', '2', '3']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 0);
});
});
@ -201,7 +201,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [3, 4, 5].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 5);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['3', '4', '5']);
});
it('removes elements from the end', function() {
@ -209,7 +209,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 3].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 5);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 3);
assert.equal(elm.children[0].innerHTML, '1');
assert.equal(elm.children[1].innerHTML, '2');
@ -220,7 +220,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 2, 4, 5].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 5);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 4);
assert.deepEqual(elm.children[0].innerHTML, '1');
assert.equal(elm.children[0].innerHTML, '1');
@ -235,7 +235,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [2, 3, 1, 4].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 4);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 4);
assert.equal(elm.children[0].innerHTML, '2');
assert.equal(elm.children[1].innerHTML, '3');
@ -247,7 +247,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [2, 3, 1].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 3);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 3);
assert.equal(elm.children[0].innerHTML, '2');
assert.equal(elm.children[1].innerHTML, '3');
@ -258,7 +258,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [1, 4, 2, 3].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 4);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 4);
assert.equal(elm.children[0].innerHTML, '1');
assert.equal(elm.children[1].innerHTML, '4');
@ -270,7 +270,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [4, 2, 3, 1].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 4);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 4);
assert.equal(elm.children[0].innerHTML, '4');
assert.equal(elm.children[1].innerHTML, '2');
@ -284,7 +284,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [4, 1, 2, 3, 6].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 5);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 5);
assert.equal(elm.children[0].innerHTML, '4');
assert.equal(elm.children[1].innerHTML, '1');
@ -297,7 +297,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [4, 6].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 3);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['4', '6']);
});
it('handles moved and set to undefined element ending at the end', function() {
@ -305,7 +305,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [4, 5, 3].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 3);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.equal(elm.children.length, 3);
assert.equal(elm.children[0].innerHTML, '4');
assert.equal(elm.children[1].innerHTML, '5');
@ -317,7 +317,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [8, 7, 6, 5, 4, 3, 2, 1].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 8);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['8', '7', '6', '5', '4', '3', '2', '1']);
});
it('something', function() {
@ -325,7 +325,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', [4, 3, 2, 1, 5, 0].map(spanNum));
var elm = createElm(vnode1);
assert.equal(elm.children.length, 6);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['4', '3', '2', '1', '5', '0']);
});
it('handles random shuffles', function() {
@ -347,7 +347,7 @@ describe('snabbdom', function() {
var vnode2 = h('span', arr.map(function(n) {
return spanNumWithOpacity(shufArr[n], opacities[n]);
}));
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
for (i = 0; i < elms; ++i) {
assert.equal(elm.children[i].innerHTML, shufArr[i].toString());
assert.equal(opacities[i].indexOf(elm.children[i].style.opacity), 0);
@ -361,7 +361,7 @@ describe('snabbdom', function() {
var vnode2 = h('div', [h('span', 'Hello'), h('span', 'World')]);
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['Hello']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['Hello', 'World']);
});
it('prepends element', function() {
@ -369,7 +369,7 @@ describe('snabbdom', function() {
var vnode2 = h('div', [h('span', 'Hello'), h('span', 'World')]);
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['World']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['Hello', 'World']);
});
it('prepends element of different tag type', function() {
@ -377,7 +377,7 @@ describe('snabbdom', function() {
var vnode2 = h('div', [h('div', 'Hello'), h('span', 'World')]);
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['World']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(prop('tagName'), elm.children), ['DIV', 'SPAN']);
assert.deepEqual(map(inner, elm.children), ['Hello', 'World']);
});
@ -386,7 +386,7 @@ describe('snabbdom', function() {
var vnode2 = h('div', [h('span', 'One'), h('span', 'Three')]);
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['One', 'Two', 'Three']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(inner, elm.children), ['One', 'Three']);
});
it('reorders elements', function() {
@ -394,10 +394,89 @@ describe('snabbdom', function() {
var vnode2 = h('div', [h('b', 'Three'), h('span', 'One'), h('div', 'Two')]);
var elm = createElm(vnode1);
assert.deepEqual(map(inner, elm.children), ['One', 'Two', 'Three']);
patchElm(vnode1, vnode2);
patch(vnode1, vnode2);
assert.deepEqual(map(prop('tagName'), elm.children), ['B', 'SPAN', 'DIV']);
assert.deepEqual(map(inner, elm.children), ['Three', 'One', 'Two']);
});
});
describe('event handling', function() {
it('attaches click event handler to children', 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 elm = createElm(vnode);
var a = elm.children[0];
var span = elm.children[1];
a.click();
span.click();
a.click();
assert.equal(2, result.length);
});
it('attaches click event handler self', function() {
var result = [];
function clicked(ev) { result.push(ev); }
var vnode = h('div', {on: {'click': clicked}}, [
h('a', 'Click my parent'),
]);
var elm = createElm(vnode);
var a = elm.children[0];
elm.click();
assert.equal(1, result.length);
});
it('attaches click event handler to subchilden', 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 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);
});
it('removes event handler from children', 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 vnode2 = h('div', {on: {'click a.yes.me': clicked}}, [
h('a.yes', 'Click me'),
h('span', 'Not me'),
]);
var elm = createElm(vnode1);
var a = elm.children[0];
a.click();
patch(vnode1, vnode2);
a.click();
assert.equal(1, result.length);
});
});
});
});

Loading…
Cancel
Save