Skip to content

Commit 701ddb7

Browse files
committed
Simulate mouse events in PointerEvents mode. Bump version.
1 parent ccab47f commit 701ddb7

File tree

6 files changed

+199
-90
lines changed

6 files changed

+199
-90
lines changed

NonBlock.es5.js

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@
7070
var windowStyle = window.getComputedStyle(document.body);
7171
this.pointerEventsSupport = windowStyle.pointerEvents && windowStyle.pointerEvents === 'auto';
7272

73+
// Some useful regexes.
74+
this.regexOn = /^on/;
75+
this.regexMouseEvents = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;
76+
this.regexUiEvents = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/;
77+
this.regexHtmlEvents = /^(scroll|resize|(un)?load|abort|error)$/;
78+
// Whether to use event constructors.
79+
this.useEventConstructors = true;
80+
try {
81+
var e = new MouseEvent('click');
82+
} catch (e) {
83+
this.useEventConstructors = false;
84+
}
85+
7386
// If mode is not provided, use PointerEvents, if it's supported.
7487
if (typeof mode === 'undefined') {
7588
this.mode = this.pointerEventsSupport ? 'PointerEvents' : 'EventForwarding';
@@ -86,6 +99,8 @@
8699
_createClass(NonBlock, [{
87100
key: 'initPointerEvents',
88101
value: function initPointerEvents() {
102+
var _this = this;
103+
89104
// Using pointer-events, we can just detect whether an element is being
90105
// hovered over. No event forwarding necessary.
91106

@@ -104,9 +119,21 @@
104119

105120
var rect = nonblock.getBoundingClientRect();
106121
if (ev.clientX >= rect.left && ev.clientX <= rect.right && ev.clientY >= rect.top && ev.clientY <= rect.bottom) {
107-
nonblock.classList.add('nonblock-hover');
122+
if (!nonblock.classList.contains('nonblock-hover')) {
123+
nonblock.classList.add('nonblock-hover');
124+
if (_this.isSimulateMouse(nonblock) && ev.isTrusted) {
125+
_this.domEvent(nonblock, 'onmouseenter', ev, false);
126+
_this.domEvent(nonblock, 'onmouseover', ev, true);
127+
}
128+
} else if (_this.isSimulateMouse(nonblock) && ev.isTrusted) {
129+
_this.domEvent(nonblock, 'onmousemove', ev, true);
130+
}
108131
} else {
109132
if (nonblock.classList.contains('nonblock-hover')) {
133+
if (_this.isSimulateMouse(nonblock) && ev.isTrusted) {
134+
_this.domEvent(nonblock, 'onmouseout', ev, true);
135+
_this.domEvent(nonblock, 'onmouseleave', ev, false);
136+
}
110137
nonblock.classList.remove('nonblock-hover');
111138
}
112139
}
@@ -132,7 +159,7 @@
132159
}, {
133160
key: 'initEventForwarding',
134161
value: function initEventForwarding() {
135-
var _this = this;
162+
var _this2 = this;
136163

137164
// No pointer-events means we have to fall back to using event forwarding.
138165

@@ -144,110 +171,98 @@
144171
// These are used for selecting text under a nonblock element.
145172
this.isOverTextNode = false;
146173
this.selectingText = false;
147-
// Some useful regexes.
148-
this.regexOn = /^on/;
149-
this.regexMouseEvents = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;
150-
this.regexUiEvents = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/;
151-
this.regexHtmlEvents = /^(scroll|resize|(un)?load|abort|error)$/;
152-
// Whether to use event constructors.
153-
this.useEventConstructors = true;
154-
try {
155-
var e = new MouseEvent('click');
156-
} catch (e) {
157-
this.useEventConstructors = false;
158-
}
159174

160175
this.onmouseenter = function (ev) {
161176
var nonblock = void 0;
162-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
163-
_this.nonBlockLastElem = false;
164-
if (!_this.isPropagating(nonblock)) {
177+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
178+
_this2.nonBlockLastElem = false;
179+
if (!_this2.isPropagating(nonblock)) {
165180
ev.stopPropagation();
166181
}
167182
}
168183
};
169184
this.onmouseleave = function (ev) {
170185
var nonblock = void 0;
171-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
172-
_this.remCursor(nonblock);
173-
_this.nonBlockLastElem = null;
174-
_this.selectingText = false;
175-
if (!_this.isPropagating(nonblock)) {
186+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
187+
_this2.remCursor(nonblock);
188+
_this2.nonBlockLastElem = null;
189+
_this2.selectingText = false;
190+
if (!_this2.isPropagating(nonblock)) {
176191
ev.stopPropagation();
177192
}
178193
}
179194
};
180195
this.onmouseover = function (ev) {
181196
var nonblock = void 0;
182-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target)) && !_this.isPropagating(nonblock)) {
197+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target)) && !_this2.isPropagating(nonblock)) {
183198
ev.stopPropagation();
184199
}
185200
};
186201
this.onmouseout = function (ev) {
187202
var nonblock = void 0;
188-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target)) && !_this.isPropagating(nonblock)) {
203+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target)) && !_this2.isPropagating(nonblock)) {
189204
ev.stopPropagation();
190205
}
191206
};
192207
this.onmousemove = function (ev) {
193208
var nonblock = void 0;
194-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
195-
_this.nonblockPass(nonblock, ev, 'onmousemove');
209+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
210+
_this2.nonblockPass(nonblock, ev, 'onmousemove');
196211
// If the user just clicks somewhere, we don't want to select text, so this
197212
// detects that the user moved their mouse.
198-
if (_this.selectingText === null) {
213+
if (_this2.selectingText === null) {
199214
window.getSelection().removeAllRanges();
200-
_this.selectingText = true;
201-
} else if (_this.selectingText) {
215+
_this2.selectingText = true;
216+
} else if (_this2.selectingText) {
202217
// Stop the default action, which would be selecting text.
203218
ev.preventDefault();
204219
}
205-
if (!_this.isPropagating(nonblock)) {
220+
if (!_this2.isPropagating(nonblock)) {
206221
ev.stopPropagation();
207222
}
208223
}
209224
};
210225
this.onmousedown = function (ev) {
211226
var nonblock = void 0;
212-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
213-
_this.nonblockPass(nonblock, ev, 'onmousedown');
214-
_this.selectingText = null;
215-
if (!_this.isFocusable(nonblock)) {
227+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
228+
_this2.nonblockPass(nonblock, ev, 'onmousedown');
229+
_this2.selectingText = null;
230+
if (!_this2.isFocusable(nonblock)) {
216231
// Stop the default action, which would focus the element.
217232
ev.preventDefault();
218233
}
219-
if (!_this.isPropagating(nonblock) || !_this.isActionPropagating(nonblock)) {
234+
if (!_this2.isPropagating(nonblock) || !_this2.isActionPropagating(nonblock)) {
220235
ev.stopPropagation();
221236
}
222237
}
223238
};
224239
this.onmouseup = function (ev) {
225240
var nonblock = void 0;
226-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
227-
_this.nonblockPass(nonblock, ev, 'onmouseup');
228-
if (_this.selectingText === null) {
241+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
242+
_this2.nonblockPass(nonblock, ev, 'onmouseup');
243+
if (_this2.selectingText === null) {
229244
window.getSelection().removeAllRanges();
230245
}
231-
_this.selectingText = false;
232-
if (!_this.isPropagating(nonblock) || !_this.isActionPropagating(nonblock)) {
246+
_this2.selectingText = false;
247+
if (!_this2.isPropagating(nonblock) || !_this2.isActionPropagating(nonblock)) {
233248
ev.stopPropagation();
234249
}
235250
}
236251
};
237252
this.onclick = function (ev) {
238253
var nonblock = void 0;
239-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
240-
_this.nonblockPass(nonblock, ev, 'onclick');
241-
if (!_this.isPropagating(nonblock) || !_this.isActionPropagating(nonblock)) {
254+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
255+
_this2.nonblockPass(nonblock, ev, 'onclick');
256+
if (!_this2.isPropagating(nonblock) || !_this2.isActionPropagating(nonblock)) {
242257
ev.stopPropagation();
243258
}
244259
}
245260
};
246261
this.ondblclick = function (ev) {
247262
var nonblock = void 0;
248-
if (ev.isTrusted && (nonblock = _this.getNonBlocking(ev.target))) {
249-
_this.nonblockPass(nonblock, ev, 'ondblclick');
250-
if (!_this.isPropagating(nonblock) || !_this.isActionPropagating(nonblock)) {
263+
if (ev.isTrusted && (nonblock = _this2.getNonBlocking(ev.target))) {
264+
_this2.nonblockPass(nonblock, ev, 'ondblclick');
265+
if (!_this2.isPropagating(nonblock) || !_this2.isActionPropagating(nonblock)) {
251266
ev.stopPropagation();
252267
}
253268
}
@@ -487,6 +502,11 @@
487502
value: function isFocusable(el) {
488503
return el.classList.contains('nonblock-allow-focus');
489504
}
505+
}, {
506+
key: 'isSimulateMouse',
507+
value: function isSimulateMouse(el) {
508+
return !el.classList.contains('nonblock-stop-mouse-simulation');
509+
}
490510
}, {
491511
key: 'getCursor',
492512
value: function getCursor(el) {

NonBlock.js

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@
2929
const windowStyle = window.getComputedStyle(document.body);
3030
this.pointerEventsSupport = (windowStyle.pointerEvents && windowStyle.pointerEvents === 'auto');
3131

32+
// Some useful regexes.
33+
this.regexOn = /^on/;
34+
this.regexMouseEvents = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;
35+
this.regexUiEvents = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/;
36+
this.regexHtmlEvents = /^(scroll|resize|(un)?load|abort|error)$/;
37+
// Whether to use event constructors.
38+
this.useEventConstructors = true;
39+
try {
40+
const e = new MouseEvent('click');
41+
} catch (e) {
42+
this.useEventConstructors = false;
43+
}
44+
3245
// If mode is not provided, use PointerEvents, if it's supported.
3346
if (typeof mode === 'undefined') {
3447
this.mode = this.pointerEventsSupport ? 'PointerEvents' : 'EventForwarding';
@@ -54,9 +67,21 @@
5467
for (let nonblock of nonblocks) {
5568
const rect = nonblock.getBoundingClientRect();
5669
if (ev.clientX >= rect.left && ev.clientX <= rect.right && ev.clientY >= rect.top && ev.clientY <= rect.bottom) {
57-
nonblock.classList.add('nonblock-hover');
70+
if (!nonblock.classList.contains('nonblock-hover')) {
71+
nonblock.classList.add('nonblock-hover');
72+
if (this.isSimulateMouse(nonblock) && ev.isTrusted) {
73+
this.domEvent(nonblock, 'onmouseenter', ev, false);
74+
this.domEvent(nonblock, 'onmouseover', ev, true);
75+
}
76+
} else if (this.isSimulateMouse(nonblock) && ev.isTrusted) {
77+
this.domEvent(nonblock, 'onmousemove', ev, true);
78+
}
5879
} else {
5980
if (nonblock.classList.contains('nonblock-hover')) {
81+
if (this.isSimulateMouse(nonblock) && ev.isTrusted) {
82+
this.domEvent(nonblock, 'onmouseout', ev, true);
83+
this.domEvent(nonblock, 'onmouseleave', ev, false);
84+
}
6085
nonblock.classList.remove('nonblock-hover');
6186
}
6287
}
@@ -115,18 +140,6 @@
115140
// These are used for selecting text under a nonblock element.
116141
this.isOverTextNode = false;
117142
this.selectingText = false;
118-
// Some useful regexes.
119-
this.regexOn = /^on/;
120-
this.regexMouseEvents = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;
121-
this.regexUiEvents = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/;
122-
this.regexHtmlEvents = /^(scroll|resize|(un)?load|abort|error)$/;
123-
// Whether to use event constructors.
124-
this.useEventConstructors = true;
125-
try {
126-
const e = new MouseEvent('click');
127-
} catch (e) {
128-
this.useEventConstructors = false;
129-
}
130143

131144
this.onmouseenter = (ev) => {
132145
let nonblock;
@@ -449,6 +462,10 @@
449462
return el.classList.contains('nonblock-allow-focus');
450463
}
451464

465+
isSimulateMouse(el) {
466+
return !el.classList.contains('nonblock-stop-mouse-simulation');
467+
}
468+
452469
getCursor(el) {
453470
const style = window.getComputedStyle(el);
454471
return style.getPropertyValue('cursor');

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ window.NonBlockJs.nonBlock = new window.NonBlockJs.NonBlock(document.body, "Even
4848

4949
Nonblocking elements are given the `pointer-events: none;` style, so that the cursor does not interact with them. NonBlock.js will listen to `mousemove` events on the document body and will detect when the cursor passes over a nonblocking element. It applies the `nonblock-hover` class to fade the element.
5050

51+
#### Mouse Events in PointerEvents Mode
52+
53+
Normally, an element with the `pointer-events: none;` style will not receive any events related to mouse movement/interaction. In order to let you listen for these events, NonBlock.js will fire simulated `mouseover`, `mouseenter`, `mousemove`, `mouseout`, and `mouseleave` events on the nonblocking element. You can add the class `nonblock-stop-mouse-simulation` to prevent this behavior. (It is worth noting that only the element with `nonblock` receives these events. None of its children receive any events.)
54+
5155
### EventForwarding Mode
5256

5357
Nonblocking elements have a `:hover` pseudoclass applied to them that will fade them. NonBlock.js will listen for mouse events on document.body and detect when a mouse event is fired on a nonblocking element. It will detect what element is below the nonblocking element and forward the event to that element. It will detect the cursor that applies to that element and apply the same cursor to the nonblocking element. It also watches mousedown and mousemove and attempts to allow the user to select text.

0 commit comments

Comments
 (0)