Tambahkan sentuhan ke situs Anda

Layar sentuh tersedia pada semakin banyak perangkat, dari ponsel hingga layar desktop. Aplikasi Anda harus merespons setiap sentuhan secara intuitif dan indah.

Layar sentuh tersedia di semakin banyak perangkat, mulai dari ponsel hingga layar desktop. Saat pengguna memilih untuk berinteraksi dengan UI Anda, aplikasi harus merespons setiap sentuhan secara intuitif.

Merespons status elemen

Pernahkah Anda menyentuh atau mengklik elemen di halaman web dan bertanya-tanya apakah situs itu benar-benar mendeteksinya?

Cukup mengubah warna elemen saat pengguna menyentuh atau berinteraksi dengan bagian UI untuk memberikan jaminan dasar bahwa situs Anda berfungsi. Hal ini tidak hanya mengurangi frustrasi, tetapi juga dapat memberikan kesan cepat dan responsif.

Elemen DOM dapat mewarisi salah satu status berikut: default, fokus, pengarahan kursor, dan aktif. Untuk mengubah UI masing-masing status, kita perlu menerapkan gaya ke class pseudo berikut :hover, :focus, dan :active seperti yang ditampilkan di bawah ini:

.btn {
  background-color: #4285f4;
}

.btn:hover {
  background-color: #296cdb;
}

.btn:focus {
  background-color: #0f52c1;

  /* The outline parameter suppresses the border
  color / outline when focused */
  outline: 0;
}

.btn:active {
  background-color: #0039a8;
}

Cobalah

Gambar yang mengilustrasikan perbedaan warna untuk status
tombol

Di sebagian besar browser seluler, status arahkan kursor dan/atau fokus akan diterapkan ke elemen setelah diketuk.

Pertimbangkan dengan hati-hati gaya yang Anda setel dan bagaimana gaya tersebut akan terlihat oleh pengguna setelah menyelesaikan sentuhan mereka.

Menyembunyikan gaya browser default

Setelah Anda menambahkan gaya untuk status yang berbeda, Anda akan melihat bahwa sebagian besar browser mengimplementasikan gaya mereka sendiri sebagai respons terhadap sentuhan pengguna. Hal ini terutama karena saat pertama kali perangkat seluler diluncurkan, sejumlah situs tidak memiliki gaya visual untuk status :active. Akibatnya, banyak browser menambahkan warna atau gaya sorotan tambahan untuk memberikan masukan kepada pengguna.

Sebagian besar browser menggunakan properti CSS outline untuk menampilkan lingkaran di sekeliling elemen saat elemen difokuskan. Anda dapat meredamnya dengan:

.btn:focus {
    outline: 0;

    /* Add replacement focus styling here (i.e. border) */
}

Safari dan Chrome menambahkan warna sorot ketuk yang dapat dicegah dengan properti CSS -webkit-tap-highlight-color:

/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
  -webkit-tap-highlight-color: transparent;
}

Cobalah

Internet Explorer pada Windows Phone memiliki perilaku yang serupa, tetapi diredam melalui tag meta:

<meta name="msapplication-tap-highlight" content="no">

Firefox memiliki dua efek samping.

Class semu -moz-focus-inner, yang menambahkan garis batas pada elemen yang dapat disentuh, yang dapat Anda hapus dengan menyetel border: 0.

Jika menggunakan elemen <button> di Firefox, Anda akan mendapatkan gradien yang diterapkan, yang dapat Anda hapus dengan menetapkan background-image: none.

/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
  background-image: none;
}

.btn::-moz-focus-inner {
  border: 0;
}

Cobalah

Menonaktifkan pilihan pengguna

Saat membuat UI, mungkin ada skenario di mana Anda ingin pengguna berinteraksi dengan elemen, tetapi Anda ingin menyembunyikan perilaku default memilih teks dengan tekan lama atau menarik mouse di atas UI.

