Interfejs API przeciągania i upuszczania HTML5

Ten post zawiera podstawowe informacje o przeciąganiu i upuszczaniu.

Tworzenie treści, które można przeciągać

W większości przeglądarek domyślnie można przeciągać tekst, obrazy i linki. Jeśli np. przeciągniesz link na stronie internetowej, zobaczysz małe pole z tytułem i adresem URL. Możesz je upuścić na pasku adresu lub pulpicie, aby utworzyć skrót lub przejść do linku. Aby móc przeciągać inne rodzaje treści, musisz użyć interfejsów API przeciągania i upuszczania HTML5.

Aby umożliwić przeciąganie obiektu, ustaw dla niego atrybut draggable=true. Można przeciągać praktycznie wszystko, w tym obrazy, pliki, linki, pliki i dowolne znaczniki na stronie.

Poniższy przykład pokazuje interfejs do zmiany kolejności kolumn, które zostały rozmieszczone za pomocą siatki CSS. Podstawowe znaczniki kolumn wyglądają tak, a atrybut draggable w każdej kolumnie ma wartość 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>

Oto kod CSS elementu kontenera i pola. Jedyną usługą CSS powiązaną z funkcją przeciągania jest właściwość cursor: move. Pozostała część kodu służy do kontrolowania układu i stylu elementów kontenera i pola.

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

Teraz możesz przeciągnąć elementy, ale nic się nie dzieje. Aby dodać działanie, musisz użyć interfejsu JavaScript API.

Wykrywa zdarzenia przeciągania

Aby monitorować proces przeciągania, możesz wykrywać dowolne z tych zdarzeń:

Aby obsługiwać przepływ przeciągania, potrzebujesz jakiegoś elementu źródłowego (gdzie zaczyna się przeciąganie), ładunku danych (przeciągniętego obiektu) i celu (obszaru, w którym nastąpi przechwycenie). Elementem źródłowym może być prawie dowolny element. Wartość docelowa to strefa utraty lub zbiór stref upuszczania, która akceptuje dane, które użytkownik próbuje usunąć. Nie wszystkie elementy mogą być celem. Nie może to być np. obraz.

Rozpoczynanie i kończenie sekwencji przeciągania

Po zdefiniowaniu atrybutów draggable="true" w treściach dodaj moduł obsługi zdarzeń dragstart, aby rozpocząć sekwencję przeciągania w każdej kolumnie.

Ten kod ustawia przezroczystość kolumny na 40%, gdy użytkownik zacznie ją przeciągać, a potem przywraca ją do 100% po zakończeniu zdarzenia przeciągania.

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

Efekt możesz zobaczyć w prezentacji poniżej. Przeciągnij element, aby zmienić jego przezroczystość. Element źródłowy zawiera zdarzenie dragstart, więc ustawienie wartości this.style.opacity na 40% daje użytkownikowi wizualną informację, że ten element jest aktualnie przenoszonym wyborem. Po upuszczeniu elementu jego przezroczystość powróci do 100%, mimo że sposób upuszczania nie został jeszcze zdefiniowany.

Dodaj dodatkowe wskazówki wizualne

Aby pokazać użytkownikowi, jak korzystać z interfejsu, użyj modułów obsługi zdarzeń dragenter, dragover i dragleave. W tym przykładzie kolumny można przeciągać, a nie tylko miejsca docelowe. Pomóż użytkownikowi zrozumieć tę kwestię, przekreślając obramowanie, gdy przeciągasz element nad kolumną. Na przykład w kodzie CSS możesz utworzyć klasę over dla elementów, które są takimi elementami:

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

Następnie w JavaScripcie skonfiguruj moduły obsługi zdarzeń, dodaj klasę over, gdy przeciągniesz kolumnę, i usuń ją, gdy przeciągany element opuści kolumnę. W module obsługi dragend dbamy też o usunięcie klas na końcu przeciągania.

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

W tym kodzie warto omówić kilka kwestii:

  • Domyślnym działaniem w przypadku zdarzenia dragover jest ustawienie właściwości dataTransfer.dropEffect na "none". Właściwość dropEffect została omówiona w dalszej części tej strony. Na razie pamiętaj, że uniemożliwia ono uruchomienie zdarzenia drop. Aby zastąpić to zachowanie, wywołaj e.preventDefault(). Inną dobrą metodą jest zwrócenie false w tym samym module obsługi.

  • Moduł obsługi zdarzeń dragenter służy do przełączania klasy over zamiast dragover. Jeśli używasz dragover, zdarzenie jest wywoływane wielokrotnie, gdy użytkownik przytrzyma przeciągany element nad kolumną, co powoduje wielokrotne przełączanie klasy CSS. To powoduje, że przeglądarka wykonuje wiele zbędnej pracy z renderowaniem, co z kolei może niekorzystnie wpływać na wrażenia użytkownika. Zdecydowanie zalecamy ograniczenie liczby powtórzeń, a jeśli chcesz użyć funkcji dragover, rozważ ograniczenie lub zmniejszenie częstotliwości radiowej detektora zdarzeń.

