Wyobraź sobie taką sytuację: użytkownik wychodzi z twojej strony, ale ty chcesz go koniecznie zatrzymać, oczywiście w dobrej wierze. Wypełnił formularz, ale jeszcze go nie wysłał, a może nie zapisał swojego projektu graficznego lub edytowanego właśnie zdjęcia. Zapomniał o komentarzu, który zaczął pisać, ale nie skończył. Nie kupił produktu, choć rozpoczął proces zakupowy. Odtwarzał muzykę albo film, nie skończył czytać artykułu. Albo zdążył kliknąć „zapisz”, ale już nie poczekał na faktyczny zapis na serwerze… W takich problematycznych sytuacjach często wspiera nas loader lub funkcjonalność automatycznego zapisywania zmian. Jednak zdarza się, że chcemy otrzymać od użytkownika świadome potwierdzenie jego intencji wyjścia ze strony. Co wtedy? Co robić, aby… się nie narobić?
Dziś wyjątkowo nie będę ciebie zmuszać do myślenia. Ot, powiem ci, tak po prostu, jaki mam na to pomysł. Panie, Panowie – WindowEventHandlers.onbeforeunload.
Najpierw teoria…
Magiczna własność onbeforeunload
to metoda obsługi zdarzenia beforeunload
występującego w momencie opuszczania witryny przez użytkownika, tuż przed zdarzeniem unload
. Mówiąc o opuszczeniu witryny, mam na myśli zarówno zamknięcie zakładki, przeładowanie strony, jak i kliknięcie na odnośnik. W trakcie stanu beforeunload
możemy sprawdzić, czy wszystkie dane zostały zapisane i poprosić użytkownika o potwierdzenie chęci zamknięcia strony. Dokument jest jeszcze widoczny dla użytkownika, a proces zamykania strony może zostać anulowany (zdarzenie unload
nie nastąpi).
Z kolei zdarzenie unload
wywołuje się, kiedy dokument jest już w stanie rozładowywania zasobów. Witryna nie jest widoczna dla użytkownika, interakcje z UI są niemożliwe, a żaden błąd nie zatrzyma procesu zamykania strony. Podczas tego etapu można jednak wysłać statystyki witryny za pomocą navigator.sendBeacon(url, data)
.
Tak więc wiemy już, że możemy poprosić użytkownika o potwierdzenie wyjścia ze strony w stanie beforeunload
. Jednakże musimy pamiętać, że nie zmienimy treści komunikatu wyświetlanego użytkownikowi. Żadna szanująca się przeglądarka na to nie pozwoli ze względów bezpieczeństwa. Tak więc różne przeglądarki obsłużą ten przypadek w różny sposób. Dla przykładu – poniżej porównanie Chrome oraz Firefox.
… a teraz praktyka.
Czas na przykład. Oczywiście w React <3.
class Prompt extends React.Component {
componentDidMount() {
window.addEventListener('beforeunload', this.beforeunload.bind(this));
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.beforeunload.bind(this));
}
beforeunload(e) {
if (this.props.dataUnsaved) {
e.preventDefault();
e.returnValue = true;
}
}
render() {...}
}
Dla jasności wyjaśnijmy sobie kluczowy fragment kodu:
e.preventDefault();
e.returnValue = true;
Czemu tak, a nie inaczej?
Otóż pierwszy sposób zatrzymania procesu wychodzenia ze strony – e.preventDefault()
– to sposób obecnie zalecany przez specyfikację HTML. Jednak… nie jest on wspierany przez wszystkie przeglądarki. Dlatego też powinno się jednocześnie korzystać z drugiego sposobu wstrzymania stanu beforeunload
, czyli e.returnValue = true
. Dawno temu, w szalonych, niebezpiecznych czasach, do returnValue
przypisywano tekst, który miał pojawić się w komunikacie. Obecnie to nie jest możliwe, jednak nadal korzysta się z własności returnValue
– przypisanie do niej jakiejkolwiek wartości innej niż pusty string daje gwarancję potwierdzenia zamknięcia strony przez użytkownika na każdej przeglądarce.
To na tyle na dziś! Tym razem bez zakończenia rodem z Nocy Kabaretowej (szkoda czasu na kawały, Netflix wrzucił NA RAZ 4 sezony The Bold Type). Mam nadzieję, że powyższa wiedza okaże się dla kogoś nie tylko ciekawa, ale też przydatna!