@@ -13,21 +13,17 @@ function setStyle(style, key, value) {
1313 }
1414}
1515
16- // A "virtual clock" to solve issues like https://github.com/preactjs/preact/issues/3927.
16+ // A logical clock to solve issues like https://github.com/preactjs/preact/issues/3927.
1717// When the DOM performs an event it leaves micro-ticks in between bubbling up which means that
1818// an event can trigger on a newly reated DOM-node while the event bubbles up.
1919//
20- // Originally inspired by Vue https://github.com/vuejs/core/blob/main/packages/runtime-dom/src/modules/events.ts#L90-L101,
21- // but modified to use a virtual clock instead of Date.now() in case event handlers get attached and
22- // events get dispatched during the same millisecond.
20+ // Originally inspired by Vue
21+ // (https://github.com/vuejs/core/blob/caeb8a68811a1b0f79/packages/runtime-dom/src/modules/events.ts#L90-L101),
22+ // but modified to use a logical clock instead of Date.now() in case event handlers get attached
23+ // and events get dispatched during the same millisecond.
2324//
24- // Odd values are reserved for event dispatch times, and even values are reserved for new
25- // event handler attachment times.
26- //
27- // The clock is incremented before a new event is dispatched if the value is even
28- // (i.e a new event handler was attached after the previous new event).
29- // The clock is also incremented when a new event handler gets attached if the value is odd
30- // (i.e. a new event was dispatched after the previous new event dispatch).
25+ // The clock is incremented after each new event dispatch. This allows 1 000 000 new events
26+ // per second for over 280 years before the value reaches Number.MAX_SAFE_INTEGER (2**53 - 1).
3127let eventClock = 0 ;
3228
3329/**
@@ -85,21 +81,12 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
8581
8682 if ( value ) {
8783 if ( ! oldValue ) {
88- // If any new events were dispatched between this moment and the last time
89- // an event handler was attached (i.e. `eventClock` is an odd number),
90- // then increment `eventClock` first.
91- //
92- // The following line is a compacted version of:
93- // if (eventClock % 2 === 1) {
94- // eventClock += 1;
95- // }
96- // value._attached = eventClock;
97- value . _attached = eventClock += eventClock % 2 ;
84+ value . _attached = eventClock ;
9885 dom . addEventListener (
99- name ,
100- useCapture ? eventProxyCapture : eventProxy ,
101- useCapture
102- ) ;
86+ name ,
87+ useCapture ? eventProxyCapture : eventProxy ,
88+ useCapture
89+ ) ;
10390 } else {
10491 value . _attached = oldValue . _attached ;
10592 }
@@ -156,40 +143,32 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
156143}
157144
158145/**
159- * Proxy an event to hooked event handlers
160- * @param {PreactEvent } e The event object from the browser
146+ * Create an event proxy function.
147+ * @param {boolean } useCapture Is the event handler for the capture phase.
161148 * @private
162149 */
163- function eventProxy ( e ) {
164- if ( this . _listeners ) {
165- const eventHandler = this . _listeners [ e . type + false ] ;
166- // If e._dispatched is set, it has to be an odd number, so !e._dispatched must be true if set.
167- if ( ! e . _dispatched ) {
168- // If any new event handlers were attached after the previous new event dispatch
169- // (i.e. `eventClock` is an even number), then increment `eventClock` first.
170- //
171- // The following line is a compacted version of:
172- // if (eventClock % 2 === 0) {
173- // eventClock += 1;
174- // }
175- // e._dispatched = eventClock;
176- e . _dispatched = eventClock += ( eventClock + 1 ) % 2 ;
177- // When the _dispatched is smaller than the time when the targetted event handler was attached
178- // we know we have bubbled up to an element that was added during patching the dom.
179- } else if ( e . _dispatched < eventHandler . _attached ) {
180- return ;
150+ function createEventProxy ( useCapture ) {
151+ /**
152+ * Proxy an event to hooked event handlers
153+ * @param {PreactEvent } e The event object from the browser
154+ * @private
155+ */
156+ return function ( e ) {
157+ if ( this . _listeners ) {
158+ const eventHandler = this . _listeners [ e . type + useCapture ] ;
159+ if ( e . _dispatched == null ) {
160+ e . _dispatched = eventClock ++ ;
161+
162+ // When `e._dispatched` is smaller than the time when the targeted event
163+ // handler was attached we know we have bubbled up to an element that was added
164+ // during patching the DOM.
165+ } else if ( e . _dispatched < eventHandler . _attached ) {
166+ return ;
167+ }
168+ return eventHandler ( options . event ? options . event ( e ) : e ) ;
181169 }
182- return eventHandler ( options . event ? options . event ( e ) : e ) ;
183- }
170+ } ;
184171}
185172
186- /**
187- * Proxy an event to hooked event handlers
188- * @param {PreactEvent } e The event object from the browser
189- * @private
190- */
191- function eventProxyCapture ( e ) {
192- if ( this . _listeners ) {
193- return this . _listeners [ e . type + true ] ( options . event ? options . event ( e ) : e ) ;
194- }
195- }
173+ const eventProxy = createEventProxy ( false ) ;
174+ const eventProxyCapture = createEventProxy ( true ) ;
0 commit comments