Anda dapat melakukannya dengan properti CSS user-select, tetapi waspadalah bahwa melakukan hal ini pada konten dapat sangat menyebalkan bagi pengguna jika mereka ingin memilih teks dalam elemen. Jadi, pastikan Anda menggunakannya dengan hati-hati dan seperlunya.

/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
  user-select: none;
}

Mengimplementasikan gestur kustom

Jika Anda memiliki gagasan untuk interaksi dan gestur khusus untuk situs Anda, ada dua topik yang perlu diingat:

  1. Cara mendukung semua browser.
  2. Cara menjaga kecepatan frame tetap tinggi.

Dalam artikel ini, kita akan melihat secara tepat topik yang mencakup API yang perlu kita dukung untuk membuka semua browser, lalu membahas cara menggunakan peristiwa ini secara efisien.

Bergantung pada isyarat yang ingin dilakukan, Anda mungkin ingin pengguna berinteraksi dengan satu elemen pada satu waktu atau Anda ingin mereka dapat berinteraksi dengan beberapa elemen secara bersamaan.

Kita akan melihat dua contoh dalam artikel ini, keduanya menunjukkan dukungan untuk semua browser dan cara menjaga agar kecepatan frame tetap tinggi.

Contoh GIF sentuh pada dokumen

Contoh pertama memungkinkan pengguna untuk berinteraksi dengan satu elemen. Dalam hal ini, Anda mungkin ingin semua peristiwa sentuh diberikan ke satu elemen tersebut, selama gestur pertama kali dimulai pada elemen itu sendiri. Misalnya, memindahkan jari dari elemen yang dapat digeser masih dapat mengontrol elemen.

Hal ini berguna karena memberikan banyak fleksibilitas bagi pengguna, tetapi menerapkan batasan tentang cara pengguna dapat berinteraksi dengan UI Anda.

Contoh GIF sentuhan pada elemen

Namun, jika Anda berharap pengguna berinteraksi dengan beberapa elemen secara bersamaan (menggunakan multi-sentuh), Anda harus membatasi sentuhan ke elemen tertentu.

Hal ini lebih fleksibel bagi pengguna, tetapi mempersulit logika untuk memanipulasi UI dan kurang tahan terhadap kesalahan pengguna.

Menambahkan pemroses peristiwa

Di Chrome (versi 55 dan yang lebih baru), Internet Explorer &Edge, PointerEvents adalah pendekatan yang direkomendasikan untuk menerapkan gestur kustom.

Di browser lain, TouchEvents dan MouseEvents adalah pendekatan yang tepat.

Fitur menarik dari PointerEvents adalah menggabungkan beberapa jenis input, termasuk peristiwa mouse, sentuh, dan pena, ke dalam satu kumpulan callback. Peristiwa yang akan diproses adalah pointerdown, pointermove, pointerup, dan pointercancel.

Persamaan dengan browser lainnya adalah touchstart, touchmove, touchend, dan touchcancel untuk peristiwa sentuh dan jika Anda ingin mengimplementasikan isyarat yang sama untuk input mouse, Anda harus mengimplementasikan mousedown, mousemove, dan mouseup.

Jika ada pertanyaan tentang peristiwa yang sebaiknya digunakan, lihat tabel Peristiwa sentuh, mouse, dan pointer ini.

Penggunaan peristiwa ini mengharuskan pemanggilan metode addEventListener() pada elemen DOM, beserta nama peristiwa, fungsi callback, dan boolean. Boolean menentukan apakah Anda harus menangkap peristiwa sebelum atau setelah elemen lain memiliki kesempatan untuk menangkap dan menafsirkan peristiwa tersebut. (true berarti Anda ingin peristiwa tersebut ditampilkan sebelum elemen lainnya.)

Berikut adalah contoh dari mendengarkan untuk memulai interaksi.

// Check if pointer events are supported.
if (window.PointerEvent) {
  // Add Pointer Event Listener
  swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
  // Add Touch Listener
  swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);

  // Add Mouse Listener
  swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}

Cobalah

Menangani interaksi elemen tunggal

