Szybkie przewijanie dotykiem domyślnie

Dave Tapuska
Dave Tapuska

Wiemy, że responsywność przewijania ma kluczowe znaczenie dla zaangażowania użytkowników w witrynę mobilną, ale detektor zdarzeń dotyku często powoduje poważne problemy z działaniem przewijania. Chrome rozwiązywał ten problem, zezwalając detektorom zdarzeń dotyku na bierne (przekazując opcję {passive: true} do addEventListener()) i wysyłając interfejs API zdarzenia wskaźnika. To świetne funkcje tworzenia nowych treści w modelach, które nie blokują przewijania. Deweloperzy czasami mają problemy z ich zrozumieniem i wdrożeniem.

Naszym zdaniem internet powinien działać szybko, a deweloperzy nie muszą rozumieć szczegółów dotyczących działania przeglądarek. W Chrome 56 domyślnie słuchacze dotykowe są ustawione na pasywne w sytuacjach, gdy jest to najczęściej zgodne z intencją dewelopera. Uważamy, że w ten sposób można znacznie poprawić komfort korzystania z witryny, a jednocześnie zminimalizować ich negatywny wpływ na witryny.

W rzadkich przypadkach ta zmiana może spowodować niezamierzone przewijanie. Zwykle można sobie z tym łatwo poradzić, stosując do elementu, w którym nie powinno być przewijania, styl dotknięcia: brak. Czytaj dalej, aby dowiedzieć się, jak sprawdzić, czy ten problem Cię dotyczy i co możesz z tym zrobić.

W tle: wydarzenia możliwe do odwołania spowalniają działanie strony

Jeśli wywołasz funkcję preventDefault() w zdarzeniu touchstart lub w pierwszych touchmove zdarzeniach, uniemożliwisz przewijanie. Problem polega na tym, że detektory najczęściej nie wywołują funkcji preventDefault(), ale aby mieć pewność, że to się stanie, przeglądarka musi zaczekać na zakończenie zdarzenia. Rozwiązują to zdefiniowane przez dewelopera „pasywne detektory zdarzeń”. Gdy dodasz zdarzenie dotknięcia z obiektem {passive: true} jako trzecim parametrem w module obsługi zdarzeń, informujesz przeglądarkę, że detektor touchstart nie wywoła funkcji preventDefault(), a przeglądarka może bezpiecznie przewijać strony bez blokowania go w module. Na przykład:

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

Interwencja

Zależy nam na skróceniu czasu potrzebnego na aktualizację wyświetlacza po dotknięciu ekranu. Aby rozumieć zastosowanie funkcji uruchamiania dotykiem i przesuwania palcem, pojawiły się dane pozwalające określić, jak często występowało blokowanie przewijania.

Sprawdziliśmy odsetek możliwych do anulowania zdarzeń dotyku, które zostały wysłane do elementu głównego (okna, dokumentu lub treści), i stwierdziliśmy, że około 80% z tych słuchaczy jest koncepcyjnie biernych, ale nie zostało w ten sposób zarejestrowanych jako takich. Biorąc pod uwagę skalę tego problemu, zauważyliśmy, że warto usprawnić przewijanie bez konieczności podejmowania działań przez dewelopera przez automatyczne ustawienie takich zdarzeń jako „pasywnych”.

Dlatego postanowiliśmy określić zakres naszej interwencji w taki sposób: jeśli celem detektora Touchstart lub touchmove jest window, document lub body passive ma wartość true. Oznacza to, że kod w rodzaju:

window.addEventListener("touchstart", func);

jest odpowiednikiem:

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

Teraz wywołania preventDefault() w detektorze będą ignorowane.

Poniższy wykres przedstawia czas potrzebny na przewinięcie przez 1% górnych przewinięć od chwili dotknięcia ekranu przez użytkownika, aby przewinąć go do czasu jego aktualizacji. Te dane dotyczą wszystkich witryn w Chrome na Androida. Przed włączeniem interwencji 1% przewinięć trwało nieco ponad 400 ms. W Chrome 56 w wersji beta czas ten został zmniejszony do nieco ponad 250 ms, czyli o około 38%. Mamy nadzieję, że w przyszłości ustawienie pasywne będzie miało wartość domyślną dla wszystkich słuchaczy touchstart i touchmove, skracając czas trwania do 50 ms.

Wykres 1% wyświetleń z góry

Naprawa i wskazówki

W większości przypadków nie można zaobserwować uszkodzenia. Kiedy jednak dochodzi do usterek, najczęstszym objawem jest to, że przewija się, gdy tego nie chcesz. W rzadkich przypadkach deweloperzy mogą też zauważyć nieoczekiwane zdarzenia kliknięcia (gdy brakuje parametru preventDefault() w detektorze touchend).

W Chrome w wersji 56 i nowszych Narzędzia deweloperskie będą rejestrować ostrzeżenie, gdy wywołasz funkcję preventDefault() w przypadku zdarzenia, w którym interwencja jest aktywna.

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 może docierać do użytkowników na zewnątrz, sprawdzając za pomocą właściwości defaultPrevented, czy wywołanie metody preventDefault miało jakiś wpływ.

Zauważyliśmy, że sporą większość problematycznych stron można stosunkowo łatwo rozwiązać, stosując w miarę możliwości właściwość CSS działanie polegające na dotknięciu. Jeśli chcesz zapobiec przewijaniu i powiększaniu elementu w przeglądarce, zastosuj do niego touch-action: none. Jeśli masz poziomą karuzelę, możesz zastosować do niej element touch-action: pan-y pinch-zoom, aby użytkownik mógł nadal przewijać tekst w pionie i powiększać go w zwykły sposób. Prawidłowe zastosowanie działań dotykowych jest już konieczne w przeglądarkach takich jak Edge na komputerze, które obsługują zdarzenia wskaźnika, a nie zdarzenia dotyku. W przeglądarkach mobilnych Safari i starszych przeglądarek mobilnych, które nie obsługują reakcji na dotyk, detektory dotykowe muszą nadal wywoływać preventDefault, nawet jeśli Chrome będzie ją ignorować.

W bardziej złożonych przypadkach może być konieczne zastosowanie jednej z poniższych opcji:

  • Jeśli detektor touchstart wywołuje metodę preventDefault(), upewnij się, że funkcja preeventDefault() jest też wywoływana przez powiązane detektory dotknięcia, aby nadal blokować generowanie zdarzeń kliknięć i inne domyślne działania związane z kliknięciami.
  • Ostatni (i odradzamy) przekazanie metody {passive: false} do metody addEventListener() w celu zastąpienia działania domyślnego. Pamiętaj, że konieczne będzie wykrywanie funkcji, czy klient użytkownika obsługuje EventListenerOptions.

Podsumowanie

W Chrome 56 na wielu stronach przewijanie rozpoczyna się znacznie szybciej. To jedyny wpływ tej zmiany, jaki zauważy większość deweloperów. W niektórych przypadkach deweloperzy mogą zauważyć niezamierzone przewijanie.

Chociaż jest to nadal konieczne w przypadku Safari na komórki, witryny nie powinny polegać na wywoływaniu preventDefault() w obu słuchaczach w touchstart i touchmove, ponieważ nie gwarantujemy, że będzie to już uwzględniane w Chrome. Deweloperzy powinni stosować właściwość CSS touch-action do elementów, w przypadku których przewijanie i powiększanie powinno być wyłączone, aby przeglądarka powiadamiała przeglądarkę o zdarzeniach dotknięcia. Aby pominąć domyślne działanie kliknięcia (takie jak generowanie zdarzenia kliknięcia), wywołaj preventDefault() w detektorze touchend.