En esta publicación, se explican los conceptos básicos de arrastrar y soltar.
Cómo crear contenido arrastrable
En la mayoría de los navegadores, las selecciones de texto, las imágenes y los vínculos son arrastrables de forma predeterminada. Por ejemplo, si arrastras un vínculo en una página web, verás un pequeño cuadro con un título y una URL que puedes soltar en la barra de direcciones o en el escritorio para crear un acceso directo o navegar al vínculo. Para que otros tipos de contenido sean arrastrables, debes usar las APIs de arrastrar y soltar de HTML5.
Para hacer que un objeto sea arrastrable, configura draggable=true
en ese elemento. Se puede habilitar casi cualquier acción para arrastrar, por ejemplo, imágenes, archivos, vínculos, archivos o cualquier lenguaje de marcado de tu página.
En el siguiente ejemplo, se crea una interfaz para reorganizar las columnas que se presentaron con la cuadrícula de CSS. El lenguaje de marcado básico para las columnas tiene el siguiente aspecto, con el atributo draggable
de cada columna establecido en true
:
<div class="container">
<div draggable="true" class="box">A</div>
<div draggable="true" class="box">B</div>
<div draggable="true" class="box">C</div>
</div>
Este es el CSS para los elementos del contenedor y del cuadro. El único CSS relacionado con el componente de arrastre es la propiedad cursor: move
. El resto del código controla el diseño y el estilo del contenedor y los elementos del cuadro.
.container {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
}
.box {
border: 3px solid #666;
background-color: #ddd;
border-radius: .5em;
padding: 10px;
cursor: move;
}
En este punto, puedes arrastrar los elementos, pero no sucede nada más. Para agregar un comportamiento, debes usar la API de JavaScript.
Cómo escuchar eventos de arrastre
Para supervisar el proceso de arrastre, puedes escuchar cualquiera de los siguientes eventos:
Para controlar el flujo de arrastre, necesitas algún tipo de elemento de origen (donde se inicia el arrastre), la carga útil de datos (lo que se arrastra) y un destino (un área para capturar la acción de soltar). El elemento fuente puede ser casi cualquier tipo de elemento. El objetivo es la zona de drop o el conjunto de zonas que acepta los datos que el usuario intenta descartar. No todos los elementos pueden ser objetivos. Por ejemplo, el destino no puede ser una imagen.
Iniciar y finalizar una secuencia de arrastre
Después de definir los atributos draggable="true"
en tu contenido, adjunta un controlador de eventos dragstart
para iniciar la secuencia de arrastre de cada columna.
Este código establece la opacidad de la columna en 40% cuando el usuario comienza a arrastrarla y, luego, la devuelve al 100% cuando finaliza el evento de arrastre.
function handleDragStart(e) {
this.style.opacity = '0.4';
}
function handleDragEnd(e) {
this.style.opacity = '1';
}
let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragend', handleDragEnd);
});
El resultado se puede ver en la siguiente demostración de Glitch. Arrastra un elemento, y su
opacidad cambia. Debido a que el elemento de origen tiene el evento dragstart
, establecer this.style.opacity
en 40% le brinda al usuario comentarios visuales de que ese elemento es la selección actual que se está moviendo. Cuando sueltas el elemento, el elemento de origen vuelve al 100% de opacidad, aunque todavía no hayas definido el comportamiento de la acción de soltar.
Agrega señales visuales adicionales
Para ayudar al usuario a entender cómo interactuar con tu interfaz, usa los controladores de eventos dragenter
, dragover
y dragleave
. En este ejemplo, las columnas son objetivos para soltar, además de ser arrastrables. Ayuda al usuario a comprender esto marcando el borde con puntos cuando sostengan un elemento arrastrado sobre una columna. Por ejemplo, en tu CSS, puedes crear una clase over
para
elementos que son destinos para soltar:
.box.over {
border: 3px dotted #666;
}
Luego, en tu JavaScript, configura los controladores de eventos, agrega la clase over
cuando se arrastra la columna y quítala cuando el elemento arrastrado se vaya. En el controlador dragend
, también nos aseguramos de quitar las clases al final del arrastre.
document.addEventListener('DOMContentLoaded', (event) => {
function handleDragStart(e) {
this.style.opacity = '0.4';
}
function handleDragEnd(e) {
this.style.opacity = '1';
items.forEach(function (item) {
item.classList.remove('over');
});
}
function handleDragOver(e) {
e.preventDefault();
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
let items = document.querySelectorAll('.container .box');
items.forEach(function(item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragover', handleDragOver);
item.addEventListener('dragenter', handleDragEnter);
item.addEventListener('dragleave', handleDragLeave);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('drop', handleDrop);
});
});
Hay un par de puntos que vale la pena abarcar en este código:
La acción predeterminada para el evento
dragover
es establecer la propiedaddataTransfer.dropEffect
en"none"
. La propiedaddropEffect
se tratará más adelante en esta página. Por ahora, solo debes saber que evita que se active el eventodrop
. Para anular este comportamiento, llama ae.preventDefault()
. Otra práctica recomendada es mostrarfalse
en ese mismo controlador.El controlador de eventos
dragenter
se usa para activar o desactivar la claseover
, en lugar dedragover
. Si usasdragover
, el evento se activa repetidamente mientras el usuario mantiene el elemento arrastrado sobre una columna, lo que provoca que la clase de CSS se active de forma repetitiva. Esto hace que el navegador realice una gran cantidad de trabajo de renderización innecesaria, lo que puede afectar la experiencia del usuario. Te recomendamos que minimices los reintentos de dibujo. Si necesitas usardragover
, considera regular o anular el objeto de escucha de eventos.
Completa el proceso de lanzamiento
Para procesar la acción de soltar, agrega un objeto de escucha de eventos para el evento drop
. En el controlador drop
, deberás evitar el comportamiento predeterminado del navegador para las bajas, que suelen ser algún tipo de redireccionamiento molesto. Para ello, llama a e.stopPropagation()
.
function handleDrop(e) {
e.stopPropagation(); // stops the browser from redirecting.
return false;
}
Asegúrate de registrar el nuevo controlador junto con los otros controladores:
let items = document.querySelectorAll('.container .box');
items.forEach(function(item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragover', handleDragOver);
item.addEventListener('dragenter', handleDragEnter);
item.addEventListener('dragleave', handleDragLeave);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('drop', handleDrop);
});
Si ejecutas el código en este punto, el elemento no baja en la nueva ubicación. Para que esto suceda, usa el objeto DataTransfer
.
La propiedad dataTransfer
contiene los datos enviados en una acción de arrastre. dataTransfer
se establece en el evento dragstart
y se lee o se controla en el evento de soltar. Llamar a e.dataTransfer.setData(mimeType, dataPayload)
te permite configurar el tipo de MIME y la carga útil de datos del objeto.
En este ejemplo, permitiremos que los usuarios reorganicen el orden de las columnas. Para ello, primero debes almacenar el código HTML del elemento fuente cuando se inicia el arrastre:
function handleDragStart(e) {
this.style.opacity = '0.4';
dragSrcEl = this;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
En el evento drop
, para procesar la eliminación de columnas, configura el HTML de la columna de origen con el HTML de la columna de destino en la que descartaste los datos. Esto incluye verificar que el usuario no vuelva a la misma columna de la que se arrastró.
function handleDrop(e) {
e.stopPropagation();
if (dragSrcEl !== this) {
dragSrcEl.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
}
return false;
}
Puedes ver el resultado en la siguiente demostración. Para que esto funcione, necesitarás un navegador de escritorio. La API de Arrastrar y soltar no es compatible con dispositivos móviles. Arrastra y suelta la columna A en la parte superior de la columna B y observa cómo cambian de lugar:
Más propiedades de arrastre
El objeto dataTransfer
expone propiedades para proporcionar información visual al usuario durante el proceso de arrastre y controlar cómo responde cada destino para soltar a un tipo de datos en particular.
dataTransfer.effectAllowed
restringe el "tipo de arrastre" que el usuario puede realizar en el elemento. Se usa en el modelo de procesamiento de arrastrar y soltar para inicializardropEffect
durante los eventosdragenter
ydragover
. La propiedad puede tener los siguientes valores:none
,copy
,copyLink
,copyMove
,link
,linkMove
,move
,all
yuninitialized
.dataTransfer.dropEffect
controla los comentarios que recibe el usuario durante los eventosdragenter
ydragover
. Cuando el usuario mantiene el puntero sobre un elemento de destino, el cursor del navegador indica el tipo de operación que se realizará, como una copia o un movimiento. El efecto puede tener uno de los siguientes valores:none
,copy
,link
omove
.e.dataTransfer.setDragImage(imgElement, x, y)
significa que, en lugar de usar los comentarios de "imagen fantasma" predeterminados del navegador, puedes configurar un ícono de arrastre.
Carga de archivo
En este ejemplo simple, se usa una columna como origen de arrastre y destino de arrastre. Esto puede suceder en una IU que le solicita al usuario que reorganice los elementos. En algunas situaciones, el destino de arrastre y la fuente pueden ser tipos de elementos diferentes, como en una interfaz en la que el usuario necesita seleccionar una imagen como la imagen principal de un producto arrastrando la imagen seleccionada hasta un destino.
La función Arrastrar y soltar se usa con frecuencia para permitir que los usuarios arrastren elementos desde su escritorio a una aplicación. La diferencia principal se encuentra en el controlador drop
. En lugar de usar dataTransfer.getData()
para acceder a los archivos, sus datos se encuentran en la propiedad dataTransfer.files
:
function handleDrop(e) {
e.stopPropagation(); // Stops some browsers from redirecting.
e.preventDefault();
var files = e.dataTransfer.files;
for (var i = 0, f; (f = files[i]); i++) {
// Read the File objects in this FileList.
}
}
Puedes obtener más información al respecto en Arrastrar y soltar de forma personalizada.
Más recursos
- La especificación de arrastrar y soltar
- API de arrastrar y soltar HTML de MDN
- Cómo crear un cargador de archivos que se puede arrastrar y soltar con JavaScript Vanilla
- Cómo crear un juego de estacionamiento con la API HTML de arrastrar y soltar
- Cómo usar la API de HTML de arrastrar y soltar en React