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.

- -
- There's a link under me. I am going to stay in the way of that link. +
+
+ +
+ There's a link under me. I am going to stay in the way of that link. +
+
Try toggling nonblocking on this demo: @@ -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" +

+ +
+ Event log: +
+
+

+ class="nonblock nonblock-stoppropagation" +

+ +
+ Event log: +
+
+ diff --git a/package-lock.json b/package-lock.json index fd90095..18e379d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nonblockjs", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e535c74..0faaa2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nonblockjs", - "version": "1.0.3", + "version": "1.0.4", "description": "Unobtrusive (click through) UI elements in JavaScript.", "keywords": [ "non blocking",