Dalam cuplikan kode pendek di atas, kita hanya menambahkan pemroses peristiwa awal untuk peristiwa mouse. Alasan untuk hal ini adalah karena peristiwa mouse hanya akan dipicu saat kursor diarahkan ke atas elemen yang ditambahkan pemroses peristiwa.

TouchEvents akan melacak gestur setelah dimulai, terlepas dari tempat sentuhan terjadi, dan PointerEvents akan melacak peristiwa, terlepas dari tempat sentuhan terjadi setelah kita memanggil setPointerCapture pada elemen DOM.

Untuk gerakan mouse dan peristiwa akhir, kita menambahkan pemroses peristiwa di metode awal gestur dan menambahkan pemroses ke dokumen, yang berarti pemroses dapat melacak kursor hingga gestur selesai.

Langkah-langkah yang diambil untuk menerapkannya adalah:

  1. Tambahkan semua pemroses TouchEvent dan PointerEvent. Untuk MouseEvents, tambahkan hanya peristiwa awal.
  2. Di dalam callback gestur awal, ikat gerakan mouse dan peristiwa akhir ke dokumen. Dengan cara ini, semua peristiwa mouse akan diterima terlepas dari apakah peristiwa terjadi pada elemen asli atau tidak. Untuk PointerEvents, kita perlu memanggil setPointerCapture() pada elemen asli untuk menerima semua peristiwa lebih lanjut. Kemudian tangani awal gestur.
  3. Menangani peristiwa perpindahan.
  4. Pada peristiwa akhir, hapus gerakan mouse dan listener akhir dari dokumen dan akhiri gestur.

Berikut adalah cuplikan metode handleGestureStart() kami yang menambahkan peristiwa gerakan dan akhir ke dokumen:

// Handle the start of gestures
this.handleGestureStart = function(evt) {
  evt.preventDefault();

  if(evt.touches && evt.touches.length > 1) {
    return;
  }

  // Add the move and end listeners
  if (window.PointerEvent) {
    evt.target.setPointerCapture(evt.pointerId);
  } else {
    // Add Mouse Listeners
    document.addEventListener('mousemove', this.handleGestureMove, true);
    document.addEventListener('mouseup', this.handleGestureEnd, true);
  }

  initialTouchPos = getGesturePointFromEvent(evt);

  swipeFrontElement.style.transition = 'initial';
}.bind(this);

Cobalah

Callback akhir yang kita tambahkan adalah handleGestureEnd(), yang menghapus pemroses peristiwa gerakan dan akhir dari dokumen dan melepaskan tangkapan pointer saat gestur selesai seperti ini:

// Handle end gestures
this.handleGestureEnd = function(evt) {
  evt.preventDefault();

  if (evt.touches && evt.touches.length > 0) {
    return;
  }

  rafPending = false;

  // Remove Event Listeners
  if (window.PointerEvent) {
    evt.target.releasePointerCapture(evt.pointerId);
  } else {
    // Remove Mouse Listeners
    document.removeEventListener('mousemove', this.handleGestureMove, true);
    document.removeEventListener('mouseup', this.handleGestureEnd, true);
  }

  updateSwipeRestPosition();

  initialTouchPos = null;
}.bind(this);

Cobalah

Dengan mengikuti pola penambahan peristiwa gerakan ke dokumen ini, jika pengguna mulai berinteraksi dengan elemen dan memindahkan gesturnya ke luar elemen, kita akan terus mendapatkan gerakan mouse, terlepas dari lokasinya di halaman, karena peristiwa diterima dari dokumen.

Diagram ini menunjukkan apa yang dilakukan peristiwa sentuh saat kita menambahkan peristiwa gerakan dan akhir ke dokumen setelah gestur dimulai.

Mengilustrasikan peristiwa sentuh pengikatan untuk mendokumentasikan di
`touchstart`

Merespons sentuhan dengan efisien

Setelah kita membereskan peristiwa awal dan akhir, kita dapat benar-benar merespons peristiwa sentuh.

