support all events
This commit is contained in:
parent
88e5724abd
commit
b17bd250b7
6 changed files with 135 additions and 26 deletions
|
|
@ -26,7 +26,7 @@ Mithril's `config` method is now replaced by several lifecycle methods to improv
|
||||||
|
|
||||||
## Robustness
|
## Robustness
|
||||||
|
|
||||||
There are over 1800 assertions in the test suite, and tests cover even difficult-to-test things like `location.href`, `element.innerHTML` and `XMLHttpRequest` usage.
|
There are over 2100 assertions in the test suite, and tests cover even difficult-to-test things like `location.href`, `element.innerHTML` and `XMLHttpRequest` usage.
|
||||||
|
|
||||||
## Modularity
|
## Modularity
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
function Node(tag, key, attrs, children, text, dom) {
|
function Node(tag, key, attrs, children, text, dom) {
|
||||||
return {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, domSize: undefined, state: {}}
|
return {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, domSize: undefined, state: {}, events: undefined}
|
||||||
}
|
}
|
||||||
Node.normalize = function(node) {
|
Node.normalize = function(node) {
|
||||||
if (node instanceof Array) return Node("[", undefined, undefined, Node.normalizeChildren(node), undefined, undefined)
|
if (node instanceof Array) return Node("[", undefined, undefined, Node.normalizeChildren(node), undefined, undefined)
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ module.exports = function($window, onevent) {
|
||||||
var oldTag = old.tag, tag = vnode.tag
|
var oldTag = old.tag, tag = vnode.tag
|
||||||
if (oldTag === tag) {
|
if (oldTag === tag) {
|
||||||
vnode.state = old.state
|
vnode.state = old.state
|
||||||
|
vnode.events = old.events
|
||||||
if (shouldUpdate(vnode, old)) return
|
if (shouldUpdate(vnode, old)) return
|
||||||
if (vnode.attrs != null) {
|
if (vnode.attrs != null) {
|
||||||
updateLifecycle(vnode.attrs, vnode, hooks, recycling)
|
updateLifecycle(vnode.attrs, vnode, hooks, recycling)
|
||||||
|
|
@ -345,11 +346,17 @@ module.exports = function($window, onevent) {
|
||||||
element.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(nsLastIndex + 1), value)
|
element.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(nsLastIndex + 1), value)
|
||||||
}
|
}
|
||||||
else if (key[0] === "o" && key[1] === "n" && typeof value === "function") {
|
else if (key[0] === "o" && key[1] === "n" && typeof value === "function") {
|
||||||
element[key] = function(e) {
|
var eventName = key.slice(2)
|
||||||
|
if (vnode.events === undefined) vnode.events = {}
|
||||||
|
if (vnode.events[key] != null) {
|
||||||
|
element.removeEventListener(eventName, vnode.events[key], false)
|
||||||
|
}
|
||||||
|
vnode.events[key] = function(e) {
|
||||||
var result = value.call(element, e)
|
var result = value.call(element, e)
|
||||||
if (typeof onevent === "function") onevent.call(element, e)
|
if (typeof onevent === "function") onevent.call(element, e)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
element.addEventListener(eventName, vnode.events[key], false)
|
||||||
}
|
}
|
||||||
else if (key === "style") updateStyle(element, old, value)
|
else if (key === "style") updateStyle(element, old, value)
|
||||||
else if (key in element && !isAttribute(key) && vnode.ns === undefined) element[key] = value
|
else if (key in element && !isAttribute(key) && vnode.ns === undefined) element[key] = value
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,46 @@ o.spec("event", function() {
|
||||||
o(onevent.args[0].type).equals("click")
|
o(onevent.args[0].type).equals("click")
|
||||||
o(onevent.args[0].target).equals(div.dom)
|
o(onevent.args[0].target).equals(div.dom)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o("fires onclick only once after redraw", function() {
|
||||||
|
var spy = o.spy()
|
||||||
|
var div = {tag: "div", attrs: {id: "a", onclick: spy}}
|
||||||
|
var updated = {tag: "div", attrs: {id: "b", onclick: spy}}
|
||||||
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
|
render(root, [div])
|
||||||
|
render(root, [updated])
|
||||||
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
o(spy.this).equals(div.dom)
|
||||||
|
o(spy.args[0].type).equals("click")
|
||||||
|
o(spy.args[0].target).equals(div.dom)
|
||||||
|
o(onevent.callCount).equals(1)
|
||||||
|
o(onevent.this).equals(div.dom)
|
||||||
|
o(onevent.args[0].type).equals("click")
|
||||||
|
o(onevent.args[0].target).equals(div.dom)
|
||||||
|
o(div.dom).equals(updated.dom)
|
||||||
|
o(div.dom.attributes["id"].nodeValue).equals("b")
|
||||||
|
})
|
||||||
|
|
||||||
|
o("handles ontransitionend", function() {
|
||||||
|
var spy = o.spy()
|
||||||
|
var div = {tag: "div", attrs: {ontransitionend: spy}}
|
||||||
|
var e = $window.document.createEvent("AnimationEvent")
|
||||||
|
e.initEvent("transitionend", true, true)
|
||||||
|
|
||||||
|
render(root, [div])
|
||||||
|
div.dom.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
o(spy.this).equals(div.dom)
|
||||||
|
o(spy.args[0].type).equals("transitionend")
|
||||||
|
o(spy.args[0].target).equals(div.dom)
|
||||||
|
o(onevent.callCount).equals(1)
|
||||||
|
o(onevent.this).equals(div.dom)
|
||||||
|
o(onevent.args[0].type).equals("transitionend")
|
||||||
|
o(onevent.args[0].target).equals(div.dom)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
|
function isModernEvent(type) {
|
||||||
|
return type === "transitionstart" || type === "transitionend" || type === "animationstart" || type === "animationend"
|
||||||
|
}
|
||||||
function appendChild(child) {
|
function appendChild(child) {
|
||||||
var ancestor = this
|
var ancestor = this
|
||||||
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
||||||
|
|
@ -77,6 +80,7 @@ module.exports = function() {
|
||||||
document: {
|
document: {
|
||||||
createElement: function(tag, is) {
|
createElement: function(tag, is) {
|
||||||
var style = {}
|
var style = {}
|
||||||
|
var events = {}
|
||||||
var element = {
|
var element = {
|
||||||
nodeType: 1,
|
nodeType: 1,
|
||||||
nodeName: tag.toUpperCase(),
|
nodeName: tag.toUpperCase(),
|
||||||
|
|
@ -148,9 +152,24 @@ module.exports = function() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
focus: function() {activeElement = this},
|
focus: function() {activeElement = this},
|
||||||
|
addEventListener: function(type, callback, useCapture) {
|
||||||
|
if (events[type] == null) events[type] = [callback]
|
||||||
|
else events[type].push(callback)
|
||||||
|
},
|
||||||
|
removeEventListener: function(type, callback, useCapture) {
|
||||||
|
if (events[type] != null) {
|
||||||
|
var index = events[type].indexOf(callback)
|
||||||
|
if (index > -1) events[type].splice(index, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
dispatchEvent: function(e) {
|
dispatchEvent: function(e) {
|
||||||
e.target = this
|
e.target = this
|
||||||
if (typeof this["on" + e.type] === "function") this["on" + e.type](e)
|
if (events[e.type] != null) {
|
||||||
|
for (var i = 0; i < events[e.type].length; i++) {
|
||||||
|
events[e.type][i].call(this, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof this["on" + e.type] === "function" && !isModernEvent(e.type)) this["on" + e.type](e)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (element.nodeName === "A") {
|
if (element.nodeName === "A") {
|
||||||
|
|
|
||||||
|
|
@ -484,31 +484,72 @@ o.spec("domMock", function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("events", function() {
|
o.spec("events", function() {
|
||||||
var spy, div, e
|
o.spec("click", function() {
|
||||||
o.beforeEach(function() {
|
var spy, div, e
|
||||||
spy = o.spy()
|
o.beforeEach(function() {
|
||||||
div = $document.createElement("div")
|
spy = o.spy()
|
||||||
e = $document.createEvent("MouseEvents")
|
div = $document.createElement("div")
|
||||||
e.initEvent("click", true, true)
|
e = $document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
$document.body.appendChild(div)
|
$document.body.appendChild(div)
|
||||||
})
|
})
|
||||||
o.afterEach(function() {
|
o.afterEach(function() {
|
||||||
$document.body.removeChild(div)
|
$document.body.removeChild(div)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("click fires onclick", function() {
|
o("addEventListener works", function() {
|
||||||
div.onclick = spy
|
div.addEventListener("click", spy, false)
|
||||||
div.dispatchEvent(e)
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
o(spy.callCount).equals(1)
|
||||||
o(spy.this).equals(div)
|
o(spy.this).equals(div)
|
||||||
o(spy.args[0].type).equals("click")
|
o(spy.args[0].type).equals("click")
|
||||||
o(spy.args[0].target).equals(div)
|
o(spy.args[0].target).equals(div)
|
||||||
|
})
|
||||||
|
o("removeEventListener works", function(done) {
|
||||||
|
div.addEventListener("click", spy, false)
|
||||||
|
div.removeEventListener("click", spy, false)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
o("click fires onclick", function() {
|
||||||
|
div.onclick = spy
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
o(spy.this).equals(div)
|
||||||
|
o(spy.args[0].type).equals("click")
|
||||||
|
o(spy.args[0].target).equals(div)
|
||||||
|
})
|
||||||
|
o("click without onclick doesn't throw", function(done) {
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
o("click without onclick doesn't throw", function(done) {
|
o.spec("transitionend", function() {
|
||||||
div.dispatchEvent(e)
|
var spy, div, e
|
||||||
done()
|
o.beforeEach(function() {
|
||||||
|
spy = o.spy()
|
||||||
|
div = $document.createElement("div")
|
||||||
|
e = $document.createEvent("AnimationEvent")
|
||||||
|
e.initEvent("transitionend", true, true)
|
||||||
|
|
||||||
|
$document.body.appendChild(div)
|
||||||
|
})
|
||||||
|
o.afterEach(function() {
|
||||||
|
$document.body.removeChild(div)
|
||||||
|
})
|
||||||
|
|
||||||
|
o("ontransitionend does not fire", function(done) {
|
||||||
|
div.ontransitionend = spy
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("attributes", function() {
|
o.spec("attributes", function() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue