Chúng ta biết rằng khả năng phản hồi khi cuộn là yếu tố quan trọng đối với mức độ tương tác của người dùng với trang web trên thiết bị di động, tuy nhiên, trình nghe sự kiện chạm thường gây ra các vấn đề nghiêm trọng về hiệu suất cuộn. Chrome đã giải quyết vấn đề này bằng cách cho phép trình nghe sự kiện chạm ở trạng thái thụ động (chuyển tuỳ chọn {passive: true}
đến addEventListener()
) và gửi API sự kiện con trỏ.
Đây là những tính năng tuyệt vời để đưa nội dung mới vào các mô hình không chặn thao tác cuộn, nhưng đôi khi nhà phát triển lại khó hiểu và áp dụng các tính năng này.
Chúng tôi tin rằng web phải nhanh theo mặc định mà không cần nhà phát triển phải hiểu rõ các chi tiết phức tạp về hành vi của trình duyệt. Trong Chrome 56, chúng tôi đặt mặc định trình nghe cảm ứng ở chế độ thụ động trong những trường hợp thường khớp với ý định của nhà phát triển. Chúng tôi tin rằng bằng cách này, chúng tôi có thể cải thiện đáng kể trải nghiệm của người dùng trong khi vẫn giảm thiểu tác động tiêu cực đến các trang web.
Trong một số ít trường hợp, thay đổi này có thể dẫn đến việc cuộn không mong muốn. Bạn thường có thể dễ dàng giải quyết vấn đề này bằng cách áp dụng kiểu touch-action: none cho phần tử không được cuộn. Hãy đọc tiếp để biết thông tin chi tiết, cách biết liệu bạn có bị ảnh hưởng hay không và những việc bạn có thể làm.
Nền: Sự kiện có thể huỷ làm chậm trang của bạn
Nếu gọi preventDefault() trong sự kiện touchstart
hoặc touchmove
đầu tiên, thì bạn sẽ ngăn việc cuộn.
Vấn đề là hầu hết các trình nghe sẽ không gọi preventDefault()
, nhưng trình duyệt cần phải đợi sự kiện kết thúc để chắc chắn về điều đó.
"Trình nghe sự kiện thụ động" do nhà phát triển xác định sẽ giải quyết vấn đề này. Khi thêm một sự kiện chạm với đối tượng {passive: true}
làm tham số thứ ba trong trình xử lý sự kiện, bạn đang cho trình duyệt biết rằng trình nghe touchstart
sẽ không gọi preventDefault()
và trình duyệt có thể thực hiện thao tác cuộn một cách an toàn mà không chặn trình nghe. Ví dụ:
window.addEventListener("touchstart", func, {passive: true} );
Can thiệp
Động lực chính của chúng tôi là giảm thời gian cập nhật màn hình sau khi người dùng chạm vào màn hình. Để hiểu rõ cách sử dụng touchstart và touchmove, chúng tôi đã thêm các chỉ số để xác định tần suất xảy ra hành vi chặn cuộn.
Chúng tôi đã xem xét tỷ lệ phần trăm các sự kiện chạm có thể huỷ được gửi đến mục tiêu gốc (cửa sổ, tài liệu hoặc nội dung) và xác định rằng khoảng 80% trình nghe này về mặt khái niệm là thụ động nhưng không được đăng ký như vậy. Do quy mô của vấn đề này, chúng tôi nhận thấy đây là cơ hội tuyệt vời để cải thiện tính năng cuộn mà không cần nhà phát triển làm gì cả bằng cách tự động đặt các sự kiện này thành "bị động".
Điều này đã thúc đẩy chúng tôi xác định biện pháp can thiệp của mình là: nếu mục tiêu của trình nghe touchstart hoặc touchmove là window
, document
hoặc body
, thì chúng tôi sẽ đặt mặc định passive
thành true
. Điều này có nghĩa là mã như:
window.addEventListener("touchstart", func);
sẽ tương đương với:
window.addEventListener("touchstart", func, {passive: true} );
Giờ đây, các lệnh gọi đến preventDefault()
bên trong trình nghe sẽ bị bỏ qua.
Biểu đồ bên dưới cho thấy thời gian của 1% lượt cuộn hàng đầu kể từ thời điểm người dùng chạm vào màn hình để cuộn đến thời điểm màn hình được cập nhật. Dữ liệu này áp dụng cho tất cả trang web trong Chrome dành cho Android. Trước khi bật biện pháp can thiệp, 1% lượt cuộn chỉ mất hơn 400 mili giây. Giờ đây, thời gian này đã giảm xuống còn hơn 250 mili giây trong Chrome 56 Beta; giảm khoảng 38%. Trong tương lai, chúng tôi hy vọng có thể đặt giá trị mặc định là giá trị rỗng cho tất cả trình nghe touchstart
và touchmove
, giảm thời gian này xuống dưới 50 mili giây.

