Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

DOMException Crash in Caret Split via Premature deleteContents()

Aperta
#3,031 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
72/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
typescript
Ambito
frontend

Direzione di ricerca

Start in src/components/modules/caret.ts, especially Caret.extractFragmentFromCaretPosition() around lines 197–233, and reproduce the formatted paragraph split described in the issue. Trace the selection and range boundaries through the native-input and contenteditable paths. Done means pressing Enter across the formatting boundary no longer throws a DOMException and the block splits correctly.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

3. DOMException Crash in Caret Split via Premature deleteContents()

📍 Affected Locations
  • src/components/modules/caret.ts (Lines 197–233)
🔍 Deep Technical Diagnosis

When pressing Enter to split a block, Caret.extractFragmentFromCaretPosition() is called to split content from the caret to the end of the block.

// src/components/modules/caret.ts: Lines 200-231
if (selection.rangeCount) {
  const selectRange = selection.getRangeAt(0);
  const currentBlockInput = this.Editor.BlockManager.currentBlock.currentInput;

  selectRange.deleteContents(); // <-- BUG: Mutates DOM before boundary inspection!

  if (currentBlockInput) {
    if ($.isNativeInput(currentBlockInput)) {
      // ...
    } else {
      const range = selectRange.cloneRange();

      range.selectNodeContents(currentBlockInput);
      range.setStart(selectRange.endContainer, selectRange.endOffset); // <-- CRASH!

      return range.extractContents();
    }
  }
}
Why It Crashes:
  1. selectRange.deleteContents() deletes the selected nodes from the document tree.
  2. In browsers (especially Chrome/WebKit), deleting nodes collapses text nodes and can completely remove the selectRange.endContainer node from the DOM, or reduce its character length below selectRange.endOffset.
  3. When range.setStart(selectRange.endContainer, selectRange.endOffset) runs immediately after:
    • If endContainer is no longer inside currentBlockInput, the browser throws:
      DOMException: Failed to execute 'setStart' on 'Range': The node provided is not a child of this node.
    • If endOffset exceeds the remaining text length, the browser throws:
      DOMException: Failed to execute 'setStart' on 'Range': The offset is larger than the node's length.
  4. This completely halts the block-splitting operation and leaves the editor in a corrupt, uneditable state.
🧪 Reproduction Steps
  1. Insert a paragraph with formatting: Hello <b>world and extra</b> text.
  2. Select text spanning across the tag boundary: e.g., select from world to extra.
  3. Press Enter.
  4. Observed: Console throws uncaught DOMException: Failed to execute 'setStart' on 'Range'. Block does not split.
💡 Proposed Solution & Patch

Clone the boundary points or collapse the range cleanly before extracting contents:

src/components/modules/caret.ts
@@ -201,8 +201,6 @@ export default class Caret extends Module {
       const selectRange = selection.getRangeAt(0);
       const currentBlockInput = this.Editor.BlockManager.currentBlock.currentInput;

-      selectRange.deleteContents();
-
       if (currentBlockInput) {
         if ($.isNativeInput(currentBlockInput)) {
           /**
@@ -213,6 +211,7 @@ export default class Caret extends Module {
           const input = currentBlockInput as HTMLInputElement | HTMLTextAreaElement;
           const newFragment = document.createDocumentFragment();

+          selectRange.deleteContents();
           const inputRemainingText = input.value.substring(0, input.selectionStart);
           const fragmentText = input.value.substring(input.selectionStart);

@@ -222,8 +221,11 @@ export default class Caret extends Module {
           return newFragment;
         } else {
-          const range = selectRange.cloneRange();
+          selectRange.deleteContents();
+          const range = document.createRange();

           range.selectNodeContents(currentBlockInput);
-          range.setStart(selectRange.endContainer, selectRange.endOffset);
+          const safeSelection = Selection.get();
+          const anchorRange = safeSelection.rangeCount ? safeSelection.getRangeAt(0) : selectRange;
+          range.setStart(anchorRange.startContainer, anchorRange.startOffset);

           return range.extractContents();
         }

Lingua principale
TypeScript
Stelle
32k
Fork
2.2k
Merge medio
8h 45m
PR unite (30g)
1

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di codex-team/editor.js

Tutte le issue di codex-team/editor.js

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.