Interfejs API przeciągania i upuszczania HTML5

Z tego artykułu dowiesz się, jak działa przeciąganie i upuszczanie.

Większość przeglądarek domyślnie umożliwia przeciąganie zaznaczonego tekstu, obrazów i linków. Jeśli na przykład przeciągniesz link na stronie internetowej, zobaczysz małe pole z tytułem i adresem URL, które możesz upuścić na pasku adresu lub na pulpicie, aby utworzyć skrót lub przejść do linku. Aby umożliwić przeciąganie innych typów treści, musisz użyć interfejsów API przeciągania i upuszczania w HTML5.

Aby obiekt można było przeciągać, ustaw dla niego parametr draggable=true. Dozwolone jest przeciąganie niemal wszystkiego, w tym obrazów, plików, linków, plików i wszelkich znaczników na stronie.

W tym przykładzie tworzymy interfejs do przestawiania kolumn rozmieszczonych za pomocą CSS Grid. Podstawowy kod znaczników kolumn wygląda tak: atrybut draggable w przypadku każdej kolumny 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 elementów kontenera i pudełka. Jedynym elementem CSS powiązanym z funkcją przeciągania jest właściwość cursor: move. Pozostała część kodu kontroluje układ i styl elementów kontenera oraz 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;
}

W tym momencie możesz przeciągać elementy, ale nic więcej się nie dzieje. Aby dodać zachowanie, musisz użyć interfejsu JavaScript API.

Nasłuchiwanie zdarzeń przeciągania

Aby monitorować proces przeciągania, możesz nasłuchiwać tych zdarzeń:

Aby obsłużyć przeciąganie, potrzebujesz jakiegoś elementu źródłowego (gdzie przeciąganie się zaczyna), danych (element przeciągany) i celu (obszar, w którym można upuścić element). Źródło może być dowolnym elementem. Docelowy obszar zrzutu to obszar zrzutu lub zestaw obszarów zrzutu, który akceptuje dane, które użytkownik próbuje zrzucić. Nie wszystkie elementy mogą być celami. Na przykład nie możesz ustawić jako celu elementu typu obraz.

Rozpoczynanie i zatrzymywanie sekwencji przeciągania

Po zdefiniowaniu atrybutów draggable="true" w treści dołącz do niej element obsługi zdarzenia dragstart, aby rozpocząć sekwencję przeciągania w przypadku każdej kolumny.

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

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żna zobaczyć w tym filmie demonstracyjnym Glitch. Przeciągnij element, aby zmienić jego przezroczystość. Źródłowy element ma zdarzenie dragstart, więc ustawienie wartości this.style.opacity na 40% daje użytkownikowi wizualną informację, że ten element jest aktualnie zaznaczany. Gdy upuścisz element, element źródłowy wróci do 100% przezroczystości, nawet jeśli nie zdefiniujesz jeszcze zachowania po upuszczeniu.

Dodawanie dodatkowych wskazówek wizualnych

Aby pomóc użytkownikowi zrozumieć, jak korzystać z interfejsu, użyj przełączników zdarzeń dragenter, dragoverdragleave. W tym przykładzie kolumny są nie tylko przeciągane, ale też miejscami docelowymi. Pomagaj użytkownikowi w zrozumieniu tej zasady, stosując przerywaną obwódkę, gdy trzyma przeciągany element nad kolumną. W kodzie CSS możesz na przykład utworzyć klasę over dla elementów, które są celami przeciągania:

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

Następnie w pliku JavaScript skonfiguruj przetwarzacze zdarzeń, dodaj klasę over, gdy kolumna zostanie przeciągnięta, i usuń ją, gdy przeciągnięty element ją opuści. W obiekcie dragend również usuwamy klasy na końcu funkcji przesuwania.

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 uwzględnić kilka kwestii:

  • Działanie domyślne dla zdarzenia dragover to ustawienie właściwości dataTransfer.dropEffect na "none". Właściwość dropEffect omówimy dalej na tej stronie. Na razie wystarczy wiedzieć, że zapobiega ono wywołaniu zdarzenia drop. Aby zastąpić to działanie, zadzwoń pod numer e.preventDefault(). Inną dobrą praktyką jest zwracanie wartościfalse w tym samym obiekcie.

  • Obsługa zdarzenia dragenter służy do przełączania klasy over zamiast klasy dragover. Jeśli używasz dragover, zdarzenie będzie się wielokrotnie aktywować, gdy użytkownik przytrzyma przeciągany element nad kolumną, co spowoduje wielokrotne przełączanie klasy CSS. W efekcie przeglądarka musi wykonać wiele niepotrzebnych operacji renderowania, co może wpłynąć na wrażenia użytkownika. Zdecydowanie zalecamy zminimalizowanie liczby ponownych wywołań. Jeśli musisz używać funkcji dragover, rozważ ograniczenie jej działania lub stosowanie debouncowania.

