@miaskiewicz/turbo-dom 0.1.14 → 0.1.16

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miaskiewicz/turbo-dom",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Faster, more spec-correct DOM for test runners — native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -344,13 +344,20 @@ export class Element extends Node {
344
344
  !(this.localName === 'input' && this.getAttribute('type') === 'hidden');
345
345
  if (!labelable) return undefined;
346
346
  const out = [];
347
+ // explicit association: <label for="thisId">
347
348
  if (this.id) {
348
349
  for (const l of this.ownerDocument.getElementsByTagName('label')) {
349
350
  if (l.getAttribute('for') === this.id) out.push(l);
350
351
  }
351
352
  }
353
+ // implicit association: an ancestor <label> — but ONLY if THIS element is
354
+ // that label's labeled control (the first labelable descendant). A second
355
+ // control nested in the same label is NOT labeled by it.
352
356
  let p = this.parentNode;
353
- while (p) { if (p.localName === 'label' && !out.includes(p)) out.push(p); p = p.parentNode; }
357
+ while (p) {
358
+ if (p.localName === 'label') { if (p.control === this && !out.includes(p)) out.push(p); break; }
359
+ p = p.parentNode;
360
+ }
354
361
  return out;
355
362
  }
356
363
  get className() { return this.getAttribute('class') || ''; }
@@ -545,7 +552,15 @@ export class Element extends Node {
545
552
  return el;
546
553
  }
547
554
 
548
- click() { this.dispatchEvent(new Event('click', { bubbles: true, cancelable: true })); }
555
+ click() {
556
+ // WHATWG "click in progress" flag: a nested .click() on the same element
557
+ // (e.g. a parent onClick that re-clicks this element on the bubble path) is
558
+ // a no-op, which breaks the otherwise-infinite re-entrancy.
559
+ if (this.__clickInProgress) return;
560
+ this.__clickInProgress = true;
561
+ try { this.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, detail: 1 })); }
562
+ finally { this.__clickInProgress = false; }
563
+ }
549
564
  // pre-click activation (runs BEFORE click listeners; undone if preventDefault)
550
565
  __preClickActivation() {
551
566
  if (this.localName !== 'input') return null;