Skip to content

Guía

EffDND añade la función de arrastrar y soltar a tu HTML con solo atributos. Sin llamadas a JavaScript ni configuración adicional más allá de una simple importación. Esta guía explica qué puedes hacer con él y cómo.

Primeros pasos

sh
npm i effdnd

Importa una sola vez en tu aplicación:

ts
import 'effdnd';            // la biblioteca
import 'effdnd/style.css';  // opcional: estilos predeterminados

Eso es todo. Cualquier elemento marcado con los atributos que se indican a continuación se puede arrastrar inmediatamente, incluso los elementos añadidos posteriormente a la página.

Frameworks: funciona con cualquiera de ellos. No se necesita ningún componente contenedor.

Cómo funciona

EffDND se basa en una idea central, y todo lo demás en esta guía es una extensión de ella:

  • Solo un trigger inicia el arrastre. Ningún elemento se puede arrastrar por sí solo. Se agarra un punto de control marcado con data-dnd-trigger y comienza el arrastre; sin él, nada se mueve.
  • El item es lo que se mueve. El trigger suele estar dentro del elemento, por lo que todo el elemento sigue el puntero, pero no tienen por qué ser el mismo nodo.
  • El scope es el área de interacción. El item solo puede moverse y soltarse dentro de su scope. Las zonas de suelta o los contenedores fuera de él simplemente se ignoran.

Este es el caso base: un trigger en un item dentro de un scope.

html
<div data-dnd-scope="board">              <!-- ámbito: el entorno aislado -->
  <div data-dnd-item="a">                 <!-- elemento: lo que se mueve -->
    <span data-dnd-trigger>⠿</span>       <!-- disparador: lo que agarras -->
      Agárrame
  </div>
</div>

Cada una de las funciones que se describen a continuación —reordenar, transferir, desplazarse o los parámetros de activación que aparecen más abajo— solo refina o amplía este comportamiento básico.

Estilo al arrastrar. El elemento que ve moverse es un clon (una copia) del item, insertado en el propio padre del elemento. Esto significa que los estilos en cascada y heredados, y las reglas escritas para los selectores padre (ul > li, .zone .card, …), siguen aplicándose al clon en movimiento; no es necesario aplicar estilos en línea a cada color. El elemento original permanece en su lugar en la lista y se atenúa mediante el estado passive (consulte Estilo más abajo). Advertencia: elementos ancestros transformados. El clon tiene position:fixed, por lo que está desvinculado del diseño. Pero si algún antecesor del elemento tiene transform, filter, perspective (o will-change:transform), el navegador trata fixed como absolute: el clon se posiciona entonces en relación con ese antecesor y puede aparecer desplazado (y se desplazará con un contenedor transformado desplazable). Evite los antecesores transformados de los elementos arrastrados o anule los estilos visuales fijos mediante data-dnd-state y los estilos en línea que aplique al elemento.

Atributos de un vistazo

AtributoQué hace
data-dnd-triggerMarca el "controlador" desde el que el usuario arrastra
data-dnd-itemMarca el elemento que se arrastra
data-dnd-scopeLimita los destinos de arrastre y suelta a un solo contenedor
data-dnd-reorderConvierte un contenedor en una lista ordenable (x o y)
data-dnd-targetMarca una zona de suelta
data-dnd-transferAsigna una acción a una zona de suelta: append, prepend o remove
data-dnd-scrollDesplaza automáticamente el contenedor mientras se arrastra
data-dnd-transitionSuaviza la animación de movimiento
data-dnd-disabledDeshabilita un activador
data-dnd-stateEstado de ejecución (activo/pasivo) que se establece durante el arrastre — se usa para el estilo

Solo se requieren tres de ellos para el caso más básico: un identificador (activador) en un elemento (item) dentro de un contenedor (scope). Todo lo demás es opcional y añade funcionalidad.

Parámetros de Trigger

Dado que el parámetro trigger es el punto de entrada, es el atributo que se ajusta con mayor frecuencia. Acepta una lista de parámetros separados por punto y coma:

html
<span data-dnd-trigger="dist:12;axis:y;scope:board;item:task-1">⠿</span>

Requiere una distancia de arrastre (dist).

Evita arrastres accidentales al hacer clic: solo se arrastra después de que el puntero se mueva el número de píxeles especificado.

html
<span data-dnd-trigger="dist:12">⠿</span>   <!-- solo se arrastra después de que el puntero se mueva 12px -->

Bloquear a un eje (axis)

html
<span data-dnd-trigger="axis:x">⠿</span>   <!-- horizontal solamente -->
<span data-dnd-trigger="axis:y">⠿</span>   <!-- vertical solamente -->

