event delegation support

This commit is contained in:
lavrton 2015-11-22 10:44:33 +07:00
parent 65bea76644
commit d1d30c6c1a
7 changed files with 217 additions and 29 deletions

View File

@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- correct `Konva.Arrow` drawing. Now it works better.
- Better support for dragging when mouse out of stage
### Added
- event delegation. You can use it in this way: `layer.on('click', 'Circle', handler);`
## [0.10.0][2015-10-27]
### Added

View File

@ -3,7 +3,7 @@
* Konva JavaScript Framework v0.11.0
* http://konvajs.github.io/
* Licensed under the MIT or GPL Version 2 licenses.
* Date: Sat Nov 21 2015
* Date: Sun Nov 22 2015
*
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
* Modified work Copyright (C) 2014 - 2015 by Anton Lavrenov (Konva)
@ -800,6 +800,13 @@ var Konva = {};
}
return names.length > 0;
},
isValidSelector: function(selector) {
if (typeof selector !== 'string') {
return false;
}
var firstChar = selector[0];
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
},
createCanvasElement: function() {
var canvas = Konva.document.createElement('canvas');
// on some environments canvas.style is readonly
@ -2658,8 +2665,17 @@ var Konva = {};
* var oldVal = evt.oldVal;
* var newVal = evt.newVal;
* });
*
* // also event delegation works
* layer.on('click', 'Group', function(evt) {
* var shape = evt.target;
* var group = evtn.currentTarger;
* });
*/
on: function(evtStr, handler) {
if (arguments.length === 3) {
return this._delegate.apply(this, arguments);
}
var events = evtStr.split(SPACE),
len = events.length,
n, event, parts, baseEvent, name;
@ -2757,6 +2773,17 @@ var Konva = {};
removeEventListener: function(type) {
this.off(type);
},
_delegate: function(event, selector, handler) {
var stopNode = this;
this.on(event, function(evt) {
var targets = evt.target._findMatchers(selector, stopNode);
for(var i = 0; i < targets.length; i++) {
evt = Konva.Util.cloneObject(evt);
evt.currentTarget = targets[i];
handler.call(targets[i], evt);
}
});
},
/**
* remove self from parent, but don't destroy
* @method
@ -3408,6 +3435,49 @@ var Konva = {};
getParent: function() {
return this.parent;
},
_findMatchers: function(selector, stopNode) {
var res = [];
if (this._isMatch(selector)) {
res.push(this);
}
var parent = this.parent;
if (!parent) {
return res;
}
if (parent === stopNode) {
return res;
}
return res.concat(parent._findMatchers(selector, stopNode));
},
_isMatch: function(selector) {
var selectorArr = selector.replace(/ /g, '').split(','),
len = selectorArr.length,
n, sel;
for (n = 0; n < len; n++) {
sel = selectorArr[n];
if (!Konva.Util.isValidSelector(sel)) {
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
Konva.Util.warn('Konva is awesome, right?');
}
// id selector
if(sel.charAt(0) === '#') {
if (this.id() === sel.slice(1)) {
return true;
}
}
// name selector
else if(sel.charAt(0) === '.') {
if (this.hasName(sel.slice(1))) {
return true;
}
} else if (this._get(sel).length !== 0) {
return true;
}
}
return false;
},
/**
* get layer ancestor
* @method
@ -3462,12 +3532,14 @@ var Konva = {};
*/
fire: function(eventType, evt, bubble) {
// bubble
evt = Konva.Util.cloneObject(evt || {});
evt.currentTarget = this;
if (bubble) {
this._fireAndBubble(eventType, evt || {});
this._fireAndBubble(eventType, evt);
}
// no bubble
else {
this._fire(eventType, evt || {});
this._fire(eventType, evt);
}
return this;
},
@ -6377,14 +6449,6 @@ var Konva = {};
(function() {
'use strict';
function isValidSelector(selector) {
if (typeof selector !== 'string') {
return false;
}
var firstChar = selector[0];
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
}
/**
* Container constructor.&nbsp; Containers are used to contain nodes or other containers
* @constructor
@ -6583,7 +6647,7 @@ var Konva = {};
for (n = 0; n < len; n++) {
sel = selectorArr[n];
if (!isValidSelector(sel)) {
if (!Konva.Util.isValidSelector(sel)) {
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
Konva.Util.warn('Konva is awesome, right?');

12
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,5 @@
(function() {
'use strict';
function isValidSelector(selector) {
if (typeof selector !== 'string') {
return false;
}
var firstChar = selector[0];
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
}
/**
* Container constructor.&nbsp; Containers are used to contain nodes or other containers
* @constructor
@ -182,7 +174,7 @@
for (n = 0; n < len; n++) {
sel = selectorArr[n];
if (!isValidSelector(sel)) {
if (!Konva.Util.isValidSelector(sel)) {
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
Konva.Util.warn('Konva is awesome, right?');

View File

@ -417,8 +417,17 @@
* var oldVal = evt.oldVal;
* var newVal = evt.newVal;
* });
*
* // also event delegation works
* layer.on('click', 'Group', function(evt) {
* var shape = evt.target;
* var group = evtn.currentTarger;
* });
*/
on: function(evtStr, handler) {
if (arguments.length === 3) {
return this._delegate.apply(this, arguments);
}
var events = evtStr.split(SPACE),
len = events.length,
n, event, parts, baseEvent, name;
@ -516,6 +525,17 @@
removeEventListener: function(type) {
this.off(type);
},
_delegate: function(event, selector, handler) {
var stopNode = this;
this.on(event, function(evt) {
var targets = evt.target._findMatchers(selector, stopNode);
for(var i = 0; i < targets.length; i++) {
evt = Konva.Util.cloneObject(evt);
evt.currentTarget = targets[i];
handler.call(targets[i], evt);
}
});
},
/**
* remove self from parent, but don't destroy
* @method
@ -1167,6 +1187,49 @@
getParent: function() {
return this.parent;
},
_findMatchers: function(selector, stopNode) {
var res = [];
if (this._isMatch(selector)) {
res.push(this);
}
var parent = this.parent;
if (!parent) {
return res;
}
if (parent === stopNode) {
return res;
}
return res.concat(parent._findMatchers(selector, stopNode));
},
_isMatch: function(selector) {
var selectorArr = selector.replace(/ /g, '').split(','),
len = selectorArr.length,
n, sel;
for (n = 0; n < len; n++) {
sel = selectorArr[n];
if (!Konva.Util.isValidSelector(sel)) {
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
Konva.Util.warn('Konva is awesome, right?');
}
// id selector
if(sel.charAt(0) === '#') {
if (this.id() === sel.slice(1)) {
return true;
}
}
// name selector
else if(sel.charAt(0) === '.') {
if (this.hasName(sel.slice(1))) {
return true;
}
} else if (this._get(sel).length !== 0) {
return true;
}
}
return false;
},
/**
* get layer ancestor
* @method
@ -1221,12 +1284,14 @@
*/
fire: function(eventType, evt, bubble) {
// bubble
evt = Konva.Util.cloneObject(evt || {});
evt.currentTarget = this;
if (bubble) {
this._fireAndBubble(eventType, evt || {});
this._fireAndBubble(eventType, evt);
}
// no bubble
else {
this._fire(eventType, evt || {});
this._fire(eventType, evt);
}
return this;
},

View File

@ -539,6 +539,13 @@
}
return names.length > 0;
},
isValidSelector: function(selector) {
if (typeof selector !== 'string') {
return false;
}
var firstChar = selector[0];
return firstChar === '#' || firstChar === '.' || firstChar === firstChar.toUpperCase();
},
createCanvasElement: function() {
var canvas = Konva.document.createElement('canvas');
// on some environments canvas.style is readonly

View File

@ -1846,6 +1846,63 @@ suite('Node', function() {
assert.equal(clicks[1], 'layer');
});
test('simple event delegation', function() {
var stage = addStage();
var layer = new Konva.Layer();
var circle = new Konva.Circle({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
name: 'myCircle'
});
stage.add(layer);
layer.add(circle);
layer.draw();
var fired = false;
layer.on('click', 'Circle', function(e) {
assert.equal(this, circle);
assert.equal(e.currentTarget, circle);
fired = true;
});
circle.fire('click', null, true);
assert(fired, true);
});
test('complex event delegation', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var circle = new Konva.Circle({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
name: 'myCircle'
});
var group = new Konva.Group({
name: 'group1 group2'
});
group.add(circle);
layer.add(group);
layer.draw();
var fired = false;
layer.on('click', '.group1', function(e) {
assert.equal(this, group);
assert.equal(e.currentTarget, group);
fired = true;
});
circle.fire('click', null, true);
assert(fired, true);
});
// ======================================================
test('move shape, group, and layer, and then get absolute position', function() {
var stage = addStage();