API Seret dan Lepas HTML5

Postingan ini menjelaskan dasar-dasar tarik lalu lepas.

Membuat konten yang dapat ditarik

Di sebagian besar browser, pilihan teks, gambar, dan link dapat ditarik secara default. Misalnya, jika menarik link di halaman web, Anda akan melihat kotak kecil dengan judul dan URL yang dapat Anda lepaskan di kolom URL atau desktop untuk membuat pintasan atau membuka link. Agar jenis konten lainnya dapat ditarik, Anda harus menggunakan HTML5 Drag and Drop API.

Untuk membuat objek dapat ditarik, tetapkan draggable=true pada elemen tersebut. Hampir semua hal dapat ditarik, termasuk gambar, file, link, file, atau markup apa pun di halaman Anda.

Contoh berikut membuat antarmuka untuk mengatur ulang kolom yang telah ditata dengan CSS Grid. Markup dasar untuk kolom terlihat seperti ini, dengan atribut draggable untuk setiap kolom ditetapkan ke 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>

Berikut adalah CSS untuk elemen penampung dan kotak. Satu-satunya CSS yang terkait dengan fitur tarik adalah properti cursor: move. Kode lainnya mengontrol tata letak dan gaya elemen penampung dan kotak.

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

Pada tahap ini, Anda dapat menarik item, tetapi tidak ada yang terjadi. Untuk menambahkan perilaku, Anda perlu menggunakan JavaScript API.

Memproses peristiwa tarik

Untuk memantau proses tarik, Anda dapat memproses salah satu peristiwa berikut:

Untuk menangani alur tarik, Anda memerlukan semacam elemen sumber (tempat tarik dimulai), payload data (hal yang ditarik), dan target (area untuk menangkap drop). Elemen sumber dapat berupa hampir semua jenis elemen. Target adalah zona lepas atau sekumpulan zona lepas yang menerima data yang coba dilepas pengguna. Tidak semua elemen dapat menjadi target. Misalnya, target Anda tidak boleh berbentuk gambar.

Memulai dan mengakhiri urutan tarik

Setelah menentukan atribut draggable="true" pada konten, lampirkan pengendali peristiwa dragstart untuk memulai urutan tarik untuk setiap kolom.

Kode ini menetapkan opasitas kolom ke 40% saat pengguna mulai menariknya, lalu mengembalikannya ke 100% saat peristiwa penarikan berakhir.

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

Hasilnya dapat dilihat di demo Glitch berikut. Tarik item, dan opasinya akan berubah. Karena elemen sumber memiliki peristiwa dragstart, menetapkan this.style.opacity ke 40% akan memberi pengguna masukan visual bahwa elemen tersebut adalah pilihan saat ini yang dipindahkan. Saat Anda melepas item, elemen sumber akan kembali ke opasitas 100%, meskipun Anda belum menentukan perilaku lepas.

Menambahkan isyarat visual tambahan

Untuk membantu pengguna memahami cara berinteraksi dengan antarmuka Anda, gunakan pengendali peristiwa dragenter, dragover, dan dragleave. Dalam contoh ini, kolom adalah target drop selain dapat ditarik. Bantu pengguna untuk memahami hal ini dengan membuat batas putus-putus saat mereka menahan item yang ditarik di atas kolom. Misalnya, di CSS, Anda dapat membuat class over untuk elemen yang merupakan target drop:

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

Kemudian, di JavaScript, siapkan pengendali peristiwa, tambahkan class over saat kolom ditarik, dan hapus saat elemen yang ditarik keluar. Di pengendali dragend, kita juga memastikan untuk menghapus class di akhir tarik.

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

Ada beberapa poin yang perlu dibahas dalam kode ini:

  • Tindakan default untuk peristiwa dragover adalah menetapkan properti dataTransfer.dropEffect ke "none". Properti dropEffect akan dibahas nanti di halaman ini. Untuk saat ini, cukup ketahui bahwa tindakan ini mencegah peristiwa drop diaktifkan. Untuk mengganti perilaku ini, panggil e.preventDefault(). Praktik baik lainnya adalah menampilkan false di pengendali yang sama.

  • Pengendali peristiwa dragenter digunakan untuk mengalihkan class over, bukan dragover. Jika Anda menggunakan dragover, peristiwa akan diaktifkan berulang kali saat pengguna memegang item yang ditarik di atas kolom, sehingga class CSS akan beralih berulang kali. Hal ini membuat browser melakukan banyak pekerjaan rendering yang tidak perlu, yang dapat memengaruhi pengalaman pengguna. Sebaiknya minimalkan penggambaran ulang, dan jika Anda perlu menggunakan dragover, pertimbangkan pembatasan atau debouncing pemroses peristiwa.

