Skip to content
sang.id.vn
Go back

The float and fixed Positioning Trap in contenteditable

Tiếng Việt

I ran into these issues while building a rich text editor. Both float and position: fixed inside contenteditable break in ways that aren’t immediately obvious.

float Inside contenteditable

Put a floated image inside a contenteditable div:

<div contenteditable="true">
  <img src="photo.jpg" style="float: left; margin: 0 12px 12px 0; width: 150px" />
  <p>Text that should wrap around the image...</p>
</div>

This breaks in several ways: the caret can’t be placed correctly near the float, selection highlights are unpredictable, Backspace may delete or skip the image, and Enter creates empty elements that stack around it.

Fix: Wrap the floated element in contenteditable="false":

<div contenteditable="true">
  <div contenteditable="false" style="float: left; margin: 0 12px 12px 0">
    <img src="photo.jpg" style="width: 150px" />
  </div>
  <p>Text wraps around the image, but the image is a non-editable island.</p>
</div>

Or use CSS Grid instead of float:

<div contenteditable="true">
  <div style="display: grid; grid-template-columns: 150px 1fr; gap: 12px">
    <img src="photo.jpg" style="width: 150px" contenteditable="false" />
    <div><p>Editable text beside the image.</p></div>
  </div>
</div>

position: fixed Inside contenteditable

Placing a toolbar inside a contenteditable div:

<div contenteditable="true" style="min-height: 500px">
  <div style="position: fixed; top: 0; background: #fff; padding: 8px; z-index: 10">
    <button onmousedown="event.preventDefault()" onclick="document.execCommand('bold')">B</button>
    <button onmousedown="event.preventDefault()" onclick="document.execCommand('italic')">I</button>
  </div>
  <p style="margin-top: 50px">Editable content here...</p>
</div>

The toolbar becomes editable — users can type into or delete toolbar buttons. Clicking buttons may also steal focus from the editor.

Fix: Move the toolbar outside as a sibling:

<div style="position: fixed; top: 0; background: #fff; padding: 8px; z-index: 10">
  <button onmousedown="event.preventDefault()" onclick="document.execCommand('bold')">B</button>
  <button onmousedown="event.preventDefault()" onclick="document.execCommand('italic')">I</button>
</div>

<div contenteditable="true" style="margin-top: 50px; min-height: 500px">
  <p>Editable content here...</p>
</div>

onmousedown="event.preventDefault()" on buttons is important — it prevents the click from stealing focus, so the editor’s text selection is preserved when execCommand runs.


Share this post on:

Previous Post
Nested contenteditable: Using Shadow DOM to Transfer the Editing Host
Next Post
When You Actually Need capture: true in addEventListener