Skip to content

Commit 6e1bfc9

Browse files
committed
Provide an ES5 build of NonBlock.js.
1 parent 7086519 commit 6e1bfc9

File tree

9 files changed

+2974
-30
lines changed

9 files changed

+2974
-30
lines changed

.babelrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"presets": [
3+
"env",
4+
"stage-3"
5+
],
6+
"plugins": ["iife-wrap"],
7+
"sourceType": "script"
8+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules/

NonBlock.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @author Hunter Perrin <hperrin@gmail.com>
77
*/
88
.nonblock { transition: opacity .3s ease; }
9-
.nonblock:hover { opacity: .2; }
9+
.nonblock:hover { opacity: .1; }
1010
.nonblock-hide { display: none !important; }
1111
.nonblock-cursor-auto { cursor: auto; }
1212
.nonblock-cursor-default { cursor: default; }

NonBlock.es5.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
;
2+
3+
(function () {
4+
/**
5+
* NonBlock.js
6+
*
7+
* Copyright (c) 2017-2018 Hunter Perrin
8+
*
9+
* @author Hunter Perrin <hperrin@gmail.com>
10+
*/
11+
'use strict';
12+
13+
function _toConsumableArray(arr) {
14+
if (Array.isArray(arr)) {
15+
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
16+
arr2[i] = arr[i];
17+
}
18+
19+
return arr2;
20+
} else {
21+
return Array.from(arr);
22+
}
23+
}
24+
25+
(function (init) {
26+
if (document.body) {
27+
init();
28+
} else {
29+
document.addEventListener('DOMContentLoaded', init);
30+
}
31+
})(function () {
32+
// This keeps track of the last element the mouse was over, so
33+
// mouseleave, mouseenter, etc can be called.
34+
var nonBlockLastElem = void 0;
35+
// These is used for selecting text under a nonblock element.
36+
var isOverTextNode = false;
37+
var isSelectingText = false;
38+
// Some useful regexes.
39+
var regexOn = /^on/,
40+
regexMouseEvents = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/,
41+
regexUiEvents = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/,
42+
regexHtmlEvents = /^(scroll|resize|(un)?load|abort|error)$/;
43+
44+
function isNonBlocking(el) {
45+
return el.classList.contains('nonblock');
46+
}
47+
48+
function getCursor(el) {
49+
var style = window.getComputedStyle(el);
50+
return style.getPropertyValue('cursor');
51+
}
52+
53+
function setCursor(el, value) {
54+
remCursor(el);
55+
el.classList.add('nonblock-cursor-' + value);
56+
}
57+
58+
function remCursor(el) {
59+
[].concat(_toConsumableArray(el.classList.values())).forEach(function (className) {
60+
if (className.indexOf('nonblock-cursor-') === 0) {
61+
el.classList.remove(className);
62+
}
63+
});
64+
}
65+
66+
document.body.addEventListener('mouseenter', function (ev) {
67+
if (isNonBlocking(ev.target)) {
68+
nonBlockLastElem = ev.target;
69+
ev.stopPropagation();
70+
}
71+
}, true);
72+
document.body.addEventListener('mouseleave', function (ev) {
73+
if (isNonBlocking(ev.target)) {
74+
remCursor(ev.target);
75+
nonBlockLastElem = null;
76+
isSelectingText = false;
77+
ev.stopPropagation();
78+
}
79+
}, true);
80+
document.body.addEventListener('mousemove', function (ev) {
81+
if (isNonBlocking(ev.target)) {
82+
nonblockPass(ev.target, ev, 'onmousemove');
83+
// If the user just clicks somewhere, we don't want to select text, so this
84+
// detects that the user moved their mouse.
85+
if (isSelectingText === null) {
86+
window.getSelection().removeAllRanges();
87+
isSelectingText = true;
88+
}
89+
ev.stopPropagation();
90+
}
91+
}, true);
92+
document.body.addEventListener('mousedown', function (ev) {
93+
if (isNonBlocking(ev.target)) {
94+
ev.preventDefault();
95+
nonblockPass(ev.target, ev, 'onmousedown');
96+
isSelectingText = null;
97+
ev.stopPropagation();
98+
}
99+
}, true);
100+
document.body.addEventListener('mouseup', function (ev) {
101+
if (isNonBlocking(ev.target)) {
102+
ev.preventDefault();
103+
nonblockPass(ev.target, ev, 'onmouseup');
104+
if (isSelectingText === null) {
105+
window.getSelection().removeAllRanges();
106+
}
107+
isSelectingText = false;
108+
ev.stopPropagation();
109+
}
110+
}, true);
111+
document.body.addEventListener('click', function (ev) {
112+
if (isNonBlocking(ev.target)) {
113+
nonblockPass(ev.target, ev, 'onclick');
114+
ev.stopPropagation();
115+
}
116+
}, true);
117+
document.body.addEventListener('dblclick', function (ev) {
118+
if (isNonBlocking(ev.target)) {
119+
nonblockPass(ev.target, ev, 'ondblclick');
120+
ev.stopPropagation();
121+
}
122+
}, true);
123+
124+
// Fire a DOM event.
125+
var domEvent = function domEvent(elem, event, origEvent) {
126+
var eventObject = void 0;
127+
event = event.toLowerCase();
128+
if (document.createEvent && elem.dispatchEvent) {
129+
// FireFox, Opera, Safari, Chrome
130+
event = event.replace(regexOn, '');
131+
if (event.match(regexMouseEvents)) {
132+
// This allows the click event to fire on the notice. There is
133+
// probably a much better way to do it.
134+
elem.getBoundingClientRect();
135+
eventObject = document.createEvent("MouseEvents");
136+
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);
137+
} else if (event.match(regexUiEvents)) {
138+
eventObject = document.createEvent("UIEvents");
139+
eventObject.initUIEvent(event, origEvent.bubbles, origEvent.cancelable, origEvent.view, origEvent.detail);
140+
} else if (event.match(regexHtmlEvents)) {
141+
eventObject = document.createEvent("HTMLEvents");
142+
eventObject.initEvent(event, origEvent.bubbles, origEvent.cancelable);
143+
}
144+
if (!eventObject) {
145+
return;
146+
};
147+
elem.dispatchEvent(eventObject);
148+
} else {
149+
// Internet Explorer
150+
if (!event.match(regexOn)) {
151+
event = "on" + event;
152+
};
153+
eventObject = document.createEventObject(origEvent);
154+
elem.fireEvent(event, eventObject);
155+
}
156+
};
157+
158+
// This is used to pass events through the el if it is non-blocking.
159+
var nonblockPass = function nonblockPass(elem, event, eventName) {
160+
elem.classList.add('nonblock-hide');
161+
var elBelow = document.elementFromPoint(event.clientX, event.clientY);
162+
var range = void 0,
163+
textNode = void 0,
164+
whitespaceBefore = void 0,
165+
text = void 0,
166+
offset = void 0;
167+
if (document.caretPositionFromPoint) {
168+
range = document.caretPositionFromPoint(event.clientX, event.clientY);
169+
textNode = range.offsetNode;
170+
offset = range.offset;
171+
} else if (document.caretRangeFromPoint) {
172+
range = document.caretRangeFromPoint(event.clientX, event.clientY);
173+
textNode = range.startContainer;
174+
offset = range.startOffset;
175+
}
176+
if (range) {
177+
whitespaceBefore = range.startContainer.textContent.match(/^[\s\n]*/)[0];
178+
text = range.startContainer.textContent.replace(/[\s\n]+$/g, '');
179+
}
180+
181+
elem.classList.remove('nonblock-hide');
182+
var cursorStyle = getCursor(elBelow);
183+
isOverTextNode = false;
184+
if (cursorStyle === 'auto' && elBelow.tagName === 'A') {
185+
cursorStyle = 'pointer';
186+
} else if ((!whitespaceBefore.length || offset > whitespaceBefore.length) && offset < text.length) {
187+
if (cursorStyle === 'auto') {
188+
cursorStyle = 'text';
189+
}
190+
isOverTextNode = true;
191+
}
192+
193+
if (range && isSelectingText && offset > 0) {
194+
var selection = window.getSelection();
195+
var selectionRange = void 0,
196+
addRange = false;
197+
if (selection.rangeCount === 0 || !selection.getRangeAt(0)) {
198+
selectionRange = document.createRange();
199+
selectionRange.setStart(range.startContainer, offset - 1);
200+
addRange = true;
201+
} else {
202+
selectionRange = selection.getRangeAt(0);
203+
}
204+
205+
selectionRange.setEnd(range.endContainer, offset);
206+
if (addRange) {
207+
window.getSelection().addRange(selectionRange);
208+
}
209+
}
210+
211+
setCursor(elem, cursorStyle !== 'auto' ? cursorStyle : 'default');
212+
// If the element changed, call mouseenter, mouseleave, etc.
213+
if (!nonBlockLastElem || nonBlockLastElem !== elBelow) {
214+
if (nonBlockLastElem) {
215+
var lastElem = nonBlockLastElem;
216+
domEvent(lastElem, 'mouseleave', event);
217+
domEvent(lastElem, 'mouseout', event);
218+
}
219+
domEvent(elBelow, 'mouseenter', event);
220+
domEvent(elBelow, 'mouseover', event);
221+
}
222+
domEvent(elBelow, eventName, event);
223+
// Remember the latest element the mouse was over.
224+
nonBlockLastElem = elBelow;
225+
};
226+
});
227+
})();

