0
|
1 |
/*! |
19
|
2 |
* hoverIntent v1.10.2 // 2020.04.28 // jQuery v1.7.0+ |
18
|
3 |
* http://briancherne.github.io/jquery-hoverIntent/ |
0
|
4 |
* |
|
5 |
* You may use hoverIntent under the terms of the MIT license. Basically that |
|
6 |
* means you are free to use hoverIntent as long as this header is left intact. |
18
|
7 |
* Copyright 2007-2019 Brian Cherne |
0
|
8 |
*/ |
18
|
9 |
|
|
10 |
/** |
|
11 |
* hoverIntent is similar to jQuery's built-in "hover" method except that |
0
|
12 |
* instead of firing the handlerIn function immediately, hoverIntent checks |
|
13 |
* to see if the user's mouse has slowed down (beneath the sensitivity |
|
14 |
* threshold) before firing the event. The handlerOut function is only |
|
15 |
* called after a matching handlerIn. |
|
16 |
* |
|
17 |
* // basic usage ... just like .hover() |
|
18 |
* .hoverIntent( handlerIn, handlerOut ) |
|
19 |
* .hoverIntent( handlerInOut ) |
|
20 |
* |
|
21 |
* // basic usage ... with event delegation! |
|
22 |
* .hoverIntent( handlerIn, handlerOut, selector ) |
|
23 |
* .hoverIntent( handlerInOut, selector ) |
|
24 |
* |
|
25 |
* // using a basic configuration object |
|
26 |
* .hoverIntent( config ) |
|
27 |
* |
|
28 |
* @param handlerIn function OR configuration object |
|
29 |
* @param handlerOut function OR selector for delegation OR undefined |
|
30 |
* @param selector selector OR undefined |
|
31 |
* @author Brian Cherne <brian(at)cherne(dot)net> |
|
32 |
*/ |
18
|
33 |
|
|
34 |
;(function(factory) { |
|
35 |
'use strict'; |
|
36 |
if (typeof define === 'function' && define.amd) { |
|
37 |
define(['jquery'], factory); |
|
38 |
} else if (typeof module === 'object' && module.exports) { |
|
39 |
module.exports = factory(require('jquery')); |
|
40 |
} else if (jQuery && !jQuery.fn.hoverIntent) { |
|
41 |
factory(jQuery); |
|
42 |
} |
|
43 |
})(function($) { |
|
44 |
'use strict'; |
|
45 |
|
|
46 |
// default configuration values |
|
47 |
var _cfg = { |
|
48 |
interval: 100, |
|
49 |
sensitivity: 6, |
|
50 |
timeout: 0 |
|
51 |
}; |
|
52 |
|
|
53 |
// counter used to generate an ID for each instance |
|
54 |
var INSTANCE_COUNT = 0; |
|
55 |
|
|
56 |
// current X and Y position of mouse, updated during mousemove tracking (shared across instances) |
|
57 |
var cX, cY; |
|
58 |
|
|
59 |
// saves the current pointer position coordinates based on the given mousemove event |
|
60 |
var track = function(ev) { |
|
61 |
cX = ev.pageX; |
|
62 |
cY = ev.pageY; |
|
63 |
}; |
0
|
64 |
|
18
|
65 |
// compares current and previous mouse positions |
|
66 |
var compare = function(ev,$el,s,cfg) { |
|
67 |
// compare mouse positions to see if pointer has slowed enough to trigger `over` function |
|
68 |
if ( Math.sqrt( (s.pX-cX)*(s.pX-cX) + (s.pY-cY)*(s.pY-cY) ) < cfg.sensitivity ) { |
|
69 |
$el.off(s.event,track); |
|
70 |
delete s.timeoutId; |
|
71 |
// set hoverIntent state as active for this element (permits `out` handler to trigger) |
|
72 |
s.isActive = true; |
|
73 |
// overwrite old mouseenter event coordinates with most recent pointer position |
|
74 |
ev.pageX = cX; ev.pageY = cY; |
|
75 |
// clear coordinate data from state object |
|
76 |
delete s.pX; delete s.pY; |
|
77 |
return cfg.over.apply($el[0],[ev]); |
|
78 |
} else { |
|
79 |
// set previous coordinates for next comparison |
|
80 |
s.pX = cX; s.pY = cY; |
|
81 |
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) |
|
82 |
s.timeoutId = setTimeout( function(){compare(ev, $el, s, cfg);} , cfg.interval ); |
|
83 |
} |
|
84 |
}; |
0
|
85 |
|
18
|
86 |
// triggers given `out` function at configured `timeout` after a mouseleave and clears state |
|
87 |
var delay = function(ev,$el,s,out) { |
|
88 |
var data = $el.data('hoverIntent'); |
|
89 |
if (data) { |
|
90 |
delete data[s.id]; |
|
91 |
} |
|
92 |
return out.apply($el[0],[ev]); |
|
93 |
}; |
|
94 |
|
19
|
95 |
// checks if `value` is a function |
|
96 |
var isFunction = function(value) { |
|
97 |
return typeof value === 'function'; |
|
98 |
}; |
|
99 |
|
18
|
100 |
$.fn.hoverIntent = function(handlerIn,handlerOut,selector) { |
|
101 |
// instance ID, used as a key to store and retrieve state information on an element |
|
102 |
var instanceId = INSTANCE_COUNT++; |
|
103 |
|
|
104 |
// extend the default configuration and parse parameters |
|
105 |
var cfg = $.extend({}, _cfg); |
|
106 |
if ( $.isPlainObject(handlerIn) ) { |
|
107 |
cfg = $.extend(cfg, handlerIn); |
19
|
108 |
if ( !isFunction(cfg.out) ) { |
18
|
109 |
cfg.out = cfg.over; |
|
110 |
} |
19
|
111 |
} else if ( isFunction(handlerOut) ) { |
0
|
112 |
cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } ); |
|
113 |
} else { |
|
114 |
cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } ); |
|
115 |
} |
|
116 |
|
|
117 |
// A private function for handling mouse 'hovering' |
|
118 |
var handleHover = function(e) { |
18
|
119 |
// cloned event to pass to handlers (copy required for event object to be passed in IE) |
5
|
120 |
var ev = $.extend({},e); |
18
|
121 |
|
|
122 |
// the current target of the mouse event, wrapped in a jQuery object |
|
123 |
var $el = $(this); |
|
124 |
|
|
125 |
// read hoverIntent data from element (or initialize if not present) |
|
126 |
var hoverIntentData = $el.data('hoverIntent'); |
|
127 |
if (!hoverIntentData) { $el.data('hoverIntent', (hoverIntentData = {})); } |
|
128 |
|
|
129 |
// read per-instance state from element (or initialize if not present) |
|
130 |
var state = hoverIntentData[instanceId]; |
|
131 |
if (!state) { hoverIntentData[instanceId] = state = { id: instanceId }; } |
0
|
132 |
|
18
|
133 |
// state properties: |
|
134 |
// id = instance ID, used to clean up data |
|
135 |
// timeoutId = timeout ID, reused for tracking mouse position and delaying "out" handler |
|
136 |
// isActive = plugin state, true after `over` is called just until `out` is called |
|
137 |
// pX, pY = previously-measured pointer coordinates, updated at each polling interval |
|
138 |
// event = string representing the namespaced event used for mouse tracking |
0
|
139 |
|
18
|
140 |
// clear any existing timeout |
|
141 |
if (state.timeoutId) { state.timeoutId = clearTimeout(state.timeoutId); } |
|
142 |
|
|
143 |
// namespaced event used to register and unregister mousemove tracking |
|
144 |
var mousemove = state.event = 'mousemove.hoverIntent.hoverIntent'+instanceId; |
|
145 |
|
|
146 |
// handle the event, based on its type |
|
147 |
if (e.type === 'mouseenter') { |
|
148 |
// do nothing if already active |
|
149 |
if (state.isActive) { return; } |
0
|
150 |
// set "previous" X and Y position based on initial entry point |
18
|
151 |
state.pX = ev.pageX; state.pY = ev.pageY; |
0
|
152 |
// update "current" X and Y position based on mousemove |
18
|
153 |
$el.off(mousemove,track).on(mousemove,track); |
0
|
154 |
// start polling interval (self-calling timeout) to compare mouse coordinates over time |
18
|
155 |
state.timeoutId = setTimeout( function(){compare(ev,$el,state,cfg);} , cfg.interval ); |
|
156 |
} else { // "mouseleave" |
|
157 |
// do nothing if not already active |
|
158 |
if (!state.isActive) { return; } |
0
|
159 |
// unbind expensive mousemove event |
18
|
160 |
$el.off(mousemove,track); |
0
|
161 |
// if hoverIntent state is true, then call the mouseOut function after the specified delay |
18
|
162 |
state.timeoutId = setTimeout( function(){delay(ev,$el,state,cfg.out);} , cfg.timeout ); |
0
|
163 |
} |
|
164 |
}; |
|
165 |
|
|
166 |
// listen for mouseenter and mouseleave |
|
167 |
return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector); |
|
168 |
}; |
18
|
169 |
}); |