From 2eae3a9ae1ea26e8fffebea725b5d7d143144598 Mon Sep 17 00:00:00 2001
From: Hunter Perrin
Date: Tue, 27 Feb 2018 13:35:56 -0800
Subject: [PATCH] Add event propagation control. Bump version.
---
NonBlock.es5.js | 54 ++++++++++++++++++++++++----------
NonBlock.js | 62 ++++++++++++++++++++++++++++++---------
README.md | 2 ++
index.html | 75 +++++++++++++++++++++++++++++++++++++++--------
package-lock.json | 2 +-
package.json | 2 +-
6 files changed, 154 insertions(+), 43 deletions(-)
diff --git a/NonBlock.es5.js b/NonBlock.es5.js
index 8caa164..ddf2234 100644
--- a/NonBlock.es5.js
+++ b/NonBlock.es5.js
@@ -55,6 +55,10 @@
return el.classList.contains('nonblock');
}
+ function isNotPropagating(el) {
+ return el.classList.contains('nonblock-stoppropagation');
+ }
+
function getCursor(el) {
var style = window.getComputedStyle(el);
return style.getPropertyValue('cursor');
@@ -76,7 +80,9 @@
document.body.addEventListener('mouseenter', function (ev) {
if (isNonBlocking(ev.target)) {
nonBlockLastElem = ev.target;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mouseleave', function (ev) {
@@ -84,7 +90,9 @@
remCursor(ev.target);
nonBlockLastElem = null;
isSelectingText = false;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mousemove', function (ev) {
@@ -96,7 +104,9 @@
window.getSelection().removeAllRanges();
isSelectingText = true;
}
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mousedown', function (ev) {
@@ -104,7 +114,9 @@
ev.preventDefault();
nonblockPass(ev.target, ev, 'onmousedown');
isSelectingText = null;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mouseup', function (ev) {
@@ -115,24 +127,30 @@
window.getSelection().removeAllRanges();
}
isSelectingText = false;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('click', function (ev) {
if (isNonBlocking(ev.target)) {
nonblockPass(ev.target, ev, 'onclick');
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('dblclick', function (ev) {
if (isNonBlocking(ev.target)) {
nonblockPass(ev.target, ev, 'ondblclick');
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
// Fire a DOM event.
- var domEvent = function domEvent(elem, event, origEvent) {
+ var domEvent = function domEvent(elem, event, origEvent, bubbles) {
var eventObject = void 0;
event = event.toLowerCase();
if (document.createEvent && elem.dispatchEvent) {
@@ -143,13 +161,13 @@
// probably a much better way to do it.
elem.getBoundingClientRect();
eventObject = document.createEvent("MouseEvents");
- eventObject.initMouseEvent(event, origEvent.bubbles, origEvent.cancelable, origEvent.view, origEvent.detail, origEvent.screenX, origEvent.screenY, origEvent.clientX, origEvent.clientY, origEvent.ctrlKey, origEvent.altKey, origEvent.shiftKey, origEvent.metaKey, origEvent.button, origEvent.relatedTarget);
+ eventObject.initMouseEvent(event, bubbles === undefined ? origEvent.bubbles : bubbles, origEvent.cancelable, origEvent.view, origEvent.detail, origEvent.screenX, origEvent.screenY, origEvent.clientX, origEvent.clientY, origEvent.ctrlKey, origEvent.altKey, origEvent.shiftKey, origEvent.metaKey, origEvent.button, origEvent.relatedTarget);
} else if (event.match(regexUiEvents)) {
eventObject = document.createEvent("UIEvents");
- eventObject.initUIEvent(event, origEvent.bubbles, origEvent.cancelable, origEvent.view, origEvent.detail);
+ eventObject.initUIEvent(event, bubbles === undefined ? origEvent.bubbles : bubbles, origEvent.cancelable, origEvent.view, origEvent.detail);
} else if (event.match(regexHtmlEvents)) {
eventObject = document.createEvent("HTMLEvents");
- eventObject.initEvent(event, origEvent.bubbles, origEvent.cancelable);
+ eventObject.initEvent(event, bubbles === undefined ? origEvent.bubbles : bubbles, origEvent.cancelable);
}
if (!eventObject) {
return;
@@ -223,11 +241,17 @@
if (!nonBlockLastElem || nonBlockLastElem !== elBelow) {
if (nonBlockLastElem) {
var lastElem = nonBlockLastElem;
- domEvent(lastElem, 'mouseleave', event);
- domEvent(lastElem, 'mouseout', event);
+ if (!lastElem.contains(elBelow)) {
+ domEvent(lastElem, 'mouseleave', event, false);
+ }
+ domEvent(lastElem, 'mouseout', event, true);
+ if (!elBelow.contains(lastElem)) {
+ domEvent(elBelow, 'mouseenter', event, false);
+ }
+ } else if (!elBelow.contains(elem)) {
+ domEvent(elBelow, 'mouseenter', event, false);
}
- domEvent(elBelow, 'mouseenter', event);
- domEvent(elBelow, 'mouseover', event);
+ domEvent(elBelow, 'mouseover', event, true);
}
domEvent(elBelow, eventName, event);
// Remember the latest element the mouse was over.
diff --git a/NonBlock.js b/NonBlock.js
index d9ce6ca..0e83e0e 100644
--- a/NonBlock.js
+++ b/NonBlock.js
@@ -80,6 +80,10 @@
return el.classList.contains('nonblock');
}
+ function isNotPropagating(el) {
+ return el.classList.contains('nonblock-stoppropagation');
+ }
+
function getCursor(el) {
const style = window.getComputedStyle(el);
return style.getPropertyValue('cursor');
@@ -101,7 +105,9 @@
document.body.addEventListener('mouseenter', (ev) => {
if (isNonBlocking(ev.target)) {
nonBlockLastElem = ev.target;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mouseleave', (ev) => {
@@ -109,6 +115,18 @@
remCursor(ev.target);
nonBlockLastElem = null;
isSelectingText = false;
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
+ }
+ }, true);
+ document.body.addEventListener('mouseover', (ev) => {
+ if (isNonBlocking(ev.target) && isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
+ }, true);
+ document.body.addEventListener('mouseout', (ev) => {
+ if (isNonBlocking(ev.target) && isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}, true);
@@ -121,7 +139,9 @@
window.getSelection().removeAllRanges();
isSelectingText = true;
}
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mousedown', (ev) => {
@@ -129,7 +149,9 @@
ev.preventDefault();
nonblockPass(ev.target, ev, 'onmousedown');
isSelectingText = null;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('mouseup', (ev) => {
@@ -140,24 +162,30 @@
window.getSelection().removeAllRanges();
}
isSelectingText = false;
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('click', (ev) => {
if (isNonBlocking(ev.target)) {
nonblockPass(ev.target, ev, 'onclick');
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
document.body.addEventListener('dblclick', (ev) => {
if (isNonBlocking(ev.target)) {
nonblockPass(ev.target, ev, 'ondblclick');
- ev.stopPropagation();
+ if (isNotPropagating(ev.target)) {
+ ev.stopPropagation();
+ }
}
}, true);
// Fire a DOM event.
- const domEvent = (elem, event, origEvent) => {
+ const domEvent = (elem, event, origEvent, bubbles) => {
let eventObject;
event = event.toLowerCase();
if (document.createEvent && elem.dispatchEvent) {
@@ -170,7 +198,7 @@
eventObject = document.createEvent("MouseEvents");
eventObject.initMouseEvent(
event,
- origEvent.bubbles,
+ bubbles === undefined ? origEvent.bubbles : bubbles,
origEvent.cancelable,
origEvent.view,
origEvent.detail,
@@ -187,10 +215,10 @@
);
} else if (event.match(regexUiEvents)) {
eventObject = document.createEvent("UIEvents");
- eventObject.initUIEvent(event, origEvent.bubbles, origEvent.cancelable, origEvent.view, origEvent.detail);
+ eventObject.initUIEvent(event, bubbles === undefined ? origEvent.bubbles : bubbles, origEvent.cancelable, origEvent.view, origEvent.detail);
} else if (event.match(regexHtmlEvents)) {
eventObject = document.createEvent("HTMLEvents");
- eventObject.initEvent(event, origEvent.bubbles, origEvent.cancelable);
+ eventObject.initEvent(event, bubbles === undefined ? origEvent.bubbles : bubbles, origEvent.cancelable);
}
if (!eventObject) {
return
@@ -259,11 +287,17 @@
if (!nonBlockLastElem || nonBlockLastElem !== elBelow) {
if (nonBlockLastElem) {
const lastElem = nonBlockLastElem;
- domEvent(lastElem, 'mouseleave', event);
- domEvent(lastElem, 'mouseout', event);
+ if (!lastElem.contains(elBelow)) {
+ domEvent(lastElem, 'mouseleave', event, false);
+ }
+ domEvent(lastElem, 'mouseout', event, true);
+ if (!elBelow.contains(lastElem)) {
+ domEvent(elBelow, 'mouseenter', event, false);
+ }
+ } else if (!elBelow.contains(elem)) {
+ domEvent(elBelow, 'mouseenter', event, false);
}
- domEvent(elBelow, 'mouseenter', event);
- domEvent(elBelow, 'mouseover', event);
+ domEvent(elBelow, 'mouseover', event, true);
}
domEvent(elBelow, eventName, event);
// Remember the latest element the mouse was over.
diff --git a/README.md b/README.md
index b256fbe..f796b0e 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ npm install --save nonblockjs
Add the class `nonblock` to any element you want to make nonblocking.
+Add the class `nonblock-stoppropagation` if you want NonBlock.js to stop event propagation for mouse events, effectively disguising it from its ancestors.
+
## Demos
https://sciactive.github.io/nonblockjs/
diff --git a/index.html b/index.html
index 738fab4..a5c7a8c 100644
--- a/index.html
+++ b/index.html
@@ -26,13 +26,13 @@
box-shadow: 0px 2px 30px 5px rgba(0,0,0,0.3);
}
.intheway.first {
- position: relative;
- top: -180px;
+ position: absolute;
+ top: 5px;
left: 30px;
height: 100px;
width: 120px;
}
- .intheway.second {
+ .intheway.second, .intheway.third, .intheway.fourth {
position: absolute;
top: 4px;
bottom: 4px;
@@ -51,16 +51,20 @@ NonBlock.js
user hovers over them, and let the user select and interact with elements
under them. Hover over the purple boxes to try it out.
-
- This is a
link . Here is some
- text. Let's have more text. All the text in the world couldn't save you now,
- Mr. Bond. Ah, but that's where you're wrong. You see, my underpants have
- been equipped with several tiny lasers that will blind you while the speaker
- up my butt blasts disco music.
-
-
- There's a link under me. I am going to stay in the way of that link.
+
+
+
+ This is a
link . Here is
+ some text. Let's have more text. All the text in the world couldn't save
+ you now, Mr. Bond. Ah, but that's where you're wrong. You see, my
+ overalls have been equipped with several tiny lasers that will blind you
+ while the speaker in my straw hat blasts disco music.
+
+
+ There's a link under me. I am going to stay in the way of that link.
+
+
Try toggling nonblocking on this demo:
Enable NonBlock
@@ -73,5 +77,52 @@
NonBlock.js
button.
+
+
+ These demos show how event propagation works. The green box is listening for
+ events and the purple box is a child.
+
+
+ class="nonblock"
+
+
+ This is a
button .
+
+ There's a button under me. I am going to stay in the way of that
+ button.
+
+
+
+
+ class="nonblock nonblock-stoppropagation"
+
+
+ This is a
button .
+
+ There's a button under me. I am going to stay in the way of that
+ button.
+
+
+
+