Esta postagem explica os conceitos básicos do recurso de arrastar e soltar.
Criar conteúdo arrastável
Na maioria dos navegadores, seleções de texto, imagens e links são arrastáveis por padrão. Por exemplo, se você arrastar um link em uma página da Web, vai aparecer uma pequena caixa com um título e um URL que pode ser arrastado para a barra de endereço ou para o computador para criar um atalho ou navegar até o link. Para tornar outros tipos de conteúdo arrastáveis, use as APIs HTML5 de arrastar e soltar.
Para tornar um objeto arrastável, defina draggable=true
nesse elemento. Praticamente tudo pode ser arrastado, incluindo imagens, arquivos, links, arquivos ou qualquer marcação na página.
O exemplo a seguir cria uma interface para reorganizar colunas que foram
organizadas com a grade CSS. A marcação básica das colunas é assim, com
o atributo draggable
de cada coluna definido como 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>
Confira o CSS para os elementos de contêiner e caixa. O único CSS relacionado ao
recurso de arrastar é a propriedade
cursor: move
. O restante do código controla o layout e o estilo dos elementos de contêiner
e da caixa.
.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;
}
Nesse ponto, é possível arrastar os itens, mas nada mais acontece. Para adicionar comportamentos, você precisa usar a API JavaScript.
Detectar eventos de arrastar
Para monitorar o processo de arrastar, você pode detectar qualquer um dos seguintes eventos:
Para processar o fluxo de arrastar, você precisa de algum tipo de elemento de origem (em que a ação de arrastar comece), o payload de dados (a coisa que está sendo arrastada) e um destino (uma área para que a ação seja solta). O elemento de origem pode ser quase qualquer tipo de elemento. O alvo é a área de soltar ou o conjunto de áreas de soltar que aceita os dados que o usuário está tentando soltar. Nem todos os elementos podem ser de destino. Por exemplo, o alvo não pode ser uma imagem.
Iniciar e encerrar uma sequência de arrasto
Depois de definir os atributos draggable="true"
no conteúdo, anexe um
manipulador de eventos dragstart
para iniciar a sequência de arrasto de cada coluna.
Esse código define a opacidade da coluna como 40% quando o usuário começa a arrastá-la e a retorna para 100% quando o evento de arrasto termina.
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);
});
Confira o resultado na demonstração do Glitch abaixo. Arraste um item e sua
opacidade mudará. Como o elemento de origem tem o evento dragstart
, definir
this.style.opacity
como 40% dá ao usuário um feedback visual de que esse elemento é
a seleção atual que está sendo movida. Quando você solta o item, o elemento de origem
retorna à opacidade de 100%, mesmo que você ainda não tenha definido o comportamento de soltar.
Adicionar outros elementos visuais
Para ajudar o usuário a entender como interagir com sua interface, use os gerenciadores de eventos
dragenter
, dragover
e dragleave
. Neste exemplo, as
colunas são destinos de soltar, além de serem arrastáveis. Ajude o usuário
a entender isso fazendo com que a borda fique tracejada quando ele mantiver um item arrastado sobre uma
coluna. Por exemplo, no seu CSS, você pode criar uma classe over
para
elementos que são destinos de soltar:
.box.over {
border: 3px dotted #666;
}
Em seguida, no JavaScript, configure os manipuladores de eventos, adicione a classe over
quando
a coluna for arrastada e a remova quando o elemento arrastado sair. No
gerenciador dragend
, também removemos as classes no final do
arrastar.
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);
});
});
Há alguns pontos a serem abordados neste código:
A ação padrão para o evento
dragover
é definir a propriedadedataTransfer.dropEffect
como"none"
. A propriedadedropEffect
será abordada mais adiante nesta página. Por enquanto, saiba que ele impede o disparo do eventodrop
. Para substituir esse comportamento, chamee.preventDefault()
. Outra prática recomendada é retornarfalse
no mesmo gerenciador.O manipulador de eventos
dragenter
é usado para alternar a classeover
em vez dedragover
. Se você usardragover
, o evento será disparado repetidamente enquanto o usuário segurar o item arrastado sobre uma coluna, fazendo com que a classe CSS seja alternada repetidamente. Isso faz com que o navegador realize muito trabalho de renderização desnecessário, o que pode afetar a experiência do usuário. É altamente recomendável minimizar os redesenhos e, se você precisar usardragover
, considere limitar ou desistir do listener de eventos.
Concluir o lançamento
Para processar a queda, adicione um listener de eventos para o evento drop
. No gerenciador drop
,
é necessário impedir o comportamento padrão do navegador para quedas, que
normalmente é algum tipo de redirecionamento irritante. Para fazer isso, chame e.stopPropagation()
.
function handleDrop(e) {
e.stopPropagation(); // stops the browser from redirecting.
return false;
}
Registre o novo gerenciador com os outros:
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);
});
Se você executar o código nesse momento, o item não será entregue ao novo local. Para
fazer isso, use o objeto DataTransfer
.
A propriedade dataTransfer
contém os dados enviados em uma ação de arrastar. dataTransfer
é definido no evento dragstart
e lido ou processado no evento de soltar. Chamar
e.dataTransfer.setData(mimeType, dataPayload)
permite definir o tipo MIME
do objeto e o payload de dados.
Neste exemplo, vamos permitir que os usuários reorganizem a ordem das colunas. Para fazer isso, primeiro é necessário armazenar o HTML do elemento de origem quando a ação de arrastar começar:
function handleDragStart(e) {
this.style.opacity = '0.4';
dragSrcEl = this;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
No evento drop
, você processa a coluna de soltar definindo o HTML da coluna de origem
como o HTML da coluna de destino em que você soltou os dados. Isso
inclui verificar se o usuário não está voltando para a mesma coluna de onde
foi arrastado.
function handleDrop(e) {
e.stopPropagation();
if (dragSrcEl !== this) {
dragSrcEl.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
}
return false;
}
Confira o resultado na demonstração a seguir. Para isso funcionar, você precisa de um navegador para computador. A API Drag and Drop não é compatível com dispositivos móveis. Arraste e solte a coluna A sobre a coluna B e observe como elas mudam de lugar:
Mais propriedades de arrasto
O objeto dataTransfer
expõe propriedades para fornecer feedback visual ao
usuário durante o processo de arrastar e controlar como cada destino de soltar responde a um
tipo de dados específico.
dataTransfer.effectAllowed
restringe o tipo de ação de arrastar que o usuário pode realizar no elemento. Ele é usado no modelo de processamento de arrastar e soltar para inicializar odropEffect
durante os eventosdragenter
edragover
. A propriedade pode ter os seguintes valores:none
,copy
,copyLink
,copyMove
,link
,linkMove
,move
,all
euninitialized
.dataTransfer.dropEffect
controla o feedback que o usuário recebe durante os eventosdragenter
edragover
. Quando o usuário mantém o ponteiro sobre um elemento de destino, o cursor do navegador indica o tipo de operação que vai ocorrer, como uma cópia ou uma movimentação. O efeito pode usar um dos seguintes valores:none
,copy
,link
,move
.e.dataTransfer.setDragImage(imgElement, x, y)
significa que, em vez de usar o feedback padrão de "imagem fantasma" do navegador, você pode definir um ícone de arrastar.
Upload de arquivo
Este exemplo simples usa uma coluna como origem e destino de arrasto. Isso pode acontecer em uma interface que pede ao usuário para reorganizar os itens. Em algumas situações, a origem e o destino do arrasto podem ser tipos de elementos diferentes, como em uma interface em que o usuário precisa selecionar uma imagem como principal de um produto arrastando a imagem selecionada para um destino.
O recurso de arrastar e soltar é usado com frequência para permitir que os usuários arrastem itens da área de trabalho para
um aplicativo. A principal diferença está no gerenciador drop
. Em vez de usar
dataTransfer.getData()
para acessar os arquivos, os dados deles estão contidos na
propriedade 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.
}
}
Confira mais informações sobre isso em Arrastar e soltar personalizado.