Zakończ upuszczenie

Aby przetworzyć drop, dodaj odbiornik zdarzeń dla zdarzenia drop. W obiekcie drop musisz zapobiec domyślnemu działaniu przeglądarki w przypadku przerwania, które zwykle powoduje irytujące przekierowanie. 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, element nie zostanie przeniesiony do nowej lokalizacji. Aby to zrobić, użyj obiektu DataTransfer.

W usłudze dataTransfer przechowywane są dane wysyłane podczas działania przeciągania. dataTransferzostaje ustawiony w zdarzeniu dragstart, a następnie odczytany lub obsłużony w zdarzeniu drop. Wywołanie funkcjie.dataTransfer.setData(mimeType, dataPayload) pozwala ustawić typ MIME obiektu i ładunek danych.

W tym przykładzie zezwolimy użytkownikom na zmianę kolejności kolumn. Aby to zrobić, musisz najpierw przechować kod HTML elementu źródłowego, gdy rozpoczyna się przeciąganie:

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 kolumnę drop, ustawiając HTML kolumny źródłowej na HTML kolumny docelowej, do której dane zostały przeciągnięte. Obejmuje to sprawdzenie, czy użytkownik nie przeciąga elementów z powrotem do tej samej kolumny, z której je przeciągał.

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

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

  return false;
}

Efekt możesz zobaczyć na poniższym filmie demonstracyjnym. Aby to zrobić, musisz użyć przeglądarki na komputerze. Interfejs API przeciągania i upuszczania nie jest obsługiwany na urządzeniach mobilnych. Przeciągnij kolumnę A i upuść ją na kolumnę B. Zwróć uwagę, jak zmieniają one miejsca:

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

Obiekt dataTransfer udostępnia właściwości, które zapewniają użytkownikowi wizualne informacje zwrotne podczas przeciągania, oraz kontrolują sposób reakcji każdego miejsca docelowe na określony typ danych.

  • dataTransfer.effectAllowedogranicza „rodzaj przeciągania”, który użytkownik może wykonać na elemencie. Jest on używany w modelu przetwarzania metodą przeciągania i upuszczania do inicjowania zmiennej dropEffect podczas zdarzeń dragenterdragover. Właściwość może mieć te wartości: none, copy, copyLink, copyMove, link, linkMove, move, all i uninitialized.
  • dataTransfer.dropEffect określa informacje zwrotne, które użytkownik otrzymuje podczas zdarzeń dragenterdragover. Gdy użytkownik najedzie kursorem na element docelowy, kursor przeglądarki wskaże, jakie działanie zostanie wykonane, np. kopiowanie lub przenoszenie. Efekt może przyjmować jedną z tych wartości: none, copy, link lub move.
  • e.dataTransfer.setDragImage(imgElement, x, y) oznacza, że zamiast domyślnej „przejrzystej ikony” w przeglądarce możesz ustawić ikonę do przeciągania.

Prześlij plik

W tym prostym przykładzie kolumna jest zarówno źródłem, jak i docelowym elementem przeciągania. Może się to zdarzyć w interfejsie, który prosi użytkownika o przesunięcie elementów. W niektórych przypadkach źródło i docelowy element przeciągania mogą być różnych typów, np. w interfejsie, w którym użytkownik musi wybrać jedno zdjęcie jako główne zdjęcie produktu, przeciągając wybrane zdjęcie do celu.

Przeciąganie i upuszczanie jest często używane, aby umożliwić użytkownikom przeciąganie elementów z pulpitu do aplikacji. Główna różnica dotyczy elementu drop. Zamiast używać usługi dataTransfer.getData() do uzyskiwania dostępu do plików, dane te są zawarte w usłudze 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 artykule Niestandardowe przeciąganie i upuszczanie.

Więcej zasobów