Acceda a un contenedor específico (scope).

Por defecto, EffDND encuentra el scope más cercano consultando el DOM. Si necesita uno específico, asígnele un nombre:

html
<span data-dnd-trigger="scope:board">⠿</span>

Dirigirse a un elemento específico (item)

El activador puede especificar qué elemento específico de arriba se debe mover:

html
<span data-dnd-trigger="scope:board;item:task-1">⠿</span>

Restringir los destinos de lanzamiento (target)

Permitir solo lanzar objetos en zonas cuyo data-dnd-target comience con el nombre especificado:

html
<span data-dnd-trigger="scope:board;target:drop">⠿</span>

Muévase libremente por toda la página (scope:*)

El valor especial scope:* ignora por completo cualquier límite de scope:

html
<span data-dnd-trigger="scope:*">⠿</span>

El elemento se puede arrastrar a cualquier parte de la página.

Desactivar un activador

html
<span data-dnd-trigger data-dnd-disabled>⠿</span>

Elimina el atributo para volver a habilitarlo.

Escenarios típicos

Estas son las formas más comunes en que los componentes se combinan en interfaces reales.

Reordenar: una lista ordenable

Envuelve tus elementos en una lista con data-dnd-reorder="y", marca cada elemento y añade un identificador.

html
<ul data-dnd-scope="todo" data-dnd-reorder="y">
  <li data-dnd-item="1"><span data-dnd-trigger>⠿</span>Buy milk</li>
  <li data-dnd-item="2"><span data-dnd-trigger>⠿</span>Read a book</li>
</ul>
  • y ordena verticalmente, x ordena horizontalmente.

  • Solo se reordenan los elementos que son hijos directos de la lista.

Transferencia: mover elementos entre contenedores

Marque un contenedor como zona de destino y asígnele la acción de transferencia.

html
<div data-dnd-scope="sprint">
  <div class="zone" data-dnd-target="backlog" data-dnd-transfer="append">Backlog</div>
  <div class="zone" data-dnd-target="done" data-dnd-transfer="prepend">Done</div>
</div>
  • append: el elemento se añade al final de la zona.
  • prepend: el elemento se añade al inicio de la zona.
  • remove: el elemento se elimina (ideal para una papelera).

Las zonas de destino deben estar dentro del mismo scope (o usar scope:*, ver más arriba).

Combinar: una lista ordenable con una papelera

html
<div data-dnd-scope="mailbox">
  <ul data-dnd-reorder="y">
    <li data-dnd-item="1"><span data-dnd-trigger>⠿</span>Invoice</li>
    <li data-dnd-item="2"><span data-dnd-trigger>⠿</span>Newsletter</li>
  </ul>
  <button data-dnd-target="trash" data-dnd-transfer="remove">Delete</button>
</div>

Aquí la lista se ordena sola y las filas se pueden descartar soltándolas en el botón.

Kanban: columnas y tarjetas

Dale al tablero data-dnd-reorder="x" para ordenar las columnas, y el área de la tarjeta de cada columna es una zona de reorden vertical y un receptor de transferencia.

html
<div data-dnd-scope="kanban" data-dnd-reorder="x">          <!-- ordena las columnas -->
  <div class="col" data-dnd-item="col-1">
    <div class="col-head"><span data-dnd-trigger>⠿</span>Backlog</div>
    <div class="cards" data-dnd-reorder="y"
         data-dnd-target="col-1" data-dnd-transfer="append"> <!-- clasifica y acepta tarjetas -->
      <div class="card" data-dnd-item="task-1"><span data-dnd-trigger>⠿</span>Write spec</div>
    </div>
  </div>
  <!-- más columnas ... -->
</div>

Arrastra el encabezado de una columna para reordenar las columnas; arrastra una tarjeta para moverla entre columnas.

Ajuste de comportamientos comunes

Desplazamiento automático de un contenedor largo

Asigne data-dnd-scroll al elemento desplazable:

html
<div data-dnd-scroll="threshold:50;speed:12">
  • umbral (valor predeterminado: 30): indica la proximidad al borde a la que comienza el desplazamiento, en píxeles.
  • velocidad (valor predeterminado: 10): indica la velocidad de desplazamiento, en píxeles por fotograma.

Al arrastrar cerca del borde, el contenedor se desplaza, y la velocidad de desplazamiento aumenta a medida que te acercas al borde.

Suaviza la animación

html
<div data-dnd-transition="150ms ease">…</div>

El valor predeterminado es 100 ms lineal.

Reacción a los arrastres desde JavaScript

Aún puedes hacerlo; EffDND simplemente lo hace opcional. Cada función a continuación devuelve una función para cancelar la suscripción:

