Thực hiện thao tác cuộn nhanh theo mặc định

Dave Tapuska
Dave Tapuska

Chúng tôi biết rằng khả năng phản hồi khi cuộn rất quan trọng đối với tương tác của người dùng với một trang web trên thiết bị di động, nhưng các 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 thụ động (chuyển tuỳ chọn {passive: true} cho addEventListener()) và truyền 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 thấy khó hiểu và áp dụng chúng.

Chúng tôi tin rằng theo mặc định, web phải có tốc độ nhanh mà không cần nhà phát triển cần hiểu chi tiết phức tạp về hành vi của trình duyệt. Trong Chrome 56, chúng tôi thiết lập mặc định các trình nghe thụ động theo mặc định trong những trường hợp thường xuyên nhất phù hợ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 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, sự thay đổi này có thể dẫn đến việc cuộn ngoài ý muốn. Vấn đề này thường được giải quyết dễ dàng bằng cách áp dụng kiểu thao tác nhấn: none (không có) cho phần tử không nên cuộn. Hãy đọc tiếp để biết thông tin chi tiết, cách để biết bạn có bị ảnh hưởng hay không và cách xử lý.

Thông tin cơ bản: Sự kiện có thể huỷ sẽ làm chậm trang của bạn

Nếu gọi preventDefault() trong các sự kiện touchstart hoặc touchmove đầu tiên thì bạn sẽ ngăn không cho cuộn trang. Vấn đề là hầu hết 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 điều đó. "Trình nghe sự kiện thụ động" do nhà phát triển định nghĩa sẽ giải quyết vấn đề này. Khi thêm một sự kiện chạm có đối tượng {passive: true} làm tham số thứ ba trong trình xử lý sự kiện, thì bạn đang thông báo cho trình duyệt rằng trình nghe touchstart sẽ không gọi preventDefault() và trình duyệt có thể thực hiện việ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 tính năng khởi động bằng thao tác chạm và di chuyển, 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 sự kiện chạm có thể huỷ được gửi tới 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 trong số này là thụ động về mặt lý thuyết nhưng không được đăng ký như vậy. Dựa trên quy mô của vấn đề này, chúng tôi nhận thấy cơ hội lớn để cải thiện tính năng cuộn mà không cần nhà phát triển hành động bằng cách tự động biến các sự kiện này thành "thụ động".

Điều này khiến chúng tôi xác định rõ sự can thiệp của mình như sau: nếu mục tiêu của trình nghe Touchstart hoặc Touchmove là window, document hoặc body, chúng ta sẽ đặt passive mặc định là true. Điều này có nghĩa là mã như:

window.addEventListener("touchstart", func);

trở thành tương đương với:

window.addEventListener("touchstart", func, {passive: true} );

Bây giờ, các lệnh gọi đến preventDefault() bên trong trình nghe sẽ bị bỏ qua.

Biểu đồ dưới đây cho thấy thời gian 1% lượt cuộn cao nhất tính từ thời điểm người dùng chạm vào màn hình để cuộn đến thời điểm cập nhật màn hình. Dữ liệu này dành cho tất cả các trang web trong Chrome dành cho Android. Trước khi áp dụng biện pháp can thiệp, 1% lượt cuộn chỉ mất hơn 400 mili giây. Thời lượng đó hiện đã giảm xuống chỉ 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 thụ động làm đúng chế độ mặc định cho tất cả trình nghe touchstarttouchmove, giảm thời lượng này xuống dưới 50 mili giây.

Biểu đồ thời gian cuộn 1% hàng đầu

Sự cố và hướng dẫn

Trong hầu hết các trường hợp, chúng tôi sẽ không quan sát thấy sự cố nào. Nhưng khi sự cố xảy ra, triệu chứng phổ biến nhất là 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 chuột không mong muốn (khi thiếu preventDefault() trong trình nghe touchend).

Trên Chrome 56 trở lên, Công cụ cho nhà phát triển sẽ ghi lại một cảnh báo khi bạn gọi preventDefault() trong trường hợp 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 liệu ứng dụng có gặp vấn đề này hay không bằng cách kiểm tra xem việc gọi preventDefault có ảnh hưởng gì thông qua thuộc tính defaultPrevented hay không.

Chúng tôi nhận thấy rằng phần lớn các trang bị ảnh hưởng đều được khắc phục tương đối dễ dàng bằng cách áp dụng thuộc tính CSS hành động nhấn bất cứ khi nào có thể. Nếu bạn muốn ngăn tất cả các trình duyệt cuộn và thu phóng trong một phần tử, hãy áp dụng touch-action: none cho phần tử đó. Nếu bạn có 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 đúng thao tác chạm trên các trình duyệt như Edge trên máy tính có hỗ trợ Sự kiện con trỏ chứ không phải Sự kiện chạm. Đối với trình duyệt Safari trên thiết bị di động và các trình duyệt cũ dành cho thiết bị di động không hỗ trợ thao tác chạm, trình nghe thao tác chạm phải tiếp tục gọi preventDefault ngay cả khi Chrome sẽ bỏ qua trình nghe này.

Trong những trường hợp phức tạp hơn, có thể cần phải dựa vào một trong những phương thức sau:

  • Nếu trình nghe touchstart gọi preventDefault(), hãy đảm bảo preventDefault() cũng được gọi từ các trình nghe cuối màn hình được liên kết để tiếp tục chặn việc tạo sự kiện nhấp chuột và 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 xem Tác nhân người dùng có hỗ trợ EventListenerOptions hay không.

Kết luận

Trong Chrome 56, tính năng 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 các nhà phát triển sẽ nhận thấy từ 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 ngoài ý muốn.

Mặc dù bạn vẫn cần thực hiện việc này cho 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 touchstarttouchmove vì điều này không còn được đảm bảo sẽ được áp dụng 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ử mà trong đó phải tắt tính năng cuộn và thu phóng để thông báo cho trình duyệt trước khi xảy ra bất kỳ sự kiện chạm nào. Để loại bỏ hành vi mặc định của một lần nhấn (chẳng hạn như việc tạo sự kiện nhấp chuột), hãy gọi preventDefault() bên trong trình nghe touchend.