Untuk setiap peristiwa awal dan gerak, Anda dapat dengan mudah mengekstrak x dan y dari sebuah peristiwa.

Contoh berikut memeriksa apakah peristiwa berasal dari TouchEvent dengan memeriksa apakah targetTouches ada. Jika ya, kode ini akan mengekstrak clientX dan clientY dari sentuhan pertama. Jika peristiwanya adalah PointerEvent atau MouseEvent, peristiwa tersebut akan mengekstrak clientX dan clientY langsung dari peristiwa itu sendiri.

function getGesturePointFromEvent(evt) {
    var point = {};

    if (evt.targetTouches) {
      // Prefer Touch Events
      point.x = evt.targetTouches[0].clientX;
      point.y = evt.targetTouches[0].clientY;
    } else {
      // Either Mouse event or Pointer Event
      point.x = evt.clientX;
      point.y = evt.clientY;
    }

    return point;
  }

Cobalah

TouchEvent memiliki tiga daftar yang berisi data sentuh:

  • touches: daftar semua sentuhan saat ini di layar, terlepas dari elemen DOM tempatnya berada.
  • targetTouches: daftar sentuhan yang saat ini ada di elemen DOM tempat peristiwa terikat.
  • changedTouches: daftar sentuhan yang berubah sehingga menyebabkan peristiwa diaktifkan.

Umumnya, targetTouches memberikan semua yang Anda butuhkan dan inginkan. (Untuk mengetahui informasi selengkapnya tentang daftar ini, lihat Daftar sentuhan).

Menggunakan requestAnimationFrame

Karena callback peristiwa diaktifkan pada thread utama, kita ingin menjalankan sesedikit mungkin kode dalam callback untuk peristiwa kita, menjaga kecepatan bingkai tetap tinggi dan mencegah jank.

Dengan menggunakan requestAnimationFrame(), kita memiliki kesempatan untuk mengupdate UI tepat sebelum browser bermaksud menggambar frame dan akan membantu kita memindahkan beberapa tugas dari callback peristiwa.

Jika belum memahami requestAnimationFrame(), Anda dapat mempelajari lebih lanjut di sini.

Implementasi umum adalah dengan menyimpan koordinat x dan y dari peristiwa mulai dan gerak serta meminta frame animasi di dalam callback peristiwa gerak.

Dalam demo, kami menyimpan posisi sentuh awal di handleGestureStart() (cari initialTouchPos):

// Handle the start of gestures
this.handleGestureStart = function(evt) {
  evt.preventDefault();

  if (evt.touches && evt.touches.length > 1) {
    return;
  }

  // Add the move and end listeners
  if (window.PointerEvent) {
    evt.target.setPointerCapture(evt.pointerId);
  } else {
    // Add Mouse Listeners
    document.addEventListener('mousemove', this.handleGestureMove, true);
    document.addEventListener('mouseup', this.handleGestureEnd, true);
  }

  initialTouchPos = getGesturePointFromEvent(evt);

  swipeFrontElement.style.transition = 'initial';
}.bind(this);

Metode handleGestureMove() menyimpan posisi peristiwanya sebelum meminta frame animasi jika diperlukan, dengan meneruskan fungsi onAnimFrame() sebagai callback:

this.handleGestureMove = function (evt) {
  evt.preventDefault();

  if (!initialTouchPos) {
    return;
  }

  lastTouchPos = getGesturePointFromEvent(evt);

  if (rafPending) {
    return;
  }

  rafPending = true;

  window.requestAnimFrame(onAnimFrame);
}.bind(this);

Nilai onAnimFrame adalah fungsi yang saat dipanggil, akan mengubah UI untuk memindahkannya. Dengan meneruskan fungsi ini ke requestAnimationFrame(), kita memberitahu browser untuk memanggilnya sesaat sebelum memperbarui halaman (yaitu, menggambar setiap perubahan pada halaman).

