@oscarpalmer/toretto 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,37 +1,39 @@
1
1
  import { isEventTarget } from "../internal/is.js";
2
2
  function addDelegatedHandler(doc, type, name, passive) {
3
- const count = `${name}${COUNT_SUFFIX}`;
4
- if (doc[count] != null) {
5
- doc[count] += 1;
6
- return;
7
- }
8
- doc[count] = 1;
3
+ if (DELEGATED.has(name)) return;
4
+ DELEGATED.add(name);
9
5
  doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, { passive });
10
6
  }
11
7
  function addDelegatedListener(target, type, name, listener, passive) {
12
8
  target[name] ??= /* @__PURE__ */ new Set();
13
- target[name]?.add(listener);
9
+ target[name].add(listener);
14
10
  addDelegatedHandler(document, type, name, passive);
15
11
  return () => {
16
- removeDelegatedListener(target, type, name, listener, passive);
12
+ removeDelegatedListener(target, name, listener);
17
13
  };
18
14
  }
19
15
  function delegatedEventHandler(event) {
20
16
  const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
21
17
  const items = event.composedPath();
22
18
  const { length } = items;
23
- Object.defineProperty(event, "target", {
24
- configurable: true,
25
- value: items[0]
19
+ let target = items[0];
20
+ Object.defineProperties(event, {
21
+ currentTarget: {
22
+ configurable: true,
23
+ get() {
24
+ return target;
25
+ }
26
+ },
27
+ target: {
28
+ configurable: true,
29
+ value: target
30
+ }
26
31
  });
27
32
  for (let index = 0; index < length; index += 1) {
28
33
  const item = items[index];
29
34
  const listeners = item[key];
30
35
  if (item.disabled || listeners == null) continue;
31
- Object.defineProperty(event, "currentTarget", {
32
- configurable: true,
33
- value: item
34
- });
36
+ target = item;
35
37
  for (const listener of listeners) {
36
38
  listener.call(item, event);
37
39
  if (event.cancelBubble) return;
@@ -41,23 +43,14 @@ function delegatedEventHandler(event) {
41
43
  function getDelegatedName(target, type, options) {
42
44
  if (isEventTarget(target) && EVENT_TYPES.has(type) && !options.capture && !options.once && options.signal == null) return `${EVENT_PREFIX}${type}${options.passive ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
43
45
  }
44
- function removeDelegatedHandler(doc, type, name, passive) {
45
- const count = `${name}${COUNT_SUFFIX}`;
46
- doc[count] -= 1;
47
- if (doc[count] < 1) {
48
- doc[count] = void 0;
49
- doc.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
50
- }
51
- }
52
- function removeDelegatedListener(target, type, name, listener, passive) {
46
+ function removeDelegatedListener(target, name, listener) {
53
47
  const handlers = target[name];
54
48
  if (handlers == null || !handlers.has(listener)) return false;
55
49
  handlers.delete(listener);
56
50
  if (handlers.size === 0) target[name] = void 0;
57
- removeDelegatedHandler(document, type, name, passive);
58
51
  return true;
59
52
  }
60
- var COUNT_SUFFIX = ".count";
53
+ var DELEGATED = /* @__PURE__ */ new Set();
61
54
  var EVENT_PREFIX = "@";
62
55
  var EVENT_SUFFIX_ACTIVE = ":active";
63
56
  var EVENT_SUFFIX_PASSIVE = ":passive";
@@ -48,7 +48,7 @@ function off(target, type, listener, options) {
48
48
  if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return;
49
49
  const extended = createEventOptions(options);
50
50
  const delegated = getDelegatedName(target, type, extended);
51
- if (delegated == null || !removeDelegatedListener(target, type, delegated, listener, extended.passive)) target.removeEventListener(type, listener, extended);
51
+ if (delegated == null || !removeDelegatedListener(target, delegated, listener)) target.removeEventListener(type, listener, extended);
52
52
  }
53
53
  function on(target, type, listener, options) {
54
54
  if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
@@ -1,4 +1,4 @@
1
- import { findAncestor, findRelatives } from "./relative.js";
1
+ import { findAncestor, findRelatives, getDistance } from "./relative.js";
2
2
  function findElement(selector, context) {
3
3
  return findElementOrElements(selector, context, true);
4
4
  }
@@ -61,4 +61,4 @@ var STYLE_HIDDEN = "hidden";
61
61
  var STYLE_NONE = "none";
62
62
  var SUFFIX_HOVER = ":hover";
63
63
  var TAG_HEAD = "HEAD";
64
- export { findElement as $, findElement, findElements as $$, findElements, findAncestor, findRelatives, getElementUnderPointer };
64
+ export { findElement as $, findElement, findElements as $$, findElements, findAncestor, findRelatives, getDistance, getElementUnderPointer };
@@ -1,12 +1,3 @@
1
- function getDistanceBetweenElements(origin, target) {
2
- if (origin === target) return 0;
3
- if (origin.parentElement === target) return 1;
4
- const children = [...origin.parentElement?.children ?? []];
5
- if (children.includes(target)) return Math.abs(children.indexOf(origin) - children.indexOf(target));
6
- const comparison = origin.compareDocumentPosition(target);
7
- const beforeOrInside = Boolean(comparison & 2 || comparison & 8);
8
- if (beforeOrInside || Boolean(comparison & 4 || comparison & 16)) return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
9
- }
10
1
  function findAncestor(origin, selector) {
11
2
  if (!(origin instanceof Element) || selector == null) return null;
12
3
  if (typeof selector === "string") {
@@ -31,8 +22,8 @@ function findRelatives(origin, selector, context) {
31
22
  let minimum;
32
23
  for (let index = 0; index < length; index += 1) {
33
24
  const element = elements[index];
34
- const distance = getDistanceBetweenElements(origin, element);
35
- if (distance != null && distance > 0) {
25
+ const distance = getDistance(origin, element);
26
+ if (distance > 0) {
36
27
  if (minimum == null || distance < minimum) minimum = distance;
37
28
  distances.push({
38
29
  distance,
@@ -40,19 +31,29 @@ function findRelatives(origin, selector, context) {
40
31
  });
41
32
  }
42
33
  }
43
- return distances.filter((found) => found.distance === minimum && found.element !== origin).map((found) => found.element);
34
+ return distances.filter((found) => found.distance === minimum).map((found) => found.element);
35
+ }
36
+ function getDistance(origin, target) {
37
+ if (origin === target) return 0;
38
+ if (origin.parentElement === target || target.parentElement === origin) return 1;
39
+ if (origin.parentElement != null && origin.parentElement === target.parentElement) {
40
+ const children = [...origin.parentElement.children];
41
+ return Math.abs(children.indexOf(origin) - children.indexOf(target));
42
+ }
43
+ const comparison = origin.compareDocumentPosition(target);
44
+ if (comparison & Node.DOCUMENT_POSITION_DISCONNECTED) return -1;
45
+ const preceding = comparison & Node.DOCUMENT_POSITION_PRECEDING;
46
+ return traverse(preceding ? origin : target, preceding ? target : origin) ?? -1;
44
47
  }
45
48
  function traverse(from, to) {
46
- let index = [...to.children].indexOf(from);
47
- if (index > -1) return 1;
48
49
  let current = from;
49
50
  let distance = 0;
50
51
  let parent = from.parentElement;
51
52
  while (parent != null) {
52
53
  if (parent === to) return distance + 1;
53
54
  const children = [...parent.children];
54
- if (children.includes(to)) return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
55
- index = children.findIndex((child) => child.contains(to));
55
+ if (to.parentElement === parent) return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
56
+ const index = children.findIndex((child) => child.contains(to));
56
57
  if (index > -1) {
57
58
  const traversed = traverse(current, children[index]);
58
59
  return traversed == null || traversed === -1 ? -1 : distance + Math.abs(index - children.indexOf(current)) + traversed;
@@ -62,4 +63,4 @@ function traverse(from, to) {
62
63
  parent = parent.parentElement;
63
64
  }
64
65
  }
65
- export { findAncestor, findRelatives };
66
+ export { findAncestor, findRelatives, getDistance };
package/dist/index.js CHANGED
@@ -3,10 +3,10 @@ import { isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInval
3
3
  import { isChildNode, isInDocument } from "./is.js";
4
4
  import { getData, setData } from "./data.js";
5
5
  import { dispatch, getPosition, off, on } from "./event/index.js";
6
- import { findAncestor, findRelatives } from "./find/relative.js";
6
+ import { findAncestor, findRelatives, getDistance } from "./find/relative.js";
7
7
  import { $ as findElement, $$ as findElements, getElementUnderPointer } from "./find/index.js";
8
8
  import { getFocusable, getTabbable, isFocusable, isTabbable } from "./focusable.js";
9
9
  import { html, sanitize } from "./html/index.js";
10
10
  import touch_default from "./touch.js";
11
11
  import { getStyle, getStyles, getTextDirection, setStyle, setStyles, toggleStyles } from "./style.js";
12
- export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
12
+ export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getDistance, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
@@ -305,6 +305,7 @@ function updateDataAttribute(element, key, value) {
305
305
  updateElementValue(element, getName(key), value, element.setAttribute, element.removeAttribute, true);
306
306
  }
307
307
  const ATTRIBUTE_DATA_PREFIX = "data-";
308
+ function noop() {}
308
309
  function calculate() {
309
310
  return new Promise((resolve) => {
310
311
  const values = [];
@@ -312,49 +313,51 @@ function calculate() {
312
313
  function step(now) {
313
314
  if (last != null) values.push(now - last);
314
315
  last = now;
315
- if (values.length >= CALCULATION_TOTAL) resolve(values.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - CALCULATION_TRIM));
316
+ if (values.length >= CALCULATION_TOTAL) resolve(values.sort().slice(CALCULATION_TRIM_PART, -CALCULATION_TRIM_PART).reduce((first, second) => first + second, 0) / (values.length - CALCULATION_TRIM_TOTAL));
316
317
  else requestAnimationFrame(step);
317
318
  }
318
319
  requestAnimationFrame(step);
319
320
  });
320
321
  }
321
- function noop() {}
322
322
  var CALCULATION_TOTAL = 10;
323
- var CALCULATION_TRIM = 4;
323
+ var CALCULATION_TRIM_PART = 2;
324
+ var CALCULATION_TRIM_TOTAL = 4;
324
325
  calculate().then((value) => {});
325
326
  function addDelegatedHandler(doc, type, name, passive) {
326
- const count = `${name}${COUNT_SUFFIX}`;
327
- if (doc[count] != null) {
328
- doc[count] += 1;
329
- return;
330
- }
331
- doc[count] = 1;
327
+ if (DELEGATED.has(name)) return;
328
+ DELEGATED.add(name);
332
329
  doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, { passive });
333
330
  }
334
331
  function addDelegatedListener(target, type, name, listener, passive) {
335
332
  target[name] ??= /* @__PURE__ */ new Set();
336
- target[name]?.add(listener);
333
+ target[name].add(listener);
337
334
  addDelegatedHandler(document, type, name, passive);
338
335
  return () => {
339
- removeDelegatedListener(target, type, name, listener, passive);
336
+ removeDelegatedListener(target, name, listener);
340
337
  };
341
338
  }
342
339
  function delegatedEventHandler(event) {
343
340
  const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
344
341
  const items = event.composedPath();
345
342
  const { length } = items;
346
- Object.defineProperty(event, "target", {
347
- configurable: true,
348
- value: items[0]
343
+ let target = items[0];
344
+ Object.defineProperties(event, {
345
+ currentTarget: {
346
+ configurable: true,
347
+ get() {
348
+ return target;
349
+ }
350
+ },
351
+ target: {
352
+ configurable: true,
353
+ value: target
354
+ }
349
355
  });
350
356
  for (let index = 0; index < length; index += 1) {
351
357
  const item = items[index];
352
358
  const listeners = item[key];
353
359
  if (item.disabled || listeners == null) continue;
354
- Object.defineProperty(event, "currentTarget", {
355
- configurable: true,
356
- value: item
357
- });
360
+ target = item;
358
361
  for (const listener of listeners) {
359
362
  listener.call(item, event);
360
363
  if (event.cancelBubble) return;
@@ -364,23 +367,14 @@ function delegatedEventHandler(event) {
364
367
  function getDelegatedName(target, type, options) {
365
368
  if (isEventTarget(target) && EVENT_TYPES.has(type) && !options.capture && !options.once && options.signal == null) return `${EVENT_PREFIX}${type}${options.passive ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
366
369
  }
367
- function removeDelegatedHandler(doc, type, name, passive) {
368
- const count = `${name}${COUNT_SUFFIX}`;
369
- doc[count] -= 1;
370
- if (doc[count] < 1) {
371
- doc[count] = void 0;
372
- doc.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
373
- }
374
- }
375
- function removeDelegatedListener(target, type, name, listener, passive) {
370
+ function removeDelegatedListener(target, name, listener) {
376
371
  const handlers = target[name];
377
372
  if (handlers == null || !handlers.has(listener)) return false;
378
373
  handlers.delete(listener);
379
374
  if (handlers.size === 0) target[name] = void 0;
380
- removeDelegatedHandler(document, type, name, passive);
381
375
  return true;
382
376
  }
383
- const COUNT_SUFFIX = ".count";
377
+ const DELEGATED = /* @__PURE__ */ new Set();
384
378
  const EVENT_PREFIX = "@";
385
379
  const EVENT_SUFFIX_ACTIVE = ":active";
386
380
  const EVENT_SUFFIX_PASSIVE = ":passive";
@@ -455,7 +449,7 @@ function off(target, type, listener, options) {
455
449
  if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return;
456
450
  const extended = createEventOptions(options);
457
451
  const delegated = getDelegatedName(target, type, extended);
458
- if (delegated == null || !removeDelegatedListener(target, type, delegated, listener, extended.passive)) target.removeEventListener(type, listener, extended);
452
+ if (delegated == null || !removeDelegatedListener(target, delegated, listener)) target.removeEventListener(type, listener, extended);
459
453
  }
460
454
  function on(target, type, listener, options) {
461
455
  if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
@@ -469,15 +463,6 @@ function on(target, type, listener, options) {
469
463
  };
470
464
  }
471
465
  const PROPERTY_DETAIL = "detail";
472
- function getDistanceBetweenElements(origin, target) {
473
- if (origin === target) return 0;
474
- if (origin.parentElement === target) return 1;
475
- const children = [...origin.parentElement?.children ?? []];
476
- if (children.includes(target)) return Math.abs(children.indexOf(origin) - children.indexOf(target));
477
- const comparison = origin.compareDocumentPosition(target);
478
- const beforeOrInside = Boolean(comparison & 2 || comparison & 8);
479
- if (beforeOrInside || Boolean(comparison & 4 || comparison & 16)) return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
480
- }
481
466
  function findAncestor(origin, selector) {
482
467
  if (!(origin instanceof Element) || selector == null) return null;
483
468
  if (typeof selector === "string") {
@@ -502,8 +487,8 @@ function findRelatives(origin, selector, context) {
502
487
  let minimum;
503
488
  for (let index = 0; index < length; index += 1) {
504
489
  const element = elements[index];
505
- const distance = getDistanceBetweenElements(origin, element);
506
- if (distance != null && distance > 0) {
490
+ const distance = getDistance(origin, element);
491
+ if (distance > 0) {
507
492
  if (minimum == null || distance < minimum) minimum = distance;
508
493
  distances.push({
509
494
  distance,
@@ -511,19 +496,29 @@ function findRelatives(origin, selector, context) {
511
496
  });
512
497
  }
513
498
  }
514
- return distances.filter((found) => found.distance === minimum && found.element !== origin).map((found) => found.element);
499
+ return distances.filter((found) => found.distance === minimum).map((found) => found.element);
500
+ }
501
+ function getDistance(origin, target) {
502
+ if (origin === target) return 0;
503
+ if (origin.parentElement === target || target.parentElement === origin) return 1;
504
+ if (origin.parentElement != null && origin.parentElement === target.parentElement) {
505
+ const children = [...origin.parentElement.children];
506
+ return Math.abs(children.indexOf(origin) - children.indexOf(target));
507
+ }
508
+ const comparison = origin.compareDocumentPosition(target);
509
+ if (comparison & Node.DOCUMENT_POSITION_DISCONNECTED) return -1;
510
+ const preceding = comparison & Node.DOCUMENT_POSITION_PRECEDING;
511
+ return traverse(preceding ? origin : target, preceding ? target : origin) ?? -1;
515
512
  }
516
513
  function traverse(from, to) {
517
- let index = [...to.children].indexOf(from);
518
- if (index > -1) return 1;
519
514
  let current = from;
520
515
  let distance = 0;
521
516
  let parent = from.parentElement;
522
517
  while (parent != null) {
523
518
  if (parent === to) return distance + 1;
524
519
  const children = [...parent.children];
525
- if (children.includes(to)) return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
526
- index = children.findIndex((child) => child.contains(to));
520
+ if (to.parentElement === parent) return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
521
+ const index = children.findIndex((child) => child.contains(to));
527
522
  if (index > -1) {
528
523
  const traversed = traverse(current, children[index]);
529
524
  return traversed == null || traversed === -1 ? -1 : distance + Math.abs(index - children.indexOf(current)) + traversed;
@@ -913,4 +908,4 @@ function updateStyleProperty(element, key, value) {
913
908
  }
914
909
  const ATTRIBUTE_DIRECTION = "dir";
915
910
  const EXPRESSION_DIRECTION = /^(ltr|rtl)$/i;
916
- export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
911
+ export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getDistance, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.115"
7
+ "@oscarpalmer/atoms": "^0.117"
8
8
  },
9
9
  "description": "A collection of badass DOM utilities.",
10
10
  "devDependencies": {
@@ -13,7 +13,7 @@
13
13
  "jsdom": "^27.3",
14
14
  "oxfmt": "^0.19",
15
15
  "oxlint": "^1.34",
16
- "rolldown": "1.0.0-beta.55",
16
+ "rolldown": "1.0.0-beta.56",
17
17
  "tslib": "^2.8",
18
18
  "typescript": "^5.9",
19
19
  "vite": "8.0.0-beta.2",
@@ -93,5 +93,5 @@
93
93
  },
94
94
  "type": "module",
95
95
  "types": "types/index.d.ts",
96
- "version": "0.31.0"
96
+ "version": "0.32.0"
97
97
  }
@@ -3,31 +3,17 @@ import type {CustomEventListener, RemovableEventListener} from '../models';
3
3
 
4
4
  //
5
5
 
6
- type DocumentWithListenerCounts = Document &
7
- Partial<{
8
- [key: string]: number;
9
- }>;
10
-
11
6
  export type EventTargetWithListeners = EventTarget &
12
7
  Partial<{
13
8
  [key: string]: Set<EventListener | CustomEventListener>;
14
9
  }>;
15
10
 
16
- function addDelegatedHandler(
17
- doc: DocumentWithListenerCounts,
18
- type: string,
19
- name: string,
20
- passive: boolean,
21
- ): void {
22
- const count = `${name}${COUNT_SUFFIX}`;
23
-
24
- if (doc[count] != null) {
25
- (doc[count] as number) += 1;
26
-
11
+ function addDelegatedHandler(doc: Document, type: string, name: string, passive: boolean): void {
12
+ if (DELEGATED.has(name)) {
27
13
  return;
28
14
  }
29
15
 
30
- doc[count] = 1;
16
+ DELEGATED.add(name);
31
17
 
32
18
  doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, {
33
19
  passive,
@@ -43,12 +29,12 @@ export function addDelegatedListener(
43
29
  ): RemovableEventListener {
44
30
  target[name] ??= new Set();
45
31
 
46
- target[name]?.add(listener);
32
+ target[name].add(listener);
47
33
 
48
- addDelegatedHandler(document as DocumentWithListenerCounts, type, name, passive);
34
+ addDelegatedHandler(document, type, name, passive);
49
35
 
50
36
  return () => {
51
- removeDelegatedListener(target, type, name, listener, passive);
37
+ removeDelegatedListener(target, name, listener);
52
38
  };
53
39
  }
54
40
 
@@ -58,9 +44,19 @@ function delegatedEventHandler(this: boolean, event: Event): void {
58
44
  const items = event.composedPath();
59
45
  const {length} = items;
60
46
 
61
- Object.defineProperty(event, 'target', {
62
- configurable: true,
63
- value: items[0],
47
+ let target = items[0];
48
+
49
+ Object.defineProperties(event, {
50
+ currentTarget: {
51
+ configurable: true,
52
+ get() {
53
+ return target;
54
+ },
55
+ },
56
+ target: {
57
+ configurable: true,
58
+ value: target,
59
+ },
64
60
  });
65
61
 
66
62
  for (let index = 0; index < length; index += 1) {
@@ -71,10 +67,7 @@ function delegatedEventHandler(this: boolean, event: Event): void {
71
67
  continue;
72
68
  }
73
69
 
74
- Object.defineProperty(event, 'currentTarget', {
75
- configurable: true,
76
- value: item,
77
- });
70
+ target = item;
78
71
 
79
72
  for (const listener of listeners) {
80
73
  (listener as EventListener).call(item, event);
@@ -102,29 +95,10 @@ export function getDelegatedName(
102
95
  }
103
96
  }
104
97
 
105
- function removeDelegatedHandler(
106
- doc: DocumentWithListenerCounts,
107
- type: string,
108
- name: string,
109
- passive: boolean,
110
- ): void {
111
- const count = `${name}${COUNT_SUFFIX}`;
112
-
113
- (doc[count] as number) -= 1;
114
-
115
- if ((doc[count] as number) < 1) {
116
- doc[count] = undefined;
117
-
118
- doc.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
119
- }
120
- }
121
-
122
98
  export function removeDelegatedListener(
123
99
  target: EventTargetWithListeners,
124
- type: string,
125
100
  name: string,
126
101
  listener: EventListener | CustomEventListener,
127
- passive: boolean,
128
102
  ): boolean {
129
103
  const handlers = target[name];
130
104
 
@@ -138,14 +112,12 @@ export function removeDelegatedListener(
138
112
  target[name] = undefined;
139
113
  }
140
114
 
141
- removeDelegatedHandler(document as DocumentWithListenerCounts, type, name, passive);
142
-
143
115
  return true;
144
116
  }
145
117
 
146
118
  //
147
119
 
148
- const COUNT_SUFFIX = '.count';
120
+ const DELEGATED = new Set<string>();
149
121
 
150
122
  const EVENT_PREFIX = '@';
151
123
 
@@ -178,6 +150,6 @@ const EVENT_TYPES: Set<string> = new Set([
178
150
  'touchstart',
179
151
  ]);
180
152
 
181
- const HANDLER_ACTIVE: EventListener = delegatedEventHandler.bind(false);
153
+ const HANDLER_ACTIVE = delegatedEventHandler.bind(false);
182
154
 
183
- const HANDLER_PASSIVE: EventListener = delegatedEventHandler.bind(true);
155
+ const HANDLER_PASSIVE = delegatedEventHandler.bind(true);
@@ -151,13 +151,7 @@ export function off(
151
151
 
152
152
  if (
153
153
  delegated == null ||
154
- !removeDelegatedListener(
155
- target as EventTargetWithListeners,
156
- type,
157
- delegated,
158
- listener,
159
- extended.passive,
160
- )
154
+ !removeDelegatedListener(target as EventTargetWithListeners, delegated, listener)
161
155
  ) {
162
156
  target.removeEventListener(type, listener as EventListener, extended);
163
157
  }
package/src/find/index.ts CHANGED
@@ -171,4 +171,4 @@ const TAG_HEAD = 'HEAD';
171
171
  //
172
172
 
173
173
  export {findElement as $, findElements as $$};
174
- export {findAncestor, findRelatives} from './relative';
174
+ export {findAncestor, findRelatives, getDistance} from './relative';
@@ -1,30 +1,3 @@
1
- /**
2
- * - Get the distance between two elements _(i.e., the amount of nodes of between them)_
3
- * - If the distance cannot be calculated, `-1` is returned
4
- */
5
- function getDistanceBetweenElements(origin: Element, target: Element): number | undefined {
6
- if (origin === target) {
7
- return 0;
8
- }
9
-
10
- if (origin.parentElement === target) {
11
- return 1;
12
- }
13
-
14
- const children = [...(origin.parentElement?.children ?? [])];
15
-
16
- if (children.includes(target)) {
17
- return Math.abs(children.indexOf(origin) - children.indexOf(target));
18
- }
19
-
20
- const comparison = origin.compareDocumentPosition(target);
21
- const beforeOrInside = Boolean(comparison & 2 || comparison & 8);
22
-
23
- if (beforeOrInside || Boolean(comparison & 4 || comparison & 16)) {
24
- return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
25
- }
26
- }
27
-
28
1
  /**
29
2
  * Find the closest ancestor element that matches the selector _(string or callback)_
30
3
  *
@@ -74,8 +47,7 @@ export function findAncestor(
74
47
  /**
75
48
  * Finds the closest elements to the origin element that matches the selector
76
49
  *
77
- * - Traverses up, down, and sideways in the _DOM_-tree
78
- * - _(If you only want to traverse up, use {@link findAncestor})_
50
+ * Traverses up, down, and sideways in the _DOM_-tree. _(If you only want to traverse up, use {@link findAncestor})_
79
51
  * @param origin Element to start from
80
52
  * @param selector Selector to match
81
53
  * @param context Context to search within
@@ -109,9 +81,9 @@ export function findRelatives(
109
81
 
110
82
  for (let index = 0; index < length; index += 1) {
111
83
  const element = elements[index];
112
- const distance = getDistanceBetweenElements(origin, element);
84
+ const distance = getDistance(origin, element);
113
85
 
114
- if (distance != null && distance > 0) {
86
+ if (distance > 0) {
115
87
  if (minimum == null || distance < minimum) {
116
88
  minimum = distance;
117
89
  }
@@ -124,17 +96,43 @@ export function findRelatives(
124
96
  }
125
97
 
126
98
  return distances
127
- .filter(found => found.distance === minimum && found.element !== origin)
99
+ .filter(found => found.distance === minimum)
128
100
  .map(found => found.element);
129
101
  }
130
102
 
131
- function traverse(from: Element, to: Element): number | undefined {
132
- let index = [...to.children].indexOf(from);
103
+ /**
104
+ * Get the distance between two elements _(i.e., the amount of nodes of between them)_
105
+ * @param origin Origin element
106
+ * @param target Target element
107
+ * @returns Distance between elements, or `-1` if distance cannot be calculated
108
+ */
109
+ export function getDistance(origin: Element, target: Element): number {
110
+ if (origin === target) {
111
+ return 0;
112
+ }
133
113
 
134
- if (index > -1) {
114
+ if (origin.parentElement === target || target.parentElement === origin) {
135
115
  return 1;
136
116
  }
137
117
 
118
+ if (origin.parentElement != null && origin.parentElement === target.parentElement) {
119
+ const children = [...origin.parentElement.children];
120
+
121
+ return Math.abs(children.indexOf(origin) - children.indexOf(target));
122
+ }
123
+
124
+ const comparison = origin.compareDocumentPosition(target);
125
+
126
+ if (comparison & Node.DOCUMENT_POSITION_DISCONNECTED) {
127
+ return -1;
128
+ }
129
+
130
+ const preceding = comparison & Node.DOCUMENT_POSITION_PRECEDING;
131
+
132
+ return traverse(preceding ? origin : target, preceding ? target : origin) ?? -1;
133
+ }
134
+
135
+ function traverse(from: Element, to: Element): number | undefined {
138
136
  let current = from;
139
137
  let distance = 0;
140
138
  let parent: Element | null = from.parentElement;
@@ -146,11 +144,11 @@ function traverse(from: Element, to: Element): number | undefined {
146
144
 
147
145
  const children = [...parent.children];
148
146
 
149
- if (children.includes(to)) {
147
+ if (to.parentElement === parent) {
150
148
  return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
151
149
  }
152
150
 
153
- index = children.findIndex(child => child.contains(to));
151
+ const index = children.findIndex(child => child.contains(to));
154
152
 
155
153
  if (index > -1) {
156
154
  const traversed = traverse(current, children[index]);
@@ -4,4 +4,4 @@ export type EventTargetWithListeners = EventTarget & Partial<{
4
4
  }>;
5
5
  export declare function addDelegatedListener(target: EventTargetWithListeners, type: string, name: string, listener: EventListener | CustomEventListener, passive: boolean): RemovableEventListener;
6
6
  export declare function getDelegatedName(target: EventTarget, type: string, options: AddEventListenerOptions): string | undefined;
7
- export declare function removeDelegatedListener(target: EventTargetWithListeners, type: string, name: string, listener: EventListener | CustomEventListener, passive: boolean): boolean;
7
+ export declare function removeDelegatedListener(target: EventTargetWithListeners, name: string, listener: EventListener | CustomEventListener): boolean;
@@ -23,4 +23,4 @@ export declare function findElements(selector: Selector, context?: Selector | nu
23
23
  */
24
24
  export declare function getElementUnderPointer(skipIgnore?: boolean): Element | null;
25
25
  export { findElement as $, findElements as $$ };
26
- export { findAncestor, findRelatives } from './relative';
26
+ export { findAncestor, findRelatives, getDistance } from './relative';
@@ -11,11 +11,17 @@ export declare function findAncestor(origin: Element, selector: string | ((eleme
11
11
  /**
12
12
  * Finds the closest elements to the origin element that matches the selector
13
13
  *
14
- * - Traverses up, down, and sideways in the _DOM_-tree
15
- * - _(If you only want to traverse up, use {@link findAncestor})_
14
+ * Traverses up, down, and sideways in the _DOM_-tree. _(If you only want to traverse up, use {@link findAncestor})_
16
15
  * @param origin Element to start from
17
16
  * @param selector Selector to match
18
17
  * @param context Context to search within
19
18
  * @returns Found elements
20
19
  */
21
20
  export declare function findRelatives(origin: Element, selector: string, context?: Document | Element): Element[];
21
+ /**
22
+ * Get the distance between two elements _(i.e., the amount of nodes of between them)_
23
+ * @param origin Origin element
24
+ * @param target Target element
25
+ * @returns Distance between elements, or `-1` if distance cannot be calculated
26
+ */
27
+ export declare function getDistance(origin: Element, target: Element): number;