October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

How to Implement Drag and Drop in JavaScript

A practical guide to native HTML drag and drop in JavaScript, including event handling, validated payloads, state updates, non-drag alternatives and common fixes.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To implement native HTML drag and drop, mark the source with draggable="true", put a stable identifier in event.dataTransfer during dragstart, cancel dragover on destinations that accept drops, and read and validate the identifier in drop. Your code—not the browser—must then update the application state. For reordering, also provide a way to achieve the same result without dragging.

Build a basic native drag-and-drop interaction

This example moves a task from a source list into a destination list. It transfers an ID, not an element or JavaScript object, and updates an array as the source of truth before rendering the result.

<ul id="source-list">
  <li draggable="true" data-id="task-42">Prepare release notes</li>
</ul>

<ul id="destination-list" aria-label="Destination tasks"></ul>

<script>
  const sourceList = document.querySelector('#source-list');
  const destinationList = document.querySelector('#destination-list');
  const transferType = 'application/x-example-task-id';

  // In a real application, populate and update this state through your app's
  // normal state-management and rendering code.
  const tasks = new Map([
    ['task-42', { id: 'task-42', title: 'Prepare release notes', list: 'source' }]
  ]);

  sourceList.addEventListener('dragstart', (event) => {
    const item = event.target.closest('[draggable="true"][data-id]');
    if (!item || !sourceList.contains(item)) return;

    event.dataTransfer.setData(transferType, item.dataset.id);
    event.dataTransfer.effectAllowed = 'move';
  });

  destinationList.addEventListener('dragover', (event) => {
    // Cancel the default so this list accepts a drop.
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  });

  destinationList.addEventListener('drop', (event) => {
    event.preventDefault();

    const id = event.dataTransfer.getData(transferType);
    const task = tasks.get(id);
    if (!task || task.list !== 'source') return;

    // Apply your app's destination rules here before changing state.
    task.list = 'destination';
    render();
  });

  function render() {
    sourceList.replaceChildren();
    destinationList.replaceChildren();

    for (const task of tasks.values()) {
      const item = document.createElement('li');
      item.textContent = task.title;
      item.dataset.id = task.id;
      item.draggable = true;
      (task.list === 'source' ? sourceList : destinationList).append(item);
    }
  }
</script>

The script uses event delegation on the source list, so it continues to handle items created by render(). In a production app, reconcile the example map with your actual state store and rendering framework rather than maintaining a separate copy.

Understand the drag event lifecycle

A typical native drag proceeds through dragstart on the source, dragenter and dragleave as it crosses elements, repeated dragover events at the current target, drop at an accepted destination, and dragend at the source. The browser suppresses ordinary device input events during a native drag, so mouse or keyboard handlers do not replace this lifecycle. See the MDN drag operations guide and the WHATWG HTML Living Standard.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • dragstart: choose the payload and allowed operation. It is also a suitable place to mark the source visually.
  • dragenter and dragleave: optionally show feedback when a pointer enters or leaves a target.
  • dragover: call preventDefault() on a valid target so it can receive a drop; set dropEffect to communicate the intended operation.
  • drop: prevent the default when appropriate, inspect the expected payload type, validate it and the destination, then update application state.
  • dragend: clear temporary styling or drag state whether the operation succeeded or was canceled.

Most elements do not accept drops by default. Although the specification says dragenter should also be canceled, MDN notes that browsers do not implement that requirement in practice; canceling dragover is the practical step that makes a target accept a drop. Consult the MDN event guidance.

Transfer data safely and update state deliberately

DataTransfer carries text or files; it does not pass arbitrary JavaScript object references between pages or applications. Use a type-tagged string, stable ID, or serialized value, then validate it in the drop handler. The MDN HTML Drag and Drop API reference describes the data-transfer model.

  • Use a stable record ID rather than a display label, array index, or DOM node. Labels can change, and indexes can become stale when items are reordered.
  • Check that the ID resolves to an item in current application state. Treat missing, malformed, or unexpected values as invalid.
  • Check that the destination is allowed for that item and operation. A canceled drag or invalid drop must not partially change state.
  • Render from the updated state. Moving a DOM node alone can leave the interface inconsistent with the data model or be undone by a later render.
  • Use a custom MIME-style type such as application/x-example-task-id to distinguish your payload from unrelated text or files.

For a sortable list, the destination may need to identify a position as well as a container. Compute the insertion point from the target item and pointer position, validate it, and update the ordered data structure; the native API does not choose or persist your application’s ordering rules.

Make the action accessible without dragging

Do not make dragging the only way to operate a feature. WCAG 2.2 Success Criterion 2.5.7 requires that functionality using a dragging movement can also be achieved by a single pointer without dragging, unless dragging is essential or the functionality is determined by the user agent and not modified by the author. The criterion is in the WCAG 2.2 specification; its rationale and examples are explained in WAI’s Understanding Dragging Movements.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keyboard access and a single-pointer alternative are separate needs: adding keyboard controls alone does not meet the single-pointer requirement. For reordering, offer controls such as Move up and Move down, or let a user select an item and then choose its destination. WAI’s G219 technique describes these kinds of alternatives.

  • Make the alternative action discoverable and label its result clearly.
  • After a move, keep focus in a useful place, such as on the moved item or its relevant control.
  • Expose the changed order or destination to assistive technology users; do not rely on color or position alone to communicate success.
  • Test the drag interaction and its non-drag alternative independently, including keyboard operation where the feature supports it.

Choose native drag and drop or a custom interaction

Native HTML drag and drop is a direct fit for browser-native file drops and can also support in-page moves. For sortable lists and boards, decide based on the intended devices and the control you need over behavior—not on an assumption that one approach is universally best.

Decision What to consider
Dropping files into a page Use the native file-drop flow when users are bringing files into the browser page, and implement the file handling your interface needs.
Mouse and touch behavior Check that the chosen interaction works for the devices and input methods your users need. If you implement pointer-driven behavior, account for touch as well as mouse rather than assuming native drag events cover every desired interaction.
Keyboard and single-pointer access Provide keyboard operation where needed and a separate single-pointer route that does not require dragging, as required by WCAG 2.2 SC 2.5.7 unless an exception applies.
State and drop placement For a board or sortable list, decide how the target position is identified, validated, and committed to application state.
Feedback and control Choose the implementation that gives you the feedback, cancellation, and drop-position control your interaction requires.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

  • The target never receives a useful drop: confirm that the intended target handles dragover and calls preventDefault() for allowed drops.
  • The payload is empty: confirm the source is draggable, setData() runs in dragstart, and the type passed to getData() exactly matches the type passed to setData().
  • The wrong item moves: transfer a stable ID and look it up in current application state instead of relying on text or an old array index.
  • A drop moves an item into an invalid location: validate the item, operation, and destination before changing state.
  • The UI changes but resets on the next render: update the application’s source-of-truth data and render from it instead of only moving DOM nodes.
  • Temporary drag styling remains after cancellation: remove it in dragend, which fires whether the drag succeeded or was canceled.

Or skip the browser setup

If your goal is to capture a page screenshot rather than implement an in-page drag interaction, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. For example, save a WebP screenshot with cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. Cookie banners are accepted and removed before capture, along with supported newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Sign up for ScreenshotNeo and get 1,000 free screenshots a month, no card required.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.