ts
import { onDrag, onDrop, onReorder, onTransfer, onDragStart, onDragEnd } from 'effdnd';

onReorder((event) => {
  console.log('Item was reordered:', event.detail.keys.item);
});

Eventos disponibles: effdragstart, effdrag, effdragend, effdragenter, effdragleave, effdrop, effreorder, efftransfer.

Cada evento proporciona un objeto detail en el que puede confiar:

ts
event.detail.keys.item;          // qué elemento
event.detail.keys.scope;         // en qué ámbito
event.detail.keys.target;        // sobre qué objetivo
event.detail.item;

Ayudantes que pueden resultarle útiles

ts
import { getItem, getScope, getTargets, getReorderContainer, reset } from 'effdnd';

getItem(trigger);           // el `item` al que pertenece un elemento
getScope(trigger);          // el ámbito al que pertenece un elemento
getTargets(trigger);        // las zonas de lanzamiento disponibles para un activador
reset(item);                // Devuelve el objeto a su posición original.

Estilo con data-dnd-state

Durante un arrastre, EffDND etiqueta los elementos involucrados con el atributo data-dnd-state en tiempo de ejecución. Esto proporciona un gancho sencillo para aplicar estilos al arrastre activo, sin necesidad de JavaScript en línea. No hay data-dnd-state cuando no se está arrastrando nada.

Valores de estado

ElementoEstadoSignificado
data-dnd-itemactiveEl clon en movimiento: el elemento que sigue actualmente al puntero
data-dnd-itempassiveEl elemento original que permanece en su lugar, atenuado detrás del clon
data-dnd-scopeactiveEl puntero está fuera del ámbito
data-dnd-scopepassiveEl puntero está dentro del ámbito: normal al arrastrar
data-dnd-targetactiveLa zona de destino sobre la que se encuentra el cursor
data-dnd-targetpassiveUna zona de destino válida, lista para recibir un objeto

El archivo index.css incluido ya contiene valores predeterminados adecuados basados ​​en estos selectores, y se utilizan los mismos selectores para anularlos:

css
/* Valores predeterminados de index.css */
[data-dnd-item][data-dnd-state="passive"] { opacity: 0.2; }
[data-dnd-item][data-dnd-state="active"]  { opacity: 0.75; z-index: 1000; }

/* Tu propio tema */
[data-dnd-item][data-dnd-state="passive"] { opacity: 0.08; }
[data-dnd-item][data-dnd-state="active"]  {
    opacity: 1;
    box-shadow: 0 0 0 2px cornflowerblue;
    border-radius: 8px;
}
[data-dnd-scope][data-dnd-state="active"] { outline: 2px dashed tomato; }
[data-dnd-target][data-dnd-state="active"] { background: rgba(100, 200, 255, 0.2); }

Dado que los valores predeterminados se encuentran en index.css, cargarlos es opcional: si omite esa importación y escribe sus propias reglas [data-dnd-state="…"], EffDND permanece totalmente libre de dependencias y usted mantiene un control visual completo.

Consejo: El elemento original se atenúa mediante opacity, no se elimina, por lo que la lista conserva su diseño y

los elementos hermanos no se desplazan al arrastrar. Si desea que el elemento original desaparezca por completo, establezca opacity: 0 (o visibility: hidden) para el estado passive.

Minireferencia

Una versión condensada de cada atributo y sus parámetros.

Atributo / parámetroValoresPredeterminadoFinalidad
data-dnd-triggerMarca el punto de inicio del arrastre
… distnúmeroArrastra solo después de que el puntero se mueva esta cantidad de píxeles
… axisx / yambosBloquea el arrastre a un eje
… scopenombre / *más cercano en el DOMUsa un contenedor específico (o ignora los ámbitos)
… itemnombremás cercano en el DOMArrastra un elemento específico
… targetnombretodosSolo permite soltar en objetivos que coincidan con un nombre
data-dnd-disabledDeshabilita un activador
data-dnd-itemnombre únicoMarca el elemento que se arrastra
data-dnd-scopenombre únicoLimita los arrastres/soltados a un contenedor
data-dnd-reorderx / yConvierte un contenedor en una lista ordenable
data-dnd-targetnombreMarca una zona de destino
data-dnd-transferappend / prepend / removeAcción al soltar en una zona
data-dnd-scrollthreshold;speed30;10Desplaza automáticamente el contenedor mientras se arrastra
data-dnd-transitionduración y suavizado100ms linealSuaviza la animación de movimiento
data-dnd-stateactive / passiveEstado de arrastre en tiempo de ejecución, con estilo aplicado mediante CSS (ver Estilo)

Publicado bajo la Licencia Apache 2.0