Mastering Drag‑And‑Drop: From HTML5 APIs to Moveable’s Custom Able Extensions

This article explores browser drag‑and‑drop capabilities, compares native HTML5 DnD, mouse/touch events, and Canvas approaches, reviews popular libraries such as React DnD, Moveable and Interact.js, and provides a step‑by‑step guide to extending Moveable with custom ables for intelligent placement.

MoonWebTeam
MoonWebTeam
MoonWebTeam
Mastering Drag‑And‑Drop: From HTML5 APIs to Moveable’s Custom Able Extensions

1. Introduction

Low‑code editors consist of a material system, configuration forms, and component composition. The core capability is drag‑and‑drop, which provides a visual, WYSIWYG interaction, reduces learning cost, and improves user satisfaction.

Drag‑and‑drop also applies to sorting, file upload, and logical composition.

This article reviews browser drag‑and‑drop APIs, compares common libraries, examines the Moveable library, and demonstrates how to create a custom Moveable able.

2. Implementing Drag in the Browser

2.1 Browser implementation

Some elements are natively draggable (selected text, images, links) and some are droppable (input fields).

To customize draggable elements you can use the HTML5 Drag and Drop API.

HTML5 Drag and Drop API

The DnD API provides native support for dragging elements and interacting with the operating system.

It defines two concepts: drag source and drop target.

Key events:

dragstart : triggered when dragging starts.

drag : continuously triggered while dragging.

dragenter : when the dragged element enters a drop target.

dragleave : when the dragged element leaves a drop target.

dragover : continuously triggered while over a drop target; default prevents dropping unless prevented.

drop : when the element is dropped on a target.

dragend : when the drag operation ends.

Using mouse events

Another approach is to listen to mouseenter, mousedown, mouseup, etc., which offers flexibility but requires more logic.

On mobile, touchstart, touchmove, touchend are used, and multi‑finger gestures enable complex interactions.

Using Canvas

Canvas can implement drag by handling mouse events, suitable for high‑performance or highly custom scenarios such as online design tools.

2.3 Common drag libraries

We list several popular libraries.

React DnD

React DnD builds on the HTML5 DnD API and provides higher‑order components. It requires a backend such as react‑dnd‑html5‑backend or react‑dnd‑touch‑backend.

function DragBox() {
  const [{isDragging}, dragRef] = useDrag({
    type,
    item: {type},
    collect(monitor) {
      return {
        // whether it is dragging
        isDragging: monitor.isDragging(),
      };
    },
  });
  return (
    <>
      <div className={classnames({
        drag: true,
        dragging: isDragging,
      })} ref={dragRef}></div>
    </>
  );
}

React DnD suits most drag scenarios such as sorting and low‑code component dragging.

moveable

Moveable focuses on element manipulation: move, scale, rotate, and warp.

It listens to mouse and touch events, calculates transformation properties, and is suitable for design tools and graphic editors.

Interact.js

Interact.js wraps native events into a unified API for multi‑device interaction, ideal for mobile scenarios.

// declare drop target
interact('.dropzone').dropzone({
  // declare drag source
  accept: '#yes-drop',
  // overlap ratio
  overlap: 0.75,
  ondropactivate: function (event) {
    event.target.classList.add('drop-active');
  },
  ondragenter: function (event) {
    var draggableElement = event.relatedTarget;
    var dropzoneElement = event.target;
    dropzoneElement.classList.add('drop-target');
    draggableElement.classList.add('can-drop');
    draggableElement.textContent = 'Dragged in';
  },
  ondragleave: function (event) {
    event.target.classList.remove('drop-target');
    event.relatedTarget.classList.remove('can-drop');
    event.relatedTarget.textContent = 'Dragged out';
  },
  ondrop: function (event) {
    event.relatedTarget.textContent = 'Dropped';
  },
  ondropdeactivate: function (event) {
    event.target.classList.remove('drop-active');
    event.target.classList.remove('drop-target');
  }
});

Interact.js provides unified handling of touch, mouse, and pointer events.

3. Moveable library overview

Moveable supports multiple UI frameworks; react‑moveable is the core implementation.

Preact support is provided via type definitions without extra code.

import Moveable from "react-moveable";
import Preact from "preact";
import { PreactMoveableInterface } from "./types";

export default Moveable as any as new (...args: any[]) => PreactMoveableInterface;

Moveable uses the croact library (a lightweight React‑like renderer) to render in non‑React environments.

Events are provided through native mouseEnter/mouseLeave and Gesto‑wrapped events such as dragStart, drag, dragEnd, pinchStart, pinch, pinchEnd.

By handling target and control element events, Moveable can implement draggable, scalable, and resizable abilities.

3.2 Extending abilities

Custom abilities (ables) are defined by a name and a render function that returns a React node.

const CustomAble = {
  name: 'customeAble',
  render() {
    return <div className="moveable-custom-able"></div>;
  }
};

For example, a “snappableSizeAble” can highlight a rectangular area and snap the target element into that area on drop.

interface SnapSizeRect {
  top: number;
  left: number;
  width: number;
  height: number;
}
export const SnappableSizeAble = {
  name: 'snappableSizeAble',
  props: ['snapSizeRect'],
  render(moveable) {
    const sizeRect = moveable.props.snapSizeRect;
    const style = {
      transform: `translate(${-moveable.state.left}px, ${-moveable.state.top}px)`,
      position: 'fixed',
      top: `${sizeRect.top}px`,
      left: `${sizeRect.left}px`,
      width: `${sizeRect.width}px`,
      height: `${sizeRect.height}px`,
      backgroundColor: moveable.hitTest(sizeRect) ? 'rgba(0,115,255,0.146)' : 'transparent',
    };
    return <div key="snappable-size" className="moveable-snappable-size" style={style}></div>;
  },
  dragEnd(moveable) {
    const sizeRect = moveable.props.snapSizeRect;
    if (!moveable.hitTest(sizeRect)) return;
    const target = moveable.getTargets()[0];
    target.style.width = `${sizeRect.width}px`;
    target.style.height = `${sizeRect.height}px`;
    target.style.transform = `translate(${sizeRect.left}px, ${sizeRect.top}px)`;
    moveable.updateRect();
  },
};

The ability can be used together with draggable and scalable options to create intelligent placement.

4. Summary

Browser drag‑and‑drop is widely used in low‑code editors, logic‑orchestration systems, and various C‑end interactions. This article introduced several drag libraries, explained Moveable’s internals, and showed how to extend it with custom ables.

ReActDrag and Dropcustom-ablesmoveable
MoonWebTeam
Written by

MoonWebTeam

Official account of MoonWebTeam. All members are former front‑end engineers from Tencent, and the account shares valuable team tech insights, reflections, and other information.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.