NonBlock.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
*
66
* @author Hunter Perrin <hperrin@gmail.com>
77
*/
8-
'use strict'
8+
'use strict';
99

10-
document.addEventListener('DOMContentLoaded', () => {
10+
((init) => {
11+
if (document.body) {
12+
init();
13+
} else {
14+
document.addEventListener('DOMContentLoaded', init);
15+
}
16+
})(() => {
1117
// This keeps track of the last element the mouse was over, so
1218
// mouseleave, mouseenter, etc can be called.
1319
let nonBlockLastElem;
@@ -56,10 +62,6 @@ document.addEventListener('DOMContentLoaded', () => {
5662
ev.stopPropagation();
5763
}
5864
}, true);
59-
document.body.addEventListener('mouseover', (ev) => {
60-
}, true);
61-
document.body.addEventListener('mouseout', (ev) => {
62-
}, true);
6365
document.body.addEventListener('mousemove', (ev) => {
6466
if (isNonBlocking(ev.target)) {
6567
nonblockPass(ev.target, ev, 'onmousemove');

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# NonBlock.js
22

3-
Nonblocking UI elements in JavaScript.
3+
Non-blocking UI elements in JavaScript.
4+
5+
NonBlock.js lets you provide uninstrusive UI elements. They will fade when a user hovers over them, and let the user select and interact with elements under them.
46

57
## Installation
68

index.html

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,56 @@
1111
margin: 30px 20px;
1212
}
1313
.linkcontainer {
14-
position: absolute;
15-
top: 200px;
16-
left: 50px;
14+
position: relative;
1715
width: 400px;
1816
background-color: #8d8;
1917
border-radius: 5px;
2018
padding: 25px;
21-
margin-bottom: 30px;
19+
margin: 30px 0;
2220
}
2321
.intheway {
24-
position: absolute;
25-
top: 180px;
26-
left: 50px;
27-
height: 100px;
28-
width: 120px;
29-
background-color: #ccf;
22+
background-color: #6400B3;
23+
color: #FFF;
3024
border-radius: 5px;
3125
padding: 12px;
26+
box-shadow: 0px 2px 30px 5px rgba(0,0,0,0.1);
3227
}
33-
.linkcontainer.second {
34-
top: 400px;
28+
.intheway.first {
29+
position: relative;
30+
top: -180px;
31+
left: 30px;
32+
height: 100px;
33+
width: 120px;
3534
}
3635
.intheway.second {
37-
top: 0;
38-
bottom: 0;
39-
left: 0;
40-
right: 0;
36+
position: absolute;
37+
top: 4px;
38+
bottom: 4px;
39+
left: 4px;
40+
right: 4px;
4141
height: auto;
4242
width: auto;
4343
}
4444
</style>
4545
<link rel="stylesheet" type="text/css" href="NonBlock.css" />
46-
<script type="text/javascript" src="NonBlock.js"></script>
46+
<script type="text/javascript" src="NonBlock.es5.js"></script>
4747
</head>
4848
<body>
4949
<h1>NonBlock.js</h1>
5050
<p>
51-
Hover over the thing.
51+
NonBlock.js lets you provide uninstrusive UI elements. They will fade when a user hovers over them, and let the user select and interact with elements under them. Hover over the purple boxes to try it out.
5252
</p>
53-
<div class="linkcontainer">
53+
<div class="linkcontainer first">
5454
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.
5555
</div>
56-
<div class="nonblock intheway">
56+
<div class="nonblock intheway first">
5757
There&apos;s a link under me. I am going to stay in the way of that link.
5858
</div>
59+
<div>
60+
Try toggling non-blocking on this demo:
61+
<button onclick="document.querySelector('.intheway.second').classList.add('nonblock')">Enable Non-Block</button>
62+
<button onclick="document.querySelector('.intheway.second').classList.remove('nonblock')">Disable Non-Block</button>
63+
</div>
5964
<div class="linkcontainer second">
6065
This is a <button type="button" onclick="alert('You clicked the button.')">button</button>.
6166
<div class="nonblock intheway second">

0 commit comments

Comments
 (0)