diff --git a/packages/projector/spec/document.nondom.spec.js b/packages/projector/spec/document.nondom.spec.js index e723330..36c1f90 100644 --- a/packages/projector/spec/document.nondom.spec.js +++ b/packages/projector/spec/document.nondom.spec.js @@ -182,4 +182,127 @@ describe('Document', () => { expect(changes[2]).toEqual([1, doc.body._id + 2, { name: 'class', value: null }]); }); }); + + describe('element eventHandlers', () => { + it('are called', () => { + let callCount = 0; + function incrementCallCount(evt) { + callCount++; + } + const el1 = doc.createElement('div'); + const el2 = doc.createElement('span'); + el1.appendChild(el2); + el2.addEventListener('click', incrementCallCount); + expect(changes).toEqual([]); + doc.body.appendChild(el1); + expect(changes).toEqual([ + [ + 0, + doc.body._id, + { + t: 1, + n: 'DIV', + p: [], + i: doc.body._id + 1, + c: [ + { + t: 1, + n: 'SPAN', + p: [{ name: 'onclick', value: true }], + i: doc.body._id + 2, + c: [] + } + ] + }, + undefined + ] + ]); + expect(changes.length).toEqual(1); + + expect(callCount).toEqual(0); + el2.dispatchEvent(new doc.Event('click')); + expect(callCount).toEqual(1); + }); + it('are not called after removed', () => { + let callCount = 0; + function incrementCallCount(evt) { + callCount++; + } + const el1 = doc.createElement('div'); + const el2 = doc.createElement('span'); + el1.appendChild(el2); + el2.addEventListener('click', incrementCallCount); + expect(changes).toEqual([]); + doc.body.appendChild(el1); + expect(changes).toEqual([ + [ + 0, + doc.body._id, + { + t: 1, + n: 'DIV', + p: [], + i: doc.body._id + 1, + c: [ + { + t: 1, + n: 'SPAN', + p: [{ name: 'onclick', value: true }], + i: doc.body._id + 2, + c: [] + } + ] + }, + undefined + ] + ]); + expect(changes.length).toEqual(1); + + expect(callCount).toEqual(0); + el2.dispatchEvent(new doc.Event('click')); + expect(callCount).toEqual(1); + el2.removeEventListener('click', incrementCallCount); + el2.dispatchEvent(new doc.Event('click')); + expect(callCount).toEqual(1); + }); + it('handle bubbled events', () => { + let callCount = 0; + function incrementCallCount(evt) { + callCount++; + } + const el1 = doc.createElement('div'); + const el2 = doc.createElement('span'); + el1.appendChild(el2); + el1.addEventListener('click', incrementCallCount); + expect(changes).toEqual([]); + doc.body.appendChild(el1); + expect(changes).toEqual([ + [ + 0, + doc.body._id, + { + t: 1, + n: 'DIV', + p: [{ name: 'onclick', value: true }], + i: doc.body._id + 1, + c: [ + { + t: 1, + n: 'SPAN', + p: [], + i: doc.body._id + 2, + c: [] + } + ] + }, + undefined + ] + ]); + expect(changes.length).toEqual(1); + + expect(callCount).toEqual(0); + el2.dispatchEvent(new doc.Event('click', { bubbles: true })); + expect(callCount).toEqual(1); + }); + }); }); diff --git a/packages/projector/src/document.js b/packages/projector/src/document.js index 2d6d4a1..7799910 100644 --- a/packages/projector/src/document.js +++ b/packages/projector/src/document.js @@ -151,10 +151,14 @@ export function CreateDocument(onChange) { } _toDataObj() { + const p = Object.keys(this.__handlers) + .map(key => ({ name: `on${key}`, value: true })) + .concat(this.attributes); + return { t: this.nodeType, n: this.nodeName, - p: this.attributes, + p, i: this._id, c: this.childNodes.map(n => n._toDataObj()) }; @@ -190,10 +194,17 @@ export function CreateDocument(onChange) { } addEventListener(type, handler) { - (this.__handlers[toLower(type)] || (this.__handlers[toLower(type)] = [])).push(handler); + const evtName = toLower(type); + (this.__handlers[evtName] || (this.__handlers[evtName] = [])).push(handler); + if (this._attached) { + onChange([1, this._id, { name: `on${evtName}`, value: true }]); + } } removeEventListener(type, handler) { - splice(this.__handlers[toLower(type)], handler, 0, true); + const evtName = toLower(type); + if (splice(this.__handlers[evtName], handler, 0, true) !== -1 && this._attached) { + onChange([1, this._id, { name: `on${evtName}`, value: null }]); + } } dispatchEvent(event) { let t = (event.currentTarget = this), @@ -219,7 +230,7 @@ export function CreateDocument(onChange) { } class Event { - constructor(type, opts) { + constructor(type, opts = {}) { this.type = type; this.bubbles = !!opts.bubbles; this.cancelable = !!opts.cancelable;