Menyelesaikan drop

Untuk memproses drop, tambahkan pemroses peristiwa untuk peristiwa drop. Di pengendali drop, Anda harus mencegah perilaku default browser untuk drop, yang biasanya merupakan semacam pengalihan yang mengganggu. Untuk melakukannya, panggil e.stopPropagation().

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

Pastikan untuk mendaftarkan pengendali baru bersama pengendali lainnya:

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

Jika Anda menjalankan kode pada tahap ini, item tidak akan dihapus ke lokasi baru. Untuk melakukannya, gunakan objek DataTransfer.

Properti dataTransfer menyimpan data yang dikirim dalam tindakan tarik. dataTransfer ditetapkan dalam peristiwa dragstart dan dibaca atau ditangani dalam peristiwa drop. Dengan memanggil e.dataTransfer.setData(mimeType, dataPayload), Anda dapat menetapkan jenis MIME dan payload data objek.

Dalam contoh ini, kita akan mengizinkan pengguna mengatur ulang urutan kolom. Untuk melakukannya, Anda harus menyimpan HTML elemen sumber terlebih dahulu saat proses tarik dimulai:

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

  dragSrcEl = this;

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

Dalam peristiwa drop, Anda memproses drop kolom dengan menetapkan HTML kolom sumber ke HTML kolom target tempat Anda meletakkan data. Hal ini termasuk memeriksa apakah pengguna tidak meletakkan kembali ke kolom yang sama dengan kolom yang mereka tarik.

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

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

  return false;
}

Anda dapat melihat hasilnya dalam demo berikut. Agar dapat berfungsi, Anda memerlukan browser desktop. API Tarik lalu Lepaskan tidak didukung di perangkat seluler. Tarik lalu lepaskan kolom A di atas kolom B dan perhatikan bagaimana posisinya berubah:

Properti menarik lainnya

Objek dataTransfer mengekspos properti untuk memberikan masukan visual kepada pengguna selama proses tarik dan mengontrol cara setiap target lepas merespons jenis data tertentu.

  • dataTransfer.effectAllowed membatasi 'jenis tarik' yang dapat dilakukan pengguna pada elemen. Fungsi ini digunakan dalam model pemrosesan tarik lalu lepas untuk melakukan inisialisasi dropEffect selama peristiwa dragenter dan dragover. Properti ini dapat memiliki nilai berikut: none, copy, copyLink, copyMove, link, linkMove, move, all, dan uninitialized.
  • dataTransfer.dropEffect mengontrol masukan yang diperoleh pengguna selama peristiwa dragenter dan dragover. Saat pengguna menahan kursor di atas elemen target, kursor browser akan menunjukkan jenis operasi yang akan dilakukan, seperti menyalin atau memindahkan. Efek dapat menggunakan salah satu nilai berikut: none, copy, link, move.
  • e.dataTransfer.setDragImage(imgElement, x, y) berarti bahwa Anda dapat menetapkan ikon tarik, bukan menggunakan masukan 'gambar hantu' default browser.

Upload file

Contoh sederhana ini menggunakan kolom sebagai sumber tarik dan target tarik. Hal ini mungkin terjadi di UI yang meminta pengguna untuk menyusun ulang item. Dalam beberapa situasi, target dan sumber tarik mungkin merupakan jenis elemen yang berbeda, seperti pada antarmuka tempat pengguna perlu memilih satu gambar sebagai gambar utama untuk produk dengan menarik gambar yang dipilih ke target.

Tarik lalu lepas sering digunakan untuk memungkinkan pengguna menarik item dari desktop ke aplikasi. Perbedaan utamanya ada pada pengendali drop Anda. Datanya tidak disimpan dalam properti dataTransfer.getData() untuk mengakses file, tetapi disimpan dalam properti 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.
  }
}

Anda dapat menemukan informasi selengkapnya tentang hal ini di Tarik lalu lepas kustom.

Referensi lainnya