Szybkie przewijanie dotykiem domyślnie

Dave Tapuska
Dave Tapuska

Wiemy, że responsywność przewijania ma kluczowe znaczenie dla zaangażowania użytkownika w witrynę na urządzeniu mobilnym, ale odsłuchi zdarzeń dotykowych często powodują poważne problemy z wydajnością przewijania. Chrome rozwiązuje ten problem, pozwalając słuchaczom zdarzeń dotykowych na bycie biernymi (przekazując opcję {passive: true} do addEventListener()) i wysyłając interfejs API zdarzeń wskaźnika. To świetne funkcje, które umożliwiają dodawanie nowych treści do modeli, które nie blokują przewijania, ale deweloperzy mają czasem problemy z ich zrozumieniem i wdrożeniem.

Uważamy, że internet powinien być szybki domyślnie, bez konieczności zrozumienia przez programistów skomplikowanych szczegółów działania przeglądarki. W Chrome 56 domyślnie detektory są pasywne w przypadkach, gdy jest to zgodne z zamiarem dewelopera. Wierzymy, że dzięki temu możemy znacznie poprawić wrażenia użytkowników, przy minimalnym negatywnym wpływie na witryny.

W rzadkich przypadkach ta zmiana może spowodować niezamierzone przewijanie. Zwykle można to łatwo rozwiązać, stosując styl touch-action: none do elementu, w którym nie powinno być przewijania. Czytaj dalej, aby dowiedzieć się więcej o tym, jak sprawdzić, czy dotyczy to Ciebie, i co możesz zrobić.

Informacje ogólne: zdarzenia anulowalne spowalniają stronę

Jeśli wywołasz metodę preventDefault() w zdarzeniu touchstart lub pierwszym zdarzeniu touchmove, uniemożliwisz przewijanie. Problem polega na tym, że w większości przypadków detektory nie wywołają funkcji preventDefault(), ale przeglądarka musi poczekać na zakończenie zdarzenia, aby się o tym przekonać. Rozwiązaniem są zdefiniowane przez deweloperów „pasywne detektory zdarzeń”. Gdy dodasz zdarzenie dotyku z obiektem {passive: true} jako 3 parametrem w obiekcie event, informujesz przeglądarkę, że odbiorca touchstart nie wywoła metody preventDefault(), a przeglądarka może bezpiecznie przeprowadzić przewijanie bez blokowania odbiornika. Na przykład:

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

Interwencja

Naszym głównym celem jest skrócenie czasu potrzebnego na zaktualizowanie wyświetlacza po dotknięciu ekranu przez użytkownika. Aby lepiej zrozumieć użycie zdarzeń touchstart i touchmove, dodaliśmy dane, które pozwalają określić, jak często występuje blokowanie przewijania.

Sprawdziliśmy odsetek zdarzeń dotykowych z możliwością anulowania, które zostały wysłane do obiektu docelowego wyższego poziomu (okna, dokumentu lub treści), i ustaliliśmy, że około 80% z nich jest teoretycznie pasywnych, ale nie zostało zarejestrowanych jako takie. Biorąc pod uwagę skalę tego problemu, zauważyliśmy, że istnieje świetna możliwość ulepszenia przewijania bez konieczności podejmowania przez programistów żadnych działań, ponieważ te zdarzenia są automatycznie „pasywne”.

W związku z tym zdefiniowaliśmy interwencję w ten sposób, że jeśli obiekt docelowy odbiornika touchstart lub touchmove to window, document lub body, domyślnie ustawiamy passive na true. Oznacza to, że kod taki jak:

window.addEventListener("touchstart", func);

staje się równoważne:

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

Teraz wywołania do preventDefault() w słuchaczu będą ignorowane.

