API HTML5 Drag and Drop

Questo post spiega le nozioni di base del trascinamento.

Crea contenuti trascinabili

Nella maggior parte dei browser, le selezioni di testo, le immagini e i link possono essere trascinati per impostazione predefinita. Ad esempio, se trascini un link in una pagina web, viene visualizzata una piccola casella con titolo e URL che puoi trascinare sulla barra degli indirizzi o sul desktop per creare scorciatoia o vai al link. Per rendere trascinabili altri tipi di contenuti, utilizzare le API di trascinamento HTML5.

Per rendere un oggetto trascinabile, imposta draggable=true su quell'elemento. Quasi qualsiasi elemento può essere trascinato, come immagini, file, link, file o altro il markup della pagina.

L'esempio seguente crea un'interfaccia per riorganizzare le colonne che sono state con CSS Grid. Il markup di base per le colonne ha questo aspetto, con Attributo draggable per ogni colonna impostata su 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>

Questo è il CSS per gli elementi container e box. L'unico CSS correlato alla la funzionalità di trascinamento è cursor: move proprietà. Il resto del codice controlla il layout e lo stile del container e cofanetti.

.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;
}

A questo punto puoi trascinare gli elementi, ma non succede nient'altro. Per aggiungere , devi usare l'API JavaScript.

Ascolta gli eventi di trascinamento

Per monitorare il processo di trascinamento, puoi ascoltare uno dei seguenti eventi:

Per gestire il flusso di trascinamento, hai bisogno di un tipo di elemento di origine (in cui il trascinamento il payload (l'elemento che viene trascinato) e un obiettivo (un'area da cogli l'occasione). L'elemento di origine può essere quasi ogni tipo di elemento. La il target è la zona di rilascio o l'insieme di zone di rilascio che accetta i dati di cui l'utente sta provando a far cadere. Non tutti gli elementi possono essere target. Ad esempio, il target non può un'immagine.

Iniziare e terminare una sequenza di trascinamento

Dopo aver definito gli attributi draggable="true" per i tuoi contenuti, allega un Gestore di eventi dragstart per avviare la sequenza di trascinamento per ogni colonna.

Questo codice imposta l'opacità della colonna al 40% quando l'utente inizia a trascinarla, quindi lo riporta al 100% al termine dell'evento di trascinamento.

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);
});

Il risultato può essere visualizzato nella demo di Glitch riportata di seguito. Trascina un elemento e i relativi variazioni di opacità. Poiché l'elemento di origine include l'evento dragstart, l'impostazione Da this.style.opacity a 40% fornisce all'utente un feedback visivo che indica che l'elemento la selezione corrente che viene spostata. Quando rilasci l'elemento, l'elemento di origine torna all'opacità del 100%, anche se non hai ancora definito il comportamento di rilascio.

Aggiungi altri elementi visivi

Per aiutare l'utente a capire come interagire con l'interfaccia, utilizza la Gestori di eventi dragenter, dragover e dragleave. In questo esempio, le colonne sono destinazioni di rilascio oltre a essere trascinabili. Aiuta l'utente a capirlo facendo il bordo tratteggiato quando si tiene un elemento trascinato su una colonna. Ad esempio, nel tuo CSS, potresti creare una classe over per che sono target di calo:

.box.over {
  border: 3px dotted #666;
}

Poi, nel codice JavaScript, configura i gestori di eventi e aggiungi la classe over quando la colonna viene trascinata e rimossa quando l'elemento trascinato esce. Nella il gestore dragend, ci assicuriamo anche di rimuovere le classi alla fine trascinare.

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);
  });
});

