@@ -6,7 +6,9 @@ const props = defineProps({
6
6
scrollPercentage: { type: Number , required: false , default: 0 },
7
7
/** TODO: Fix prop name. Maybe: 'waitSecondsAndShow' */
8
8
navigateBeforeShowSeconds: { type: Number , required: false , default: 0 },
9
+ /** TODO: Fix prop name. Maybe: 'exitIntentEnabled' */
9
10
mouseOutEnabled: { type: Boolean , required: false , default: true },
11
+ touchDeviceSensitivity: { type: Number , required: false , default: 5 },
10
12
showByDefault: { type: Boolean , required: false , default: false },
11
13
showCloseBtn: { type: Boolean , required: false , default: true },
12
14
color: { type: String , required: false , default: ' #555' },
@@ -21,10 +23,16 @@ const props = defineProps({
21
23
22
24
const show = ref (false );
23
25
let scrollHandler = null ;
26
+ let touchDeviceExitIntentHandler = null ;
24
27
25
28
onMounted (() => {
26
29
if (props .mouseOutEnabled ) {
27
- document .documentElement .addEventListener (' mouseleave' , handleMouseLeave);
30
+ if (props .touchDeviceSensitivity && isTouchDevice ()) {
31
+ touchDeviceExitIntentHandler = showOnFastTouchScrollUp ();
32
+ window .addEventListener (' scroll' , touchDeviceExitIntentHandler);
33
+ } else {
34
+ document .documentElement .addEventListener (' mouseleave' , handleMouseLeave);
35
+ }
28
36
}
29
37
if (props .navigateBeforeShowSeconds ) {
30
38
if (isAllowedToShow () && isLocalStorageExpired ()) {
@@ -45,6 +53,7 @@ onMounted(() => {
45
53
onUnmounted (() => {
46
54
document .documentElement .removeEventListener (' mouseleave' , handleMouseLeave);
47
55
window .removeEventListener (' scroll' , scrollHandler);
56
+ window .removeEventListener (' scroll' , touchDeviceExitIntentHandler);
48
57
});
49
58
50
59
const handleMouseLeave = () => {
@@ -103,6 +112,50 @@ const isLocalStorageExpired = () => {
103
112
}
104
113
};
105
114
115
+ const isTouchDevice = () => {
116
+ if (' maxTouchPoints' in navigator ) {
117
+ return navigator .maxTouchPoints > 0 ;
118
+ } else if (' msMaxTouchPoints' in navigator ) {
119
+ return navigator .msMaxTouchPoints > 0 ;
120
+ } else {
121
+ const mQ = window .matchMedia && matchMedia (' (pointer:coarse)' );
122
+ if (mQ && mQ .media === ' (pointer:coarse)' ) {
123
+ return !! mQ .matches ;
124
+ } else if (' orientation' in window ) {
125
+ /* Deprecated, used as a fallback */
126
+ return true ;
127
+ } else {
128
+ /* Fallback to user agent sniffing*/
129
+ const UA = navigator .userAgent ;
130
+ return (
131
+ / \b (BlackBerry| webOS| iPhone| IEMobile)\b / i .test (UA ) ||
132
+ / \b (Android| Windows Phone| iPad| iPod)\b / i .test (UA )
133
+ );
134
+ }
135
+ }
136
+ };
137
+
138
+ const showOnFastTouchScrollUp = () => {
139
+ let isScrolling, startPos, finalPos, destUpwards;
140
+ let i = 0 ;
141
+ return () => {
142
+ i++ ;
143
+ if (i == 1 ) startPos = window .scrollY ;
144
+ /* Clear our timeout throughout the scroll */
145
+ window .clearTimeout (isScrolling);
146
+ isScrolling = setTimeout (() => {
147
+ finalPos = window .scrollY ;
148
+ destUpwards = startPos - finalPos;
149
+ if (destUpwards > 5000 / props .touchDeviceSensitivity ) {
150
+ if (isAllowedToShow () && isLocalStorageExpired ()) {
151
+ showModal ();
152
+ }
153
+ }
154
+ i = 0 ;
155
+ }, 50 );
156
+ };
157
+ };
158
+
106
159
const closeModal = () => {
107
160
show .value = false ;
108
161
document .body .style .overflowY = ' auto' ;
0 commit comments