Tambahkan sentuhan ke situs Anda

Layar sentuh tersedia di semakin banyak perangkat, dari ponsel hingga layar desktop. Aplikasi Anda harus merespons sentuhan mereka dengan cara yang intuitif dan indah.

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

Merespons status elemen

Pernahkah Anda menyentuh atau mengklik elemen pada halaman web dan bertanya-tanya apakah situs tersebut 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 nuansa yang cepat dan responsif.

Elemen DOM dapat mewarisi salah satu status berikut: default, fokus, arahkan kursor, dan aktif. Untuk mengubah UI bagi setiap status ini, kita harus menerapkan gaya ke class pseudo berikut :hover, :focus, dan :active seperti yang ditunjukkan 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 warna yang berbeda untuk status
tombol

Pada sebagian besar browser seluler, status hover dan/atau focus akan diterapkan ke elemen setelah diketuk.

Pertimbangkan dengan cermat gaya yang Anda tetapkan dan tampilannya bagi pengguna setelah menyelesaikan sentuhan mereka.

Menyembunyikan gaya browser default

Setelah menambahkan gaya untuk status yang berbeda, Anda akan melihat bahwa sebagian besar browser mengimplementasikan gaya mereka sendiri sebagai respons terhadap sentuhan pengguna. Hal ini sebagian besar karena saat perangkat seluler pertama kali diluncurkan, sejumlah situs tidak memiliki gaya 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 sekitar elemen saat elemen difokuskan. Anda dapat menyembunyikannya dengan:

