Việc trỏ vào các mục trên web từng rất đơn giản. Bạn có một con chuột, bạn di chuyển con chuột đó, đôi khi bạn nhấn các nút, và thế là xong. Mọi thứ không phải là chuột đều được mô phỏng như một con chuột và nhà phát triển biết chính xác những gì cần dựa vào.
Tuy nhiên, đơn giản không nhất thiết là tốt. Theo thời gian, việc không phải mọi thứ đều là (hoặc giả vờ là) chuột ngày càng trở nên quan trọng: bạn có thể có bút cảm ứng áp lực và nhận biết độ nghiêng để có được sự tự do sáng tạo tuyệt vời; bạn có thể sử dụng ngón tay, vì vậy, tất cả những gì bạn cần là thiết bị và bàn tay; và tại sao không sử dụng nhiều ngón tay trong khi bạn đang làm việc?
Chúng tôi đã có sự kiện chạm trong một thời gian để giúp chúng tôi giải quyết vấn đề đó, nhưng đó là một API hoàn toàn riêng biệt dành riêng cho thao tác chạm, buộc bạn phải lập trình hai mô hình sự kiện riêng biệt nếu bạn muốn hỗ trợ cả chuột và thao tác chạm. Chrome 55 được cung cấp theo một tiêu chuẩn mới hơn kết hợp cả hai mô hình: sự kiện con trỏ.
Mô hình sự kiện đơn
Sự kiện con trỏ hợp nhất mô hình nhập bằng con trỏ cho trình duyệt, kết hợp thao tác chạm, bút cảm ứng và chuột vào một nhóm sự kiện. Ví dụ:
document.addEventListener('pointermove',
ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
ev => console.log('The pointer is now over foo.'));
Dưới đây là danh sách tất cả các sự kiện hiện có. Danh sách này sẽ khá quen thuộc nếu bạn đã quen với các sự kiện chuột:
pointerover
|
Con trỏ đã vào hộp giới hạn của phần tử.
Điều này xảy ra ngay lập tức đối với các thiết bị hỗ trợ tính năng di chuột hoặc trước sự kiện pointerdown đối với các thiết bị không hỗ trợ tính năng này.
|
pointerenter
|
Tương tự như pointerover , nhưng không tạo bong bóng và xử lý các phần tử con theo cách khác.
Thông tin chi tiết về thông số kỹ thuật.
|
pointerdown
|
Con trỏ đã chuyển sang trạng thái nút đang hoạt động, với một nút đang được nhấn hoặc tiếp xúc được thiết lập, tuỳ thuộc vào ngữ nghĩa của thiết bị đầu vào. |
pointermove
|
Con trỏ đã thay đổi vị trí. |
pointerup
|
Con trỏ đã rời khỏi trạng thái nút đang hoạt động. |
pointercancel
|
Đã xảy ra sự cố khiến con trỏ khó có thể phát thêm sự kiện nào. Điều này có nghĩa là bạn nên huỷ mọi thao tác đang diễn ra và quay lại trạng thái nhập trung tính. |
pointerout
|
Con trỏ đã rời khỏi hộp giới hạn của phần tử hoặc màn hình. Ngoài ra, sau một pointerup , nếu thiết bị không hỗ trợ thao tác di chuột.
|
pointerleave
|
Tương tự như pointerout , nhưng không tạo bong bóng và xử lý các phần tử con theo cách khác.
Thông tin chi tiết về thông số kỹ thuật.
|
gotpointercapture
|
Phần tử đã nhận được tính năng chụp con trỏ. |
lostpointercapture
|
Con trỏ đang được chụp đã được phát hành. |
Các loại dữ liệu đầu vào
Nhìn chung, Sự kiện con trỏ cho phép bạn viết mã theo cách không phân biệt phương thức nhập, mà không cần đăng ký trình xử lý sự kiện riêng biệt cho các thiết bị nhập khác nhau.
Tất nhiên, bạn vẫn cần lưu ý đến sự khác biệt giữa các loại dữ liệu đầu vào, chẳng hạn như liệu khái niệm di chuột có áp dụng hay không. Nếu muốn phân biệt các loại thiết bị đầu vào – có thể là để cung cấp mã/chức năng riêng biệt cho các loại đầu vào – bạn có thể thực hiện việc này trong cùng một trình xử lý sự kiện bằng cách sử dụng thuộc tính pointerType
của giao diện PointerEvent
. Ví dụ: nếu đang lập trình ngăn điều hướng bên, bạn có thể sử dụng logic sau đây cho sự kiện pointermove
:
switch(ev.pointerType) {
case 'mouse':
// Do nothing.
break;
case 'touch':
// Allow drag gesture.
break;
case 'pen':
// Also allow drag gesture.
break;
default:
// Getting an empty string means the browser doesn't know
// what device type it is. Let's assume mouse and do nothing.
break;
}
Thao tác mặc định
Trong các trình duyệt hỗ trợ cảm ứng, một số cử chỉ nhất định được dùng để cuộn, thu phóng hoặc làm mới trang.
Trong trường hợp sự kiện chạm, bạn vẫn sẽ nhận được sự kiện trong khi các thao tác mặc định này đang diễn ra – ví dụ: touchmove
vẫn sẽ được kích hoạt trong khi người dùng đang cuộn.
Với các sự kiện con trỏ, bất cứ khi nào một thao tác mặc định như cuộn hoặc thu phóng được kích hoạt, bạn sẽ nhận được một sự kiện pointercancel
để cho bạn biết rằng trình duyệt đã kiểm soát con trỏ. Ví dụ:
document.addEventListener('pointercancel',
ev => console.log('Go home, the browser is in charge now.'));
Tốc độ tích hợp: Theo mặc định, mô hình này cho phép hiệu suất tốt hơn, so với các sự kiện chạm, trong đó bạn cần sử dụng trình nghe sự kiện thụ động để đạt được cùng một mức độ phản hồi.
Bạn có thể ngăn trình duyệt kiểm soát bằng thuộc tính CSS touch-action
. Việc đặt thuộc tính này thành none
trên một phần tử sẽ vô hiệu hoá tất cả các thao tác do trình duyệt xác định bắt đầu trên phần tử đó. Tuy nhiên, có một số giá trị khác để kiểm soát chi tiết hơn, chẳng hạn như pan-x
, cho phép trình duyệt phản ứng với chuyển động trên trục x nhưng không phải trục y. Chrome 55 hỗ trợ các giá trị sau:
auto
|
Mặc định; trình duyệt có thể thực hiện bất kỳ hành động mặc định nào. |
none
|
Trình duyệt không được phép thực hiện bất kỳ hành động mặc định nào. |
pan-x
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn theo chiều ngang theo mặc định. |
pan-y
|
Trình duyệt chỉ được phép thực hiện hành động mặc định cuộn theo chiều dọc. |
pan-left
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn ngang mặc định và chỉ được kéo trang sang trái. |
pan-right
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn ngang mặc định và chỉ được kéo trang sang phải. |
pan-up
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn dọc mặc định và chỉ được phép kéo trang lên. |
pan-down
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn dọc mặc định và chỉ được phép kéo trang xuống. |
manipulation
|
Trình duyệt chỉ được phép thực hiện thao tác cuộn và thu phóng. |
Chụp con trỏ
Bạn đã bao giờ dành một giờ bực bội để gỡ lỗi sự kiện mouseup
bị hỏng, cho đến khi nhận ra rằng đó là do người dùng thả nút ra ngoài mục tiêu nhấp của bạn? Bạn không thấy đúng không? Được rồi, có thể chỉ có tôi.
Tuy nhiên, cho đến nay, vẫn chưa có cách giải quyết hiệu quả cho vấn đề này. Chắc chắn, bạn có thể thiết lập trình xử lý mouseup
trên tài liệu và lưu một số trạng thái trên ứng dụng để theo dõi mọi thứ. Tuy nhiên, đó không phải là giải pháp rõ ràng nhất, đặc biệt là nếu bạn đang xây dựng một thành phần web và cố gắng giữ mọi thứ gọn gàng và tách biệt.
Với các sự kiện con trỏ, bạn có một giải pháp tốt hơn nhiều: bạn có thể chụp con trỏ để đảm bảo nhận được sự kiện pointerup
đó (hoặc bất kỳ sự kiện nào khác khó nắm bắt).
const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
console.log('Button down, capturing!');
// Every pointer has an ID, which you can read from the event.
foo.setPointerCapture(ev.pointerId);
});
foo.addEventListener('pointerup',
ev => console.log('Button up. Every time!'));
Hỗ trợ trình duyệt
Tại thời điểm viết bài, các Sự kiện con trỏ được hỗ trợ trong Internet Explorer 11, Microsoft Edge, Chrome và Opera, đồng thời được hỗ trợ một phần trong Firefox. Bạn có thể tìm thấy danh sách mới nhất tại caniuse.com.
Bạn có thể sử dụng Pointer Events polyfill để điền vào các khoảng trống. Ngoài ra, bạn có thể kiểm tra tính năng hỗ trợ trình duyệt trong thời gian chạy một cách đơn giản:
if (window.PointerEvent) {
// Yay, we can use pointer events!
} else {
// Back to mouse and touch events, I guess.
}
Sự kiện con trỏ là một ứng cử viên tuyệt vời để cải tiến dần dần: bạn chỉ cần sửa đổi các phương thức khởi chạy để kiểm tra ở trên, thêm trình xử lý sự kiện con trỏ trong khối if
và di chuyển trình xử lý sự kiện chuột/chạm vào khối else
.
Hãy dùng thử và cho chúng tôi biết ý kiến của bạn!