Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #171 from uxebu/related-target

Add `event.relatedTarget` for mouseover and mouseout events
  • Loading branch information...
commit cd3f355a740449d01df8f17d970fc9367583df8e 2 parents b3da285 + 1852fdc
@klipstein klipstein authored
View
1  CHANGELOG
@@ -19,6 +19,7 @@ v0.4.2
* Fix matrix attribute setter to update scale values correctly
* Add tools.parseAngle to accept angle units as strings.
http://www.w3.org/TR/css3-values/#angles
+* Add relatedTarget event properties for mouseout / mouseover.
v0.4.1 / 2012-10-26
-------------------
View
32 example/library/movies/event-mouse-over-out.js
@@ -0,0 +1,32 @@
+function animate(element, property, from) {
+ if (!element || element === stage.root) return;
+ var anim = {};
+ anim[property] = element.initialValues[property];
+ element
+ .attr(property, from)
+ .animate('.5s', anim);
+}
+
+tools.forEach(Array(4), function(_, i) {
+ var initialFill = 'hsl(' + 90 * i + ', 100%, 50%)';
+ var initialStroke = 'black'
+ var rect = new Rect((i % 2) * 101, i > 1 ? 121 : 20, 100, 100)
+ .fill(initialFill)
+ .attr({
+ strokeWidth: 2,
+ strokeColor: initialStroke
+ })
+ .on('mouseover', function(event) {
+ animate(this, 'fillColor', 'white');
+ animate(event.relatedTarget, 'fillColor', 'black');
+ })
+ .on('mouseout', function(event) {
+ animate(this, 'strokeColor', 'red');
+ animate(event.relatedTarget, 'strokeColor', 'yellow');
+ })
+ .addTo(stage);
+ rect.initialValues = {
+ fillColor: initialFill,
+ strokeColor: initialStroke
+ };
+});
View
1  example/library/movies/movie_list.js
@@ -73,6 +73,7 @@ movieList = {
'event-multi-touch.js',
'event-multi-touch2.js',
'event-mouse-button.js',
+ 'event-mouse-over-out.js',
'key-events.js'
],
'Filter': [
View
1  example/library/movies/movie_list.json
@@ -73,6 +73,7 @@
"event-multi-touch.js",
"event-multi-touch2.js",
"event-mouse-button.js",
+ "event-mouse-over-out.js",
"key-events.js"
],
"Filter": [
View
5 src/renderer/renderer_controller.js
@@ -64,10 +64,11 @@ function(tools, EventEmitter, URI) {
});
// Bind to renderer, tunnel user events through to RunnerContext:
- this.renderer.on('userevent', this, function(event, targetId) {
+ this.renderer.on('userevent', this, function(event, targetId, relatedTargetId) {
this.post('userevent', {
event: event,
- targetId: targetId
+ targetId: targetId,
+ relatedTargetId: relatedTargetId
});
});
View
3  src/renderer/svg/svg.js
@@ -829,9 +829,6 @@ define([
}
}
- // Mark the element as one with a corresponding BS DOMElement object
- element._isBSDOMElement = true;
-
for (var i in attributes) {
if (/^dom_/.test(i)) {
if (i === 'dom_innerHTML') {
View
92 src/renderer/svg/svg_event_handlers.js
@@ -8,6 +8,9 @@ define([
], function(tools) {
'use strict';
+ /** @const */
+ var ELEMENT_NODE = 1;
+
var TOUCH_SUPPORT = typeof document == 'undefined' ? false : 'createTouch' in document;
var rMultiEvent = /drag|pointerup|pointerdown|pointermove/;
var rPointerEvent = /click|pointer/;
@@ -16,6 +19,40 @@ define([
return tools.mixin({}, e);
}
+ /**
+ * Returns the bonsai id of a DOM node
+ *
+ * @param {Node} node A DOM node
+ * @return {number} The bonsai id of the dom node or -1
+ */
+ function getBonsaiIdOf(node) {
+ var id = node && node.nodeType === ELEMENT_NODE && node.getAttribute('data-bs-id');
+ return id ? +id : -1; // string '0' evaluates to true
+ }
+
+ /**
+ * Determines whether a DOM node is an SVG element
+ * @param {Node} node
+ * @return {boolean}
+ */
+ function isSvgElement(node) {
+ return 'ownerSVGElement' in node;
+ }
+
+ /**
+ * Finds the first object that is a bonsai object out of the passed node and
+ * its ancestors.
+ *
+ * @param {Node} node The node to start the search with
+ * @return {Node}
+ */
+ function findBonsaiObject(node) {
+ while (node && (node.nodeType !== ELEMENT_NODE || node.hasAttribute('data-bs-id'))) {
+ node = node.parentNode;
+ }
+ return node;
+ }
+
// These are mixed-in into the svg-renderer's prototype.
return {
@@ -28,11 +65,13 @@ define([
clientX = event.clientX,
clientY = event.clientY,
prefix = isMulti ? 'multi:' : '',
- target = this._getTarget(touchEvent),
- targetId = this._getIdOfTarget(target),
+ target = findBonsaiObject(touchEvent.target),
+ targetId = getBonsaiIdOf(target),
type = touchEvent.type,
trueTarget = document.elementFromPoint(touchEvent.pageX, touchEvent.pageY),
- trueTargetId = trueTarget ? this._getIdOfTarget(trueTarget) : 0;
+ trueTargetId = trueTarget ? getBonsaiIdOf(trueTarget) : 0;
+
+ if (targetId === -1) { return }
event.touchId = touchEvent.identifier;
event.touchIndex = touchEvent.index;
@@ -108,26 +147,20 @@ define([
var target = domEvent.target;
// only prevent default for SVG elements, not for embedded html
- if (!this.allowEventDefaults && (target.ownerSVGElement || target.nodeName === 'svg')) {
+ if (!this.allowEventDefaults && isSvgElement(target)) {
// event killing is needed to prevent native scrolling etc. within bonsai movies
domEvent.preventDefault();
}
- target = this._getTarget(domEvent);
- var targetId = this._getIdOfTarget(target),
- type = domEvent.type,
- data = this;
-
- if (target && target instanceof HTMLElement) {
- // Get DOM element for which there is a corresponding bonsai object
- // i.e. children added via e.g. innerHTML should not trigger events
- // (only their parents should)
- while (!target._isBSDOMElement) {
- target = target.parentNode;
- }
+ target = findBonsaiObject(domEvent.target);
+
+ var type = domEvent.type, data = this;
+ var targetId = getBonsaiIdOf(target);
+ if (targetId < 0) {
+ targetId = 0;
}
- targetId = targetId || 0;
+ var relatedTarget;
var event = this._getBasicEventData(domEvent),
clientX = event.clientX,
@@ -213,6 +246,15 @@ define([
// Pass focused element's value to bonsai
event.inputValue = domEvent.target.value;
break;
+
+ case 'mouseover':
+ relatedTarget = domEvent.relatedTarget || domEvent.fromElement;
+ relatedTarget = findBonsaiObject(relatedTarget);
+ break;
+ case 'mouseout':
+ relatedTarget = domEvent.relatedTarget || domEvent.toElement;
+ relatedTarget = findBonsaiObject(relatedTarget);
+ break;
}
data._lastEventPos = [clientX, clientY];
@@ -226,7 +268,7 @@ define([
domEvent.button === 1 || domEvent.button === 0;
}
- this.emit('userevent', event, targetId);
+ this.emit('userevent', event, targetId, relatedTarget && getBonsaiIdOf(relatedTarget));
if (!TOUCH_SUPPORT && rMultiEvent.test(type)) {
// If we're on a non-touch platform (e.g. regular desktop)
@@ -237,20 +279,6 @@ define([
}
},
- _getTarget: function(e) {
-
- var target = e.target;
- while (target && this._getIdOfTarget(target) == null) {
- target = target.parentNode;
- }
- return target;
- },
-
- _getIdOfTarget: function(target) {
- var id = target && target.getAttribute && target.getAttribute('data-bs-id');
- return id == null ? null : +id;
- },
-
_getBasicEventData: function(e) {
var stageOffset = this.getOffset(),
View
9 src/runner/stage.js
@@ -136,10 +136,17 @@ define([
this.assetLoader.handleEvent('error', data.id, data.loadData);
break;
case 'userevent':
- var target = data.targetId ? this.registry.displayObjects[data.targetId] : this;
+ var displayObjectsRegistry = this.registry.displayObjects;
+ var targetId = data.targetId;
+ var target = targetId ? displayObjectsRegistry[targetId] : this;
if (target) { // target might have been removed already
var event = data.event;
event.target = target;
+ var relatedTargetId = data.relatedTargetId;
+ if (relatedTargetId === 0 || relatedTargetId > 0) {
+ event.relatedTarget = displayObjectsRegistry[relatedTargetId] || this;
+ }
+
uiEvent(event).emitOn(target);
}
break;
View
38 test/stage-spec.js
@@ -105,14 +105,15 @@ define([
dParent = new DisplayObject,
dChild = new DisplayObject;
- dParent.id = 1;
- dChild.id = 2;
dChild.parent = dParent;
+ var registry = stage.registry.displayObjects = {};
+ registry[dParent.id] = dParent;
+ registry[dChild.id] = dChild;
- stage.registry.displayObjects = {
- 1: dParent,
- 2: dChild
- };
+ afterEach(function() {
+ dChild.removeAllListeners();
+ dParent.removeAllListeners();
+ });
it('Triggers correct event on child and parent [bubbles]', function() {
@@ -128,7 +129,7 @@ define([
proxy.runMessageListener({
command: 'userevent',
data: {
- targetId: 2,
+ targetId: dChild.id,
event: eventObject
}
});
@@ -152,7 +153,7 @@ define([
proxy.runMessageListener({
command: 'userevent',
data: {
- targetId: 2,
+ targetId: dChild.id,
event: eventObject
}
});
@@ -162,6 +163,27 @@ define([
});
+ it('should add the correct `relatedTarget` property to the event ' +
+ 'object if a related target id is passed', function() {
+
+ var event, eventType = 'arbitrary';
+ dChild.on(eventType, function(event_) {
+ event = event_;
+ });
+
+ stage.handleEvent({
+ command: 'userevent',
+ data: {
+ event: {type: eventType},
+ targetId: dChild.id,
+ relatedTargetId: dParent.id
+ }
+ });
+
+ expect(event.type).toBe(eventType);
+ expect(event.target).toBe(dChild);
+ expect(event.relatedTarget).toBe(dParent);
+ });
});
describe('sendMessage', function() {
Please sign in to comment.
Something went wrong with that request. Please try again.