Dalam callback handleGestureMove(), pada awalnya kami akan memeriksa apakah rafPending bernilai salah (false), yang menunjukkan apakah onAnimFrame() telah dipanggil oleh requestAnimationFrame() sejak peristiwa pemindahan terakhir. Ini berarti kita hanya memiliki satu requestAnimationFrame() yang menunggu dijalankan pada satu waktu.

Saat callback onAnimFrame() dijalankan, kita menetapkan transformasi pada elemen apa pun yang ingin dipindahkan sebelum memperbarui rafPending ke false, sehingga peristiwa sentuh berikutnya dapat meminta frame animasi baru.

function onAnimFrame() {
  if (!rafPending) {
    return;
  }

  var differenceInX = initialTouchPos.x - lastTouchPos.x;
  var newXTransform = (currentXPosition - differenceInX)+'px';
  var transformStyle = 'translateX('+newXTransform+')';

  swipeFrontElement.style.webkitTransform = transformStyle;
  swipeFrontElement.style.MozTransform = transformStyle;
  swipeFrontElement.style.msTransform = transformStyle;
  swipeFrontElement.style.transform = transformStyle;

  rafPending = false;
}

Mengontrol gestur menggunakan tindakan sentuh

Properti CSS touch-action memungkinkan Anda mengontrol perilaku sentuhan default dari elemen. Dalam contoh, kita menggunakan touch-action: none untuk mencegah browser melakukan tindakan apa pun dengan sentuhan pengguna, sehingga kita dapat mencegat semua peristiwa sentuh.

/* Pass all touches to javascript: */
button.custom-touch-logic {
  touch-action: none;
}

Menggunakan touch-action: none agak dianggap sebagai opsi nuklir karena mencegah semua perilaku browser default. Dalam banyak kasus, salah satu opsi di bawah ini adalah solusi yang lebih baik.

touch-action memungkinkan Anda menonaktifkan gestur yang diterapkan oleh browser. Misalnya, IE10+ mendukung gestur ketuk dua kali untuk zoom. Dengan menetapkan touch-action dari manipulation, Anda mencegah perilaku ketuk dua kali default.

Hal ini memungkinkan Anda mengimplementasikan gestur ketuk dua kali sendiri.

Di bawah ini adalah daftar nilai touch-action yang umum digunakan:

Parameter Tindakan Sentuh
touch-action: none Tidak ada interaksi sentuh yang akan ditangani oleh browser.
touch-action: pinch-zoom Menonaktifkan semua interaksi browser seperti `touch-action: none` selain `pinch-zoom`, yang masih ditangani oleh browser.
touch-action: pan-y pinch-zoom Tangani scroll horizontal di JavaScript tanpa menonaktifkan scroll vertikal atau zoom cubit (misalnya carousel gambar).
touch-action: manipulation Menonaktifkan gestur ketuk dua kali yang menghindari penundaan klik oleh browser. Membiarkan scroll dan cubit-perbesar bergantung pada browser.

Mendukung IE versi lama

Jika Anda ingin mendukung IE10, Anda harus menangani versi awalan vendor dari PointerEvents.

Untuk memeriksa dukungan PointerEvents, biasanya Anda akan mencari window.PointerEvent, tetapi di IE10, Anda akan mencari window.navigator.msPointerEnabled.

Nama peristiwa dengan awalan vendor adalah: 'MSPointerDown', 'MSPointerUp', dan 'MSPointerMove'.

Contoh di bawah ini menunjukkan cara memeriksa dukungan dan mengganti nama peristiwa.

var pointerDownName = 'pointerdown';
var pointerUpName = 'pointerup';
var pointerMoveName = 'pointermove';

if (window.navigator.msPointerEnabled) {
  pointerDownName = 'MSPointerDown';
  pointerUpName = 'MSPointerUp';
  pointerMoveName = 'MSPointerMove';
}

// Simple way to check if some form of pointerevents is enabled or not
window.PointerEventsSupport = false;
if (window.PointerEvent || window.navigator.msPointerEnabled) {
  window.PointerEventsSupport = true;
}

