Chromium recreates empty span after delete when user types
OS: Windows 11 · Device: Desktop Any · Browser: Chrome 124.0 · Keyboard: US QWERTY
Open case →Scenario
After deleting an empty or inline element (e.g. span, b) inside contenteditable, typing causes the browser to recreate the deleted element, leading to unpredictable DOM and editor state.
In Chromium (and with varying behavior in other engines), when the user deletes an empty inline element such as <span> or <b> inside a contenteditable region and then types, the browser may automatically recreate that deleted inline element. This “smart” DOM modification contradicts the expectation that the editor controls the DOM; it causes state divergence in framework-based editors and complicates normalization logic.
<span> or <b> with no text), then types a character.Example DOM before/after:
<!-- Before: user deletes the empty <span> and types "x" -->
<div contenteditable="true">hello <span></span> world</div>
<!-- Chromium may produce -->
<div contenteditable="true">hello <span>x</span> world</div>
input or beforeinput, walk the DOM and remove or merge redundant inline wrappers that the editor did not create.<span> or <b> etc.; use text nodes or placeholders (e.g. \u200B) so the node is not “empty” from the engine’s perspective.beforeinput with inputType deleteContentBackward/deleteContentForward to apply your own DOM change and preventDefault() so the browser does not perform its default delete (and subsequent recreation) when feasible.Example normalization (remove empty inlines):
editor.addEventListener('input', () => {
const emptyInlines = editor.querySelectorAll('span:empty, b:empty, i:empty');
emptyInlines.forEach(el => {
const parent = el.parentNode;
while (el.firstChild) parent.insertBefore(el.firstChild, el);
el.remove();
});
});
Visual view of how this scenario connects to its concrete cases and environments. Nodes can be dragged and clicked.
Each row is a concrete case for this scenario, with a dedicated document and playground.
| Case | OS | Device | Browser | Keyboard | Status |
|---|---|---|---|---|---|
| ce-0582 | Windows 11 | Desktop Any | Chrome 124.0 | US QWERTY | draft |
Open a case to see the detailed description and its dedicated playground.
OS: Windows 11 · Device: Desktop Any · Browser: Chrome 124.0 · Keyboard: US QWERTY
Open case →Other scenarios that share similar tags or category.
During editing operations, empty elements (empty paragraphs, divs, spans with no content) accumulate in the DOM. These elements can cause layout issues, make the HTML bloated, and create unexpected behavior. Browsers handle empty element cleanup inconsistently.
The document.execCommand() API, which is commonly used to apply formatting (bold, italic, etc.) in contenteditable regions, has been deprecated. However, there is no complete replacement, and many implementations still rely on it. This creates uncertainty about future browser support.
Deleting images from contenteditable elements behaves differently across browsers. Some browsers delete the image cleanly, while others may leave empty elements, break the DOM structure, or require multiple delete operations.
When using document.execCommand('insertHTML', ...) to insert HTML content into a contenteditable region, the DOM structure may be broken or reformatted unexpectedly. Nested elements may be flattened or reorganized.
When pressing Backspace or Delete at the beginning or end of a list item, the behavior varies significantly across browsers. Some browsers delete the list item and merge with adjacent content, while others may delete the entire list or create unexpected DOM structures.
Have questions, suggestions, or want to share your experience? Join the discussion below.