.btn:focus {
    outline: 0;

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

Safari dan Chrome menambahkan warna sorotan 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 di Windows Phone memiliki perilaku yang serupa, tetapi disembunyikan melalui tag meta:

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

Firefox memiliki dua efek samping yang perlu ditangani.

Class pseudo -moz-focus-inner, yang menambahkan outline pada elemen yang dapat disentuh, dapat Anda hapus dengan menetapkan border: 0.

Jika menggunakan elemen <button> di Firefox, Anda akan mendapatkan penerapan gradien, yang dapat dihapus dengan menyetel 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 beberapa skenario ketika Anda ingin pengguna berinteraksi dengan elemen, tetapi Anda ingin menyembunyikan perilaku default pemilihan teks dengan menekan lama atau menarik mouse ke atas UI Anda.

Anda dapat melakukannya dengan properti CSS user-select, tetapi berhati-hatilah karena melakukan hal ini pada konten dapat extremely menjengkelkan bagi pengguna jika mereka ingin memilih teks dalam elemen. Jadi, pastikan Anda menggunakannya dengan hati-hati dan hemat.

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

Mengimplementasikan gestur khusus

Jika Anda memiliki ide untuk interaksi dan gestur kustom 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 topik yang membahas API yang perlu kita dukung untuk membuka semua browser lalu membahas cara menggunakan peristiwa ini secara efisien.

Bergantung pada gestur yang diinginkan, 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, yang menunjukkan dukungan untuk semua browser dan cara menjaga kecepatan frame tetap tinggi.

Contoh GIF sentuh pada dokumen

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

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

Contoh GIF sentuhan pada elemen

Namun, jika Anda mengharapkan pengguna berinteraksi dengan beberapa elemen pada saat yang sama (menggunakan multi-sentuh), Anda harus membatasi sentuhan ke elemen tertentu.

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

Menambahkan pemroses peristiwa

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

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

Fitur hebat 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.

Yang setara di browser lain adalah touchstart, touchmove, touchend, dan touchcancel untuk peristiwa sentuh, dan jika ingin menerapkan gestur yang sama untuk input mouse, Anda harus mengimplementasikan mousedown, mousemove, dan mouseup.

Jika ada pertanyaan tentang peristiwa mana yang akan 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. (true berarti Anda menginginkan peristiwa sebelum elemen lainnya.)

Berikut adalah contoh 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 singkat di atas, kami hanya menambahkan pemroses peristiwa awal untuk peristiwa mouse. Alasannya adalah peristiwa mouse hanya akan dipicu saat kursor diarahkan ke atas elemen yang ditambahi pemroses peristiwa.

TouchEvents akan melacak gestur setelah dimulai terlepas dari mana pun sentuhan terjadi dan PointerEvents akan melacak peristiwa di mana pun terjadinya sentuhan 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, sehingga pemroses tersebut dapat melacak kursor hingga gestur selesai.

Langkah-langkah yang diambil untuk menerapkannya adalah:

  1. Menambahkan 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 tersebut 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 pemindahan.
  4. Pada peristiwa akhir, hapus gerakan mouse dan pemroses akhir dari dokumen, lalu akhiri gestur.

Berikut adalah cuplikan metode handleGestureStart() yang menambahkan peristiwa pemindahan dan peristiwa 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 gerak dan peristiwa akhir dari dokumen serta melepaskan rekaman 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 pemindahan ke dokumen ini, jika pengguna mulai berinteraksi dengan elemen dan menggerakkan gestur di luar elemen, kita akan terus mendapatkan gerakan mouse terlepas dari posisinya di halaman karena peristiwa tersebut diterima dari dokumen.

Diagram ini menunjukkan fungsi peristiwa sentuh saat kita menambahkan peristiwa pindahkan dan akhiri ke dokumen setelah gestur dimulai.

Mengilustrasikan peristiwa sentuh binding ke dokumen di
`touchstart`

Merespons sentuhan secara efisien

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

Untuk peristiwa awal dan pemindahan, Anda dapat dengan mudah mengekstrak x dan y dari peristiwa.

Contoh berikut memeriksa apakah peristiwa berasal dari TouchEvent dengan memeriksa apakah targetTouches ada atau tidak. Jika ya, kode akan mengekstrak clientX dan clientY dari sentuhan pertama. Jika peristiwa berupa PointerEvent atau MouseEvent, peristiwa 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 terikat pada elemen DOM yang dikaitkan oleh peristiwa.
  • changedTouches: daftar sentuhan yang berubah sehingga menghasilkan peristiwa diaktifkan.

Dalam kebanyakan kasus, targetTouches memberikan semua yang Anda butuhkan dan inginkan. (Untuk mengetahui informasi selengkapnya tentang daftar ini, lihat Daftar sentuh).

Menggunakan requestAnimationFrame

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

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

Jika tidak terbiasa dengan requestAnimationFrame(), Anda dapat mempelajari lebih lanjut di sini.

Implementasi standar adalah menyimpan koordinat x dan y dari peristiwa awal dan memindahkan, serta meminta frame animasi di dalam callback peristiwa pemindah.

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 perlu, 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 memberi tahu browser untuk memanggilnya tepat sebelum memperbarui halaman (yaitu, menampilkan perubahan apa pun ke halaman).

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

Saat callback onAnimFrame() dieksekusi, kita menetapkan transformasi pada elemen apa pun yang ingin dipindahkan sebelum memperbarui rafPending ke false, sehingga peristiwa sentuh berikutnya 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 sentuh default sebuah elemen. Dalam contoh ini, kita menggunakan touch-action: none untuk mencegah browser melakukan 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 terbilang opsi ekstrem karena mencegah semua perilaku browser default. Dalam banyak kasus, salah satu opsi di bawah ini merupakan solusi yang lebih baik.

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

Hal ini memungkinkan Anda untuk menerapkan gestur ketuk dua kali sendiri.

Berikut 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 mencubit untuk memperbesar tampilan browser.

Mendukung IE versi lama

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

Untuk memeriksa dukungan PointerEvents, Anda biasanya akan mencari window.PointerEvent, tetapi dalam 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 mengetahui informasi selengkapnya, baca artikel pembaruan dari Microsoft ini.

Referensi

Class pseudo untuk status sentuh

Class Contoh Deskripsi
:hover
Tombol dalam Status Ditekan
Dimasukkan saat kursor ditempatkan di atas elemen. Perubahan UI saat kursor diarahkan berguna untuk mendorong pengguna berinteraksi dengan elemen.
.fokus
Tombol dengan Status Fokus
Dimasukkan saat pengguna menekan 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.
:aktif
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 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 jarinya dari layar atau melepaskan mouse.
touchcancel pointercancel Metode ini dipanggil saat browser membatalkan gestur sentuh. Misalnya, pengguna menyentuh aplikasi web, lalu mengubah tab.

Daftar sentuh

Setiap peristiwa sentuh menyertakan tiga atribut daftar:

Atribut Peristiwa Sentuh
touches Daftar semua sentuhan saat ini pada layar, terlepas dari elemen yang disentuh.
targetTouches Daftar sentuhan yang dimulai pada elemen yang merupakan target peristiwa saat ini. Misalnya, jika Anda mengikat ke <button>, Anda hanya akan mendapatkan sentuhan saat ini pada tombol tersebut. Jika terikat ke dokumen, Anda akan mendapatkan semua sentuhan saat ini pada dokumen.
changedTouches Daftar sentuhan yang berubah sehingga menyebabkan peristiwa diaktifkan:
  • Untuk peristiwa touchstart-- daftar titik sentuh yang baru saja menjadi 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 platform.

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 isi dokumen atau ke setiap elemen.

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

Menambahkan touch start ke isi memiliki keuntungan karena diterapkan ke semua elemen dalam DOM, tetapi hal ini mungkin menyebabkan masalah performa 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 sentuhan mulai 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);
    }
  }
};