Untuk informasi selengkapnya, lihat artikel update dari Microsoft ini.

Referensi

Kelas pseudo untuk status sentuh

Kelas Contoh Deskripsi
:hover
Tombol dalam Status Ditekan
Dimasukkan saat kursor ditempatkan ke atas elemen. Perubahan UI pada saat kursor diarahkan ke atas elemen mendorong pengguna untuk berinteraksi dengan elemen.
:focus
Tombol dengan Status Fokus
Dimasukkan saat pengguna mengaktifkan tab melalui elemen pada halaman. Status fokus memungkinkan pengguna mengetahui elemen yang sedang berinteraksi dengan mereka; juga memungkinkan pengguna menavigasi UI Anda dengan mudah menggunakan keyboard.
:active
Tombol dalam Status Ditekan
Dimasukkan saat elemen sedang dipilih, misalnya, saat pengguna mengklik atau menyentuh elemen.

Referensi peristiwa sentuh definitif dapat ditemukan di sini: Peristiwa Sentuh W3C.

Peristiwa sentuh, mouse, dan pointer

Peristiwa ini adalah elemen penyusun untuk menambahkan gestur baru ke dalam aplikasi Anda:

Peristiwa Sentuhan, Mouse, Pointer
touchstart, mousedown, pointerdown Metode ini dipanggil saat jari pertama kali menyentuh elemen atau saat pengguna mengklik mouse.
touchmove, mousemove, pointermove Metode ini dipanggil saat pengguna menggerakkan jarinya di layar atau menarik dengan mouse.
touchend, mouseup, pointerup Metode ini dipanggil saat pengguna mengangkat jari dari layar atau melepaskan mouse.
touchcancel pointercancel Ini dipanggil saat browser membatalkan gestur sentuh. Misalnya, pengguna menyentuh aplikasi web, lalu berpindah tab.

Daftar sentuh

Setiap peristiwa sentuh berisi tiga atribut daftar:

Atribut Peristiwa Sentuh
touches Daftar semua sentuhan saat ini di layar, terlepas dari elemen yang disentuh.
targetTouches Daftar sentuhan yang dimulai pada elemen yang merupakan target peristiwa saat ini. Misalnya, jika Anda terikat ke <button>, Anda hanya akan mendapatkan sentuhan saat ini di tombol tersebut. Jika terikat ke dokumen, Anda akan mendapatkan semua sentuhan yang saat ini ada di dokumen.
changedTouches Daftar sentuhan yang berubah sehingga menyebabkan peristiwa diaktifkan:
  • Untuk peristiwa touchstart, daftar titik sentuh yang baru saja aktif dengan peristiwa saat ini.
  • Untuk peristiwa touchmove-- daftar titik sentuh yang telah berpindah sejak peristiwa terakhir.
  • Untuk peristiwa touchend dan touchcancel -- daftar titik sentuh yang baru saja dihapus dari permukaan.

Mengaktifkan dukungan status aktif di iOS

Sayangnya, Safari di iOS tidak menerapkan status active secara default. Untuk membuatnya berfungsi, Anda perlu menambahkan pemroses peristiwa touchstart ke body dokumen atau ke setiap elemen.

Anda harus melakukannya di belakang pengujian agen-pengguna sehingga hanya berjalan di perangkat iOS.

Menambahkan touch start ke body memiliki keuntungan karena diterapkan ke semua elemen dalam DOM, namun ini mungkin menyebabkan masalah kinerja saat men-scroll halaman.

window.onload = function() {
  if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
    document.body.addEventListener('touchstart', function() {}, false);
  }
};

Alternatifnya adalah dengan menambahkan pemroses awal sentuh ke semua elemen yang dapat berinteraksi di halaman, sehingga mengurangi beberapa masalah performa.

window.onload = function() {
  if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
    var elements = document.querySelectorAll('button');
    var emptyFunction = function() {};

    for (var i = 0; i < elements.length; i++) {
        elements[i].addEventListener('touchstart', emptyFunction, false);
    }
  }
};