Skip to content

Commit

Permalink
Add event propagation control. Bump version.
Browse files Browse the repository at this point in the history
  • Loading branch information
hperrin committed Feb 27, 2018
1 parent e6fb1ac commit 2eae3a9
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 43 deletions.
54 changes: 39 additions & 15 deletions NonBlock.es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -76,15 +80,19 @@
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) {
if (isNonBlocking(ev.target)) {
remCursor(ev.target);
nonBlockLastElem = null;
isSelectingText = false;
ev.stopPropagation();
if (isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}
}, true);
document.body.addEventListener('mousemove', function (ev) {
Expand All @@ -96,15 +104,19 @@
window.getSelection().removeAllRanges();
isSelectingText = true;
}
ev.stopPropagation();
if (isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}
}, true);
document.body.addEventListener('mousedown', function (ev) {
if (isNonBlocking(ev.target)) {
ev.preventDefault();
nonblockPass(ev.target, ev, 'onmousedown');
isSelectingText = null;
ev.stopPropagation();
if (isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}
}, true);
document.body.addEventListener('mouseup', function (ev) {
Expand All @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
62 changes: 48 additions & 14 deletions NonBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -101,14 +105,28 @@
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) => {
if (isNonBlocking(ev.target)) {
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);
Expand All @@ -121,15 +139,19 @@
window.getSelection().removeAllRanges();
isSelectingText = true;
}
ev.stopPropagation();
if (isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}
}, true);
document.body.addEventListener('mousedown', (ev) => {
if (isNonBlocking(ev.target)) {
ev.preventDefault();
nonblockPass(ev.target, ev, 'onmousedown');
isSelectingText = null;
ev.stopPropagation();
if (isNotPropagating(ev.target)) {
ev.stopPropagation();
}
}
}, true);
document.body.addEventListener('mouseup', (ev) => {
Expand All @@ -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) {
Expand All @@ -170,7 +198,7 @@
eventObject = document.createEvent("MouseEvents");
eventObject.initMouseEvent(
event,
origEvent.bubbles,
bubbles === undefined ? origEvent.bubbles : bubbles,
origEvent.cancelable,
origEvent.view,
origEvent.detail,
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
75 changes: 63 additions & 12 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -51,16 +51,20 @@ <h1>NonBlock.js</h1>
user hovers over them, and let the user select and interact with elements
under them. Hover over the purple boxes to try it out.
</p>
<div class="linkcontainer first">
This is a <a href="http://google.com" target="_blank">link</a>. 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.
</div>
<div class="nonblock intheway first">
There&apos;s a link under me. I am going to stay in the way of that link.
<hr />
<div style="position: relative;">
<div class="linkcontainer first">
This is a <a href="http://google.com" target="_blank">link</a>. 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.
</div>
<div class="nonblock intheway first">
There&apos;s a link under me. I am going to stay in the way of that link.
</div>
</div>
<hr />
<div>
Try toggling nonblocking on this demo:
<button onclick="document.querySelector('.intheway.second').classList.add('nonblock')">Enable NonBlock</button>
Expand All @@ -73,5 +77,52 @@ <h1>NonBlock.js</h1>
button.
</div>
</div>
<hr />
<div>
These demos show how event propagation works. The green box is listening for
events and the purple box is a child.
</div>
<h4>
class="nonblock"
</h4>
<div class="linkcontainer third">
This is a <button type="button" onclick="alert('You clicked the button.')">button</button>.
<div class="nonblock intheway third">
There&apos;s a button under me. I am going to stay in the way of that
button.
</div>
</div>
<div style="display: inline-block; font-family: monospace; width: auto;">
Event log:
<div id="event-third" style="max-height: 250px; overflow: auto;"></div>
</div>
<h4>
class="nonblock nonblock-stoppropagation"
</h4>
<div class="linkcontainer fourth">
This is a <button type="button" onclick="alert('You clicked the button.')">button</button>.
<div class="nonblock nonblock-stoppropagation intheway fourth">
There&apos;s a button under me. I am going to stay in the way of that
button.
</div>
</div>
<div style="display: inline-block; font-family: monospace; width: auto;">
Event log:
<div id="event-fourth" style="max-height: 250px; overflow: auto;"></div>
</div>
<script type="text/javascript">
function listenerDemo(demo, eventType) {
document.querySelector('.linkcontainer.'+demo).addEventListener(eventType, function(event){
var output = document.getElementById('event-'+demo);
output.innerHTML += '<br />'+eventType+'<br />Target: '+event.target.tagName+' class="'+event.target.className+'"<br />';
output.scrollTop = output.scrollHeight;
});
}
var events = ['mouseenter', 'mouseover', 'mouseleave', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'click', 'dblclick'];
for (var i in events) {
listenerDemo('third', events[i]);
listenerDemo('fourth', events[i]);
}
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit 2eae3a9

Please sign in to comment.