Questo codice presenta alcuni punti che vale la pena trattare:

  • L'azione predefinita per l'evento dragover è impostare la proprietà dataTransfer.dropEffect su "none". La proprietà dropEffect verrà trattata più avanti in questa pagina. Per il momento, tieni presente che impedisce l'attivazione dell'evento drop. Per eseguire l'override del tuo comportamento, chiama e.preventDefault(). Un'altra buona prassi è restituire false nello stesso gestore.

  • Il gestore di eventi dragenter viene utilizzato per attivare/disattivare la classe over dragover. Se utilizzi dragover, l'evento si attiva ripetutamente mentre l'utente tiene l'elemento trascinato sopra una colonna, determinando l'attivazione/disattivazione della classe CSS ripetutamente. Il browser esegue quindi molto lavoro di rendering non necessario, che possono influire sull'esperienza utente. Consigliamo vivamente di ridurre al minimo e, se devi usare dragover, valuta la limitazione o il debouncing del listener di eventi.

Completa il lancio

Per elaborare il rilascio, aggiungi un listener di eventi per l'evento drop. In drop dovrai evitare che venga usato il comportamento predefinito del browser in relazione ai rilasci, è un fastidioso reindirizzamento. Per farlo, chiama e.stopPropagation().

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

Assicurati di registrare il nuovo gestore insieme agli altri gestori:

  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 esegui il codice a questo punto, l'elemento non viene rilasciato nella nuova posizione. A per farlo, utilizza la DataTransfer .

La proprietà dataTransfer contiene i dati inviati in un'azione di trascinamento. dataTransfer viene impostata nell'evento dragstart e viene letta o gestita nell'evento di rilascio. Chiamata in corso e.dataTransfer.setData(mimeType, dataPayload) ti consente di impostare il tipo MIME dell'oggetto tipo e payload di dati.

In questo esempio, consentiremo agli utenti di riorganizzare l'ordine delle colonne. Per farlo, devi innanzitutto memorizzare il codice HTML dell'elemento di origine durante il trascinamento inizia:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

Nell'evento drop, elabori il rilascio della colonna impostando il valore HTML all'HTML della colonna di destinazione in cui hai rilasciato i dati. Questo verifica che l'utente non ritorni nella stessa colonna su cui trascinato da.

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

Puoi vedere il risultato nella demo seguente. Affinché questa operazione funzioni, è necessaria una un browser per desktop. L'API di trascinamento non è supportata sui dispositivi mobili. Trascina rilascia la colonna A sopra la colonna B e osserva come cambiano posizione:

Altre proprietà di trascinamento

L'oggetto dataTransfer espone le proprietà per fornire un feedback visivo al utente durante il processo di trascinamento e controllare la risposta di ogni bersaglio tipo di dati specifico.

  • dataTransfer.effectAllowed limita il "tipo di trascinamento" che l'utente può eseguire sull'elemento. È usato nel modello di elaborazione con trascinamento per inizializzare dropEffect durante gli eventi dragenter e dragover. La proprietà può avere seguenti valori: none, copy, copyLink, copyMove, link, linkMove, move, all e uninitialized.
  • dataTransfer.dropEffect controlla il feedback che l'utente riceve durante dragenter e dragover eventi. Quando l'utente posiziona il puntatore su un elemento target, lo stato Il cursore indica il tipo di operazione che verrà eseguita, ad esempio una copia o una mossa. L'effetto può avere uno dei seguenti valori: none, copy, link, move.
  • e.dataTransfer.setDragImage(imgElement, x, y) significa che, invece di utilizzare l'"immagine fantasma" predefinita del browser, i tuoi feedback, puoi impostare un'icona di trascinamento.

Caricamento file

Questo semplice esempio utilizza una colonna sia come origine del trascinamento sia come target del trascinamento. Questo potrebbe verificarsi in una UI che chiede all'utente di riorganizzare gli elementi. In alcune situazioni, il target di trascinamento e l'origine potrebbero essere tipi di elementi diversi, ad esempio in un'interfaccia in cui l'utente deve selezionare un'immagine come immagine principale di un prodotto trascinando l'immagine selezionata su un bersaglio.

La funzionalità di trascinamento viene spesso utilizzata per consentire agli utenti di trascinare elementi dal desktop a di un'applicazione. La differenza principale è nel tuo gestore drop. Invece di utilizzare dataTransfer.getData() per accedere ai file, i relativi dati sono contenuti nell' Proprietà 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.
  }
}

Puoi trovare ulteriori informazioni in merito in Trascinamento personalizzato.

Altre risorse