Hư hỏng và hướng dẫn
Trong hầu hết các trường hợp, bạn sẽ không thấy sự cố nào. Tuy nhiên, khi sự cố xảy ra, triệu chứng phổ biến nhất là việc cuộn xảy ra khi bạn không muốn. Trong một số ít trường hợp, nhà phát triển cũng có thể nhận thấy các sự kiện nhấp không mong muốn
(khi preventDefault()
bị thiếu trong trình nghe touchend
).
Trong Chrome 56 trở lên, DevTools sẽ ghi lại một cảnh báo khi bạn gọi preventDefault()
trong một sự kiện mà biện pháp can thiệp đang hoạt động.
touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
Ứng dụng của bạn có thể xác định xem ứng dụng có thể gặp phải lỗi này trong thực tế hay không bằng cách kiểm tra xem việc gọi preventDefault
có ảnh hưởng gì không thông qua thuộc tính defaultPrevented
.
Chúng tôi nhận thấy rằng phần lớn các trang bị ảnh hưởng có thể được khắc phục tương đối dễ dàng bằng cách áp dụng thuộc tính CSS touch-action bất cứ khi nào có thể. Nếu bạn muốn ngăn tất cả thao tác cuộn và thu phóng của trình duyệt trong một phần tử, hãy áp dụng touch-action: none
cho phần tử đó. Nếu bạn có một băng chuyền ngang, hãy cân nhắc áp dụng touch-action: pan-y pinch-zoom
cho băng chuyền đó để người dùng vẫn có thể cuộn theo chiều dọc và thu phóng như bình thường. Bạn cần áp dụng thao tác chạm chính xác trên các trình duyệt như Edge dành cho máy tính hỗ trợ Sự kiện con trỏ chứ không phải Sự kiện chạm. Đối với Safari dành cho thiết bị di động và các trình duyệt di động cũ không hỗ trợ thao tác chạm, trình nghe chạm của bạn phải tiếp tục gọi preventDefault
ngay cả khi Chrome bỏ qua thao tác này.
Trong các trường hợp phức tạp hơn, bạn cũng có thể cần phải dựa vào một trong những cách sau:
- Nếu trình nghe
touchstart
gọipreventDefault()
, hãy đảm bảo rằng preventDefault() cũng được gọi từ các trình nghe touchend được liên kết để tiếp tục ngăn chặn việc tạo sự kiện nhấp và các hành vi nhấn mặc định khác. - Cuối cùng (và không nên) chuyển
{passive: false}
đến addEventListener() để ghi đè hành vi mặc định. Xin lưu ý rằng bạn sẽ phải phát hiện tính năng nếu Agent hỗ trợ EventListenerOptions.
Kết luận
Trong Chrome 56, thao tác cuộn bắt đầu nhanh hơn đáng kể trên nhiều trang web. Đây là tác động duy nhất mà hầu hết nhà phát triển sẽ nhận thấy sau thay đổi này. Trong một số trường hợp, nhà phát triển có thể nhận thấy thao tác cuộn không mong muốn.
Mặc dù vẫn cần phải làm như vậy đối với Safari dành cho thiết bị di động, nhưng các trang web không nên dựa vào việc gọi preventDefault()
bên trong trình nghe touchstart
và touchmove
vì điều này không còn được đảm bảo sẽ được thực hiện trong Chrome. Nhà phát triển nên áp dụng thuộc tính CSS touch-action
trên các phần tử cần tắt tính năng cuộn và thu phóng để thông báo cho trình duyệt trước khi có bất kỳ sự kiện chạm nào xảy ra.
Để ngăn hành vi mặc định của một thao tác nhấn (chẳng hạn như tạo sự kiện nhấp), hãy gọi preventDefault()
bên trong trình nghe touchend
.