Scenario

HTML Drag and Drop API with contenteditable

Using the HTML drag-and-drop API inside or alongside contenteditable regions often diverges from behavior on plain elements: default actions, `contenteditable` hit-testing, and `beforeinput`/`drop` ordering differ by browser. Custom editors must reconcile native DnD with their own selection model.

input
Scenario ID
scenario-drag-drop-api

Details

Using the HTML drag-and-drop API inside or alongside contenteditable regions often diverges from behavior on plain elements: default actions, contenteditable hit-testing, and beforeinput/drop ordering differ by browser. Custom editors must reconcile native DnD with their own selection model.

Problem Overview

The drag-and-drop model predates modern beforeinput and IME-aware editing. contenteditable introduces nested editable islands, shadow roots in some setups, and browser-specific default drag images for text.

Observed Behavior

  • Internal text drag does not fire events in the order your framework expects.
  • drop default handling fights preventDefault when moving text within the same editable.
  • Dragging from outside (files, HTML) interacts differently with paste pipelines.

Impact

Broken drag-to-reorder, lost selection after drop, and duplicated or missing fragments.

Browser Comparison

Chromium and Gecko differ on dragstart data transfer for selection text; WebKit has additional quirks on macOS with image drags.

Solutions

  • Use dataTransfer.setData('text/plain', ...) explicitly when building custom drags.
  • On drop, sync selection from the drop caret before applying document mutations.
  • Consider disabling native DnD for internal moves and emulating with transparent overlays only if you can match accessibility expectations.

Best Practices

  • Test drag from another app, same-page second editor, and internal paragraph reorder.

References

Scenario flow

Visual view of how this scenario connects to its concrete cases and environments. Nodes can be dragged and clicked.

React Flow mini map

Variants

Each row is a concrete case for this scenario, with a dedicated document and playground.

Case OS Device Browser Keyboard Status
ce-0083-contenteditable-with-drag-drop-api macOS 14.0 Desktop or Laptop MacBook Pro Chrome 120.0 US draft

Cases

Open a case to see the detailed description and its dedicated playground.

Related Scenarios

Other scenarios that share similar tags or category.

Tags: contenteditable, beforeinput, firefox

getTargetRanges() can reference the wrong contenteditable when editors are nested

On some engines (notably Firefox for Android / Fenix), beforeinput getTargetRanges() may describe the outer contenteditable host instead of the inner focused editor. Custom handlers that trust targetRanges alone may delete or insert in the parent surface while the user believes they are typing in a nested field.

1 case
Tags: contenteditable, chrome

Caret jumps to end after DOM manipulation in Chrome

In Chromium, programmatic DOM updates (normalization, wrapping, React reconciliation) while the user is typing can move the caret to the end of the contenteditable or to an unexpected boundary—especially when the mutation happens between keystrokes.

1 case

Comments & Discussion

Have questions, suggestions, or want to share your experience? Join the discussion below.