Wykres poniżej pokazuje czas, jaki zajmuje 1% najdłuższych przewijań od momentu dotknięcia ekranu do momentu jego zaktualizowania. Te dane dotyczą wszystkich witryn w Chrome na Androida. Zanim włączono interwencję, przewijanie 1% strony trwało nieco ponad 400 ms. W Chrome 56 Beta czas ten został skrócony do nieco ponad 250 ms, co oznacza spadek o około 38%. Mamy nadzieję, że w przyszłości wszystkie touchstarttouchmove będą domyślnie pasywne, co skróci czas ich działania do mniej niż 50 ms.

Wykres Czas przewijania dla 1% najdłuższych czasów

Uszkodzenia i wskazówki

W większości przypadków nie dochodzi do żadnych naruszeń. Jednak gdy dochodzi do awarii, najczęściej objawia się to przewijaniem, gdy tego nie chcesz. W rzadkich przypadkach deweloperzy mogą też zauważyć nieoczekiwane zdarzenia click (gdy w słuchaczu touchend brakuje argumentu preventDefault()).

W Chrome 56 i nowszych wersjach Narzędzia programistyczne będą rejestrować ostrzeżenie, gdy wywołasz funkcję preventDefault() w przypadku aktywnej interwencji.

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

Aplikacja może określić, czy jest to problem występujący w naturze, sprawdzając, czy wywołanie funkcji preventDefault miało jakikolwiek wpływ na element defaultPrevented.

Zauważyliśmy, że większość dotkniętych stron można stosunkowo łatwo naprawić, stosując w miarę możliwości właściwość touch-action w CSS. Jeśli chcesz zablokować przewijanie i powiększanie w przeglądarce w danym elemencie, zastosuj do niego touch-action: none. Jeśli masz poziomy karuzelę, rozważ zastosowanie do niej touch-action: pan-y pinch-zoom, aby użytkownik mógł normalnie przewijać i powiększać obraz w kierunku pionowym. Właściwe zastosowanie atrybutu touch-action jest już konieczne w przypadku przeglądarek takich jak Edge na komputery, które obsługują zdarzenia wskaźnika, a nie zdarzenia dotyku. W przypadku przeglądarki mobilnej Safari i starszych przeglądarek mobilnych, które nie obsługują akcji dotykowej, klasy do obsługi zdarzeń dotykowych muszą nadal wywoływać funkcję preventDefault, nawet jeśli zostanie ona zignorowana przez Chrome.

W bardziej złożonych przypadkach może być konieczne użycie jednej z tych opcji:

  • Jeśli Twój detektor touchstart wywołuje funkcję preventDefault(), upewnij się, że funkcja preventDefault() jest również wywoływana przez powiązane detektory touchend, aby nadal blokować generowanie zdarzeń kliknięcia i inne domyślne zachowania dotyku.
  • Ostatnia (niezalecana) forma przekazania parametru {passive: false} do funkcji addEventListener() służy do zastąpienia domyślnego zachowania. Pamiętaj, że musisz użyć funkcji wykrywania, aby sprawdzić, czy agent użytkownika obsługuje EventListenerOptions.

Podsumowanie

W Chrome 56 przewijanie staje się znacznie szybsze na wielu stronach internetowych. Jest to jedyny wpływ, jaki ta zmiana będzie miała na większość deweloperów. W niektórych przypadkach deweloperzy mogą zauważyć niezamierzone przewijanie.

Chociaż w przypadku przeglądarki Safari na urządzenia mobilne nadal jest to konieczne, witryny nie powinny polegać na wywoływaniu funkcji preventDefault() w metodach touchstarttouchmove, ponieważ nie ma już gwarancji, że będą one obsługiwane w Chrome. Deweloperzy powinni zastosować właściwość CSS touch-action do elementów, w których przypadku przewijanie i powiększanie powinno być wyłączone, aby powiadomić przeglądarkę przed wystąpieniem zdarzeń dotykowych. Aby stłumić domyślne działanie kliknięcia (np. generowanie zdarzenia kliknięcia), wywołaj funkcję preventDefault() w detektorze touchend.