Dokończ dodawanie

Aby przetworzyć spadek, dodaj odbiornik zdarzenia drop. W module obsługi drop musisz wyłączyć domyślne zachowanie przeglądarki w przypadku spadków, które jest zwykle jakimś rodzajem irytującego przekierowania. Aby to zrobić, zadzwoń pod numer e.stopPropagation().

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

Pamiętaj, aby zarejestrować nowy moduł obsługi razem z innymi modułami obsługi:

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

Jeśli uruchomisz kod w tym momencie, produkt nie spadnie w nowej lokalizacji. Aby to zrobić, użyj obiektu DataTransfer.

Właściwość dataTransfer zawiera dane wysłane podczas przeciągania. Parametr dataTransfer jest ustawiony w zdarzeniu dragstart i jest odczytywany lub obsługiwany w zdarzeniu odrzucenia. Wywołanie e.dataTransfer.setData(mimeType, dataPayload) umożliwia ustawienie typu MIME i ładunku danych obiektu.

W tym przykładzie użytkownicy będą mogli zmieniać kolejność kolumn. Aby to zrobić, musisz najpierw zapisać kod HTML elementu źródłowego po rozpoczęciu przeciągania:

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

  dragSrcEl = this;

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

W zdarzeniu drop przetwarzasz spadek kolumny, ustawiając kod HTML kolumny źródłowej na kod HTML kolumny docelowej, w której dane zostały umieszczone. Obejmuje to sprawdzenie, czy użytkownik nie wraca do kolumny, z której przeciągnął.

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

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

  return false;
}

Wynik możesz zobaczyć w poniższej wersji demonstracyjnej. Aby to zrobić, potrzebujesz przeglądarki na komputerze. Interfejs API Drag and Drop nie jest obsługiwany na urządzeniach mobilnych. Przeciągnij i zwolnij kolumnę A na kolumnę B. Zwróć uwagę na to, jak zmienia ona miejsca:

Więcej właściwości przeciągania

Obiekt dataTransfer udostępnia właściwości, aby podczas przeciągania użytkownik mógł zobaczyć wizualne informacje zwrotne, a także kontrolować reakcje każdego miejsca docelowego na konkretny typ danych.

  • dataTransfer.effectAllowed ogranicza typ „przeciągnięcia”, który użytkownik może wykonać na elemencie. Jest on używany w modelu przetwarzania metodą „przeciągnij i upuść” do inicjowania dropEffect podczas zdarzeń dragenter i dragover. Właściwość może mieć te wartości: none, copy, copyLink, copyMove, link, linkMove, move, all i uninitialized.
  • dataTransfer.dropEffect kontroluje informacje zwrotne, które użytkownik otrzymuje podczas zdarzeń dragenter i dragover. Gdy użytkownik najedzie kursorem na element docelowy, kursor przeglądarki wskazuje typ operacji, która zostanie wykonana, np. skopiowanie lub przeniesienie. Efekt może mieć jedną z tych wartości: none, copy, link, move.
  • e.dataTransfer.setDragImage(imgElement, x, y) oznacza, że zamiast korzystać z domyślnego obrazu widmo w przeglądarce, możesz ustawić ikonę przeciągania.

Prześlij plik

W tym prostym przykładzie użyto kolumny jako źródła i miejsca docelowego przeciągania. Może się to zdarzyć w interfejsie, który prosi użytkownika o zmianę kolejności elementów. W niektórych sytuacjach element docelowy i źródło przeciągania mogą być różnymi typami elementów, np. w interfejsie, gdzie użytkownik musi wybrać jeden obraz jako główny obraz produktu, przeciągając wybrany obraz na miejsce docelowe.

Przeciąganie i upuszczanie jest często stosowane, aby umożliwić użytkownikom przeciąganie elementów z pulpitu do aplikacji. Główną różnicą jest moduł obsługi drop. Zamiast korzystać z metody dataTransfer.getData() w celu uzyskania dostępu do plików, dane te znajdują się we właściwości 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.
  }
}

Więcej informacji na ten temat znajdziesz w sekcji Niestandardowe przeciąganie i upuszczanie.

Więcej materiałów