Chrome Dev Summit 2018 is happening now and streaming live on YouTube. Watch now.

Menambahkan Sentuhan Ke Situs Anda

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

Merespons status elemen

Apakah Anda pernah menyentuh atau mengeklik elemen pada laman web dan bertanya-tanya apakah situs itu benar-benar mendeteksi tindakan tersebut?

Cukup ubah warna elemen saat pengguna menyentuh atau berinteraksi dengan bagian UI untuk memberikan jaminan kembali bahwa situs Anda bekerja. Hal ini tidak hanya mengurangi frustrasi, tapi juga bisa memberikan kesan cepat dan responsif.

Elemen DOM bisa mewarisi salah satu status berikut: default, focus, hover dan active. Untuk mengubah UI masing-masing status, kita perlu menerapkan gaya ke kelas 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 surpresses the border
  color / outline when focused */
  outline: 0;
}

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

Cobalah

Gambar yang mengilustrasikan perbedaan warna untuk status
tombol

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

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

Meredam gaya browser default

Setelah Anda menambahkan gaya untuk status yang berbeda, Anda akan melihat bahwa kebanyakan browser mengimplementasikan gaya mereka sendiri dalam merespons sentuhan pengguna. Hal ini terutama karena saat pertama kali perangkat seluler diluncurkan, sejumlah situs tidak memiliki penataan gaya untuk status :active. Akibatnya, banyak browser menambahkan warna sorot atau gaya tambahan untuk memberikan masukan bagi pengguna.

Kebanyakan browser menggunakan properti CSS outline untuk menampilkan sebuah lingkaran di sekeliling elemen saat elemen difokuskan. Anda bisa meredamnya dengan:

.btn:focus {
  outline: 0;

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

Safari dan Chrome menambahkan warna sorot ketuk yang bisa 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, namun diredam melalui tag meta:

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

Firefox memiliki dua efek samping yang harus diatasi.

Kelas pseudo -moz-focus-inner, yang menambahkan outline pada elemen bisa disentuh, dapat Anda buang dengan menyetel border: 0.

Jika Anda menggunakan elemen <button> pada Firefox, Anda mendapat penerapan sebuah gradien, yang bisa Anda buang 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

Perhatian: Hanya redam gaya default yang disebutkan di atas jika Anda memiliki kelas-kelas pseudo untuk :hover, :active dan :focus!

Menonaktifkan user-select

Ketika membuat UI, mungkin ada skenario di mana Anda menginginkan pengguna agar berinteraksi dengan elemen namun ingin Anda redam perilaku defaultnya yaitu memilih teks dengan tekan lama atau menyeret mouse di atas UI.

Anda bisa melakukannya dengan properti CSS user-select, tapi waspadalah bahwa melakukan ini pada materi bisa sangat menyebalkan pengguna jika mereka ingin memilih teks dalam elemen. Jadi, pastikan Anda menggunakannya dengan hati-hati dan secukupnya.

user-select: none;

Mengimplementasikan isyarat khusus

Jika Anda memiliki gagasan untuk interaksi dan isyarat khusus untuk situs Anda, ada dua hal yang harus diingat:

  1. Cara mendukung semua browser.
  2. Cara menjaga laju bingkai Anda tetap tinggi.

Pada artikel ini, kita akan melihat secara seksama topik mencakup API yang harus kita dukung untuk masuk ke semua browser dan kemudian membahas bagaimana menggunakan kejadian ini dengan efisien.

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

Perhatian: Jangan lupa bahwa beberapa pengguna menginginkan masukan keyboard dan pengguna yang menjalankan teknologi bantu pada perangkat layar sentuh mungkin tidak mampu melakukan isyarat karena mereka dicegat/dikonsumsi oleh teknologi bantu.

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

Contoh GIF sentuh pada dokumen

Contoh pertama memungkinkan pengguna untuk berinteraksi dengan satu elemen. Untuk kasus ini Anda mungkin menginginkan semua kejadian sentuh diberikan kepada satu elemen, selama isyarat itu awalnya dimulai pada elemen itu sendiri. Misalnya, memindahkan jari dari elemen yang bisa-digesek, masih tetap bisa mengontrol elemen.

Hal ini berguna karena menyediakan banyak fleksibilitas bagi pengguna, tetapi memberlakukan pembatasan tentang bagaimana pengguna bisa berinteraksi dengan UI Anda.

Contoh GIF sentuh pada elemen

Namun, jika Anda berharap pengguna berinteraksi dengan beberapa elemen pada saat yang bersamaan (menggunakan multi-touch), Anda harus membatasi sentuhan ke elemen tertentu.

Ini lebih fleksibel bagi pengguna, namun mempersulit logika untuk memanipulasi UI dan kurang tahan terhadap kesalahan pengguna.

Menambahkan event listener

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

Di browser lainnya TouchEvents dan MouseEvents adalah pendekatan yang tepat.

Fitur menarik dari PointerEvents adalah bahwa itu menggabungkan beberapa tipe masukan, termasuk kejadian mouse, sentuh dan pena, menjadi satu rangkaian callback. Kejadian yang didengarkan adalah pointerdown, pointermove, pointerup dan pointercancel.

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

Jika Anda memiliki pertanyaan tentang kejadian apa yang sebaiknya digunakan, silakan lihat tabel ini Kejadian sentuh, mouse dan pointer).

Menggunakan kejadian ini membutuhkan pemanggilan metode addEventListener() pada elemen DOM, bersama dengan nama kejadian, fungsi callback dan boolean. Boolean menentukan apakah Anda harus menangkap kejadian sebelum atau sesudah elemen lain memiliki kesempatan untuk menangkap dan menginterpretasikan kejadian. (true berarti Anda menginginkan kejadian 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 awal event listener untuk kejadian mouse. Alasan untuk hal ini adalah karena kejadian mouse hanya akan dipicu ketika kursor di arahkan ke atas elemen yang ditambahkan event listener.

TouchEvents akan melacak isyarat setelah dimulai tanpa menghiraukan dari mana kejadian sentuh terjadi dan PointerEvents akan melacak kejadian tanpa menghiraukan dari mana kejadian sentuh terjadi saat kita memanggil setPointerCapture pada elemen DOM.

Untuk gerakan mouse dan kejadian akhir, kita menambahkan event listener di metode awal isyarat dan menambahkan listener ke dokumen, sehingga bisa melacak kursor sampai isyarat selesai.

Langkah-langkah yang harus diimplementasikan adalah:

  1. Tambahkan semua listener TouchEvent dan PointerEvent. Untuk MouseEvents tambahkan hanya kejadian awal.
  2. Di dalam callback isyarat awal, ikat gerakan mouse dan kejadian akhir ke dokumen. Dengan cara ini semua kejadian mouse akan diterima terlepas dari apakah kejadian itu terjadi pada elemen asli atau tidak. Untuk PointerEvents kita perlu memanggil setPointerCapture() pada elemen asli untuk menerima semua kejadian lebih lanjut. Kemudian tangani awal isyarat tersebut.
  3. Tangani kejadian gerak.
  4. Pada kejadian akhir, buang gerakan mouse dan listener akhir dari dokumen dan mengakhiri isyarat itu.

Berikut adalah cuplikan metode handleGestureStart() kami yang menambahkan kejadian gerak dan kejadian 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 membuang event listener gerak dan event listener akhir dari dokumen dan melepaskan tangkapan penunjuk ketika isyarat telah selesai seperti:

// 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 menambahkan kejadian gerak ke dokumen ini, jika pengguna mulai berinteraksi dengan elemen dan menggerakkan isyarat mereka di luar dari elemen, kita akan terus mendapatkan gerakan mouse terlepas dari posisi mereka di laman, karena kejadian sedang diterima dari dokumen.

Diagram ini menunjukkan apa yang dilakukan kejadian sentuh saat kita menambahkan kejadian gerak dan kejadian akhir ke dokumen setelah isyarat dimulai.

Mengilustrasikan kejadian sentuh mengikat ke dokumen pada
klzzwxh:0051

Merespons sentuhan dengan efisien

Sekarang setelah kita membereskan kejadian awal dan akhir, kita bisa dengan benar merespons kejadian sentuh.

Untuk setiap kejadian awal dan gerak, Anda bisa dengan mudah mengekstrak x dan y dari sebuah kejadian.

Contoh berikut memeriksa apakah sebuah kejadian berasal dari TouchEvent dengan memeriksa jika terdapat targetTouches. Jika memang begitu, maka itu mengekstrak clientX dan clientY dari sentuhan pertama. Jika kejadiannya adalah PointerEvent atau MouseEvent, itu mengekstrak clientX dan clientY langsung dari kejadian 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

Sebuah TouchEvent memiliki tiga daftar yang berisi data sentuh:

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

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

Menggunakan requestAnimationFrame

Karena callback kejadian diaktifkan pada thread utama, kita ingin menjalankan sesedikit mungkin kode dalam callback untuk kejadian kita, menjaga laju bingkai tetap tinggi dan mencegah sampah.

Dengan menggunakan requestAnimationFrame() kita memiliki kesempatan untuk memperbarui UI sesaat sebelum browser bermaksud menggambar bingkai dan akan memudahkan kita memindahkan beberapa pekerjaan dari callback kejadian.

Jika Anda belum familier dengan requestAnimationFrame(), Anda bisa mempelajari selengkapnya di sini.

Implementasi khusus adalah dengan menyimpan koordinat x dan y dari kejadian awal dan kejadian gerak serta meminta bingkai animasi di dalam callback kejadian gerak.

Dalam demo, kami menyimpan posisi sentuh awal di handleGestureStart() (carilah 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 kejadiannya sebelum meminta bingkai animasi bila kita membutuhkannya, 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 sebuah fungsi yang bila dipanggil, akan mengubah UI untuk bergerak. Dengan meneruskan fungsi ini ke requestAnimationFrame(), kita memberitahu browser untuk memanggilnya sesaat sebelum itu memperbarui laman (yaitu, menggambarkan setiap perubahan ke laman).

Di callback handleGestureMove() kami awalnya memeriksa apakah rafPending bernilai false, yang menunjukkan jika onAnimFrame() telah dipanggil oleh requestAnimationFrame() sejak kejadian gerak terakhir. Ini berarti kita hanya memiliki satu requestAnimationFrame() yang menunggu dijalankan pada satu waktu.

Ketika callback onAnimFrame() dijalankan, kita menyetel transformasi pada setiap elemen yang ingin dipindah sebelum memperbarui rafPending ke false, memungkinkan kejadian sentuh berikutnya untuk meminta bingkai 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 isyarat menggunakan tindakan sentuh

Properti CSS touch-action memungkinkan Anda untuk mengontrol perilaku sentuh default dari elemen. Dalam contoh, kami menggunakan touch-action: none untuk mencegah browser melakukan sesuatu dengan sentuhan pengguna, yang memungkinkan kita untuk mencegat semua kejadian sentuh.

/* Pass all touches to javascript */
touch-action: none;

Menggunakan touch-action: none bisa dikatakan opsi ekstrem karena mencegah semua perilaku browser default. Umumnya, salah satu opsi di bawah ini merupakan solusi yang lebih baik.

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

Hal ini memungkinkan Anda untuk mengimplementasikan isyarat ketuk dua kali Anda sendiri.

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

Parameter Aksi 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 dari `pinch-zoom`, yang masih ditangani oleh browser.
touch-action: pan-y pinch-zoom Menangani gulir horizontal dalam JavaScript tanpa menonaktifkan gulir vertikal atau cubit-untuk-zoom (mis. korsel gambar).
touch-action: manipulation Menonaktifkan isyarat ketuk dua kali untuk menghindari penundaan klik oleh browser. Membiarkan gulir dan cubit-untuk-zoom ditentukan browser.

Mendukung IE versi lama

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

Untuk memeriksa dukungan PointerEvents biasanya Anda mencari window.PointerEvent, namun dalam IE10, Anda akan mencari window.navigator.msPointerEnabled.

Nama kejadian dengan awalan vendor adalah: 'MSPointerDown', 'MSPointerUp' and 'MSPointerMove'.

Contoh di bawah ini menunjukkan kepada Anda cara memeriksa dukungan dan mengganti nama kejadian.

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, periksa pembaruan artikel dari Microsoft.

Referensi

Kelas pseudo untuk status sentuh

Kelas Contoh Keterangan
:hover Tombol dengan Status Ditekan Dimasukkan ketika kursor ditempatkan ke atas sebuah elemen. Perubahan UI pada saat kursor di arahkan ke atas elemen mendorong pengguna untuk berinteraksi dengan elemen.
:focus Tombol dengan Status Focus Dimasukkan saat pengguna mengaktifkan tab melalui elemen pada laman. Status focus memungkinkan pengguna untuk mengetahui elemen yang sedang berinteraksi dengan mereka; juga memungkinkan pengguna untuk menavigasi UI dengan mudah menggunakan keyboard.
:active Tombol dengan Status Ditekan Dimasukkan saat elemen sedang dipilih, misalnya, ketika pengguna mengeklik atau menyentuh elemen.

Referensi kejadian sentuh definitif bisa dilihat disini: Kejadian Sentuh w3.

Kejadian sentuh, mouse, dan pointer

Kejadian ini adalah blok pembangun untuk menambahkan isyarat baru ke dalam aplikasi Anda:

Kejadian Sentuh, Mouse, dan Pointer
touchstart, mousedown, pointerdown Ini dipanggil ketika jari pertama menyentuh elemen atau ketika pengguna mengeklik mouse.
touchmove, mousemove, pointermove Ini dipanggil ketika pengguna menggerakkan jari mereka di layar atau menyeret dengan mouse.
touchend, mouseup, pointerup Ini dipanggil ketika pengguna mengangkat jari mereka dari layar atau melepaskan mouse.
touchcancel pointercancel Ini dipanggil ketika browser membatalkan isyarat sentuh. Misalnya, pengguna menyentuh aplikasi web dan kemudian berpindah tab.

Daftar sentuh

Setiap kejadian sentuh berisi tiga atribut daftar:

Atribut Kejadian Sentuh
touches Daftar semua sentuhan saat ini di layar, terlepas dari elemen yang disentuh.
targetTouches Daftar sentuhan yang dimulai pada elemen yang merupakan target dari kejadian 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 saat ini pada dokumen.
changedTouches Daftar sentuhan yang berubah sehingga menyebabkan kejadian diaktifkan.

Mengaktifkan dukungan status aktif pada iOS

Sayangnya, Safari di iOS tidak menerapkan status active secara default, untuk membuatnya bekerja Anda perlu menambahkan event listener touchstart ke body dokumen atau ke setiap elemen.

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

Menambahkan touch start ke body memiliki keuntungan karena mengaplikasikan ke semua elemen dalam DOM, namun ini mungkin menyebabkan masalah kinerja ketika menggulir laman.

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

Alternatif-nya adalah dengan menambahkan listener touch start ke semua elemen bisa-berinteraksi di laman, mengurangi beberapa masalah kinerja.

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