Zdarzenie wyjściowe przechwytywania strony

117

Podczas edycji strony w moim systemie użytkownik może zdecydować się na przejście do innej witryny internetowej, a robiąc to, może utracić wszystkie zmiany, których nie zapisał.

Chciałbym przechwycić każdą próbę przejścia do innej strony i poprosić użytkownika, aby był pewien, że chce, aby tak się stało, ponieważ może potencjalnie stracić swoją bieżącą pracę.

Gmail robi to w bardzo podobny sposób. Na przykład utwórz nową wiadomość e-mail, zacznij pisać w treści wiadomości i wprowadź nową lokalizację w pasku adresu (np. Twitter.com lub coś w tym stylu). Zostanie wyświetlony monit z pytaniem „Czy na pewno?”

Pomysły, jak to powtórzyć? Celuję w IE8, ale chciałbym być również kompatybilny z FF i Chrome.

Chris Ballance
źródło

Odpowiedzi:

154

Podobnie jak odpowiedź Ghommeya, ale obsługuje również starsze wersje przeglądarek IE i Firefox.

window.onbeforeunload = function (e) {
  var message = "Your confirmation message goes here.",
  e = e || window.event;
  // For IE and Firefox
  if (e) {
    e.returnValue = message;
  }

  // For Safari
  return message;
};
Eli Grey
źródło
1
Czy mógłbyś wyjaśnić pierwszą linię (var message = ...)? Nie wiem, co robi ten przecinek i drugie wyrażenie.
mtmurdock
7
@mtmurdock To po prostu składnia Javascript do deklarowania wielu zmiennych. var foo, bar;jest taki sam jakvar foo; var bar;
T Nguyen
4
@mtmurdock, Drugie stwierdzenie " var e = e || window.event;" oznacza ustawienie e równe parametrowi e (jeśli jest prawdziwe) lub window.event, jeśli nie jest prawdziwe. Nie będzie prawdą, jeśli parametr e jest zerowy.
T Nguyen
3
@Blieque addEventListener nie jest obsługiwana w IE8. Nie edytuj odpowiedzi innych osób, nawet nie czytając pytania.
Eli Gray,
2
Nie działa już w Chrome (wycofane): developers.google.com/web/updates/2016/04/…
Micha Schwab
25

Zobacz ten artykuł. Funkcja, której szukasz, to funkcja przed pobraniem

przykładowy kod:

  <script language="JavaScript">
  window.onbeforeunload = confirmExit;
  function confirmExit()
  {
    return "You have attempted to leave this page.  If you have made any changes to the fields without clicking the Save button, your changes will be lost.  Are you sure you want to exit this page?";
  }
</script>
jantimon
źródło
2
Czy istnieje sposób, aby zidentyfikować użytkownika, który wybrał opcję „Opuść tę stronę” zamiast „Pozostań na tej stronie”?
Shilpa Soni
@ShilpaSoni wkrótce otrzymasz kolejne zdarzenie wyładowania, developer.mozilla.org/en-US/docs/Web/API/Window/unload_event, ale nie sądzę, aby istniał synchroniczny sposób.
scipilot
6

Zamiast irytującego wyskakującego okienka potwierdzenia, byłoby miło odłożyć trochę czasu (kwestia milisekund) na pomyślne wysłanie niezapisanych danych na serwer, co udało mi się dla mojej witryny, zapisując fikcyjny tekst na konsoli w następujący sposób:

window.onbeforeunload=function(e){
  // only take action (iterate) if my SCHEDULED_REQUEST object contains data        
  for (var key in SCHEDULED_REQUEST){   
    postRequest(SCHEDULED_REQUEST); // post and empty SCHEDULED_REQUEST object
    for (var i=0;i<1000;i++){
      // do something unnoticable but time consuming like writing a lot to console
      console.log('buying some time to finish saving data'); 
    };
    break;
  };
}; // no return string --> user will leave as normal but data is send to server

Edycja: Zobacz także Synchronous_AJAX i jak to zrobić za pomocą jquery

Remi
źródło
Podoba mi się ta alternatywa dla wyskakującego okienka. Dzięki za sugestię, Remi.
Chris Ballance
2
to nie ma absolutnie żadnego sensu -1. javascript nie jest podzielony na wątki, co robisz, to wykonanie żądania postRequest, a następnie wstrzymanie obliczeń witryny, nie pozwalając nikomu nic robić (łącznie z postRequest, które w międzyczasie zostało wysłane przed przerwą obliczeniową).
fmsf,
I właśnie dlatego to działa: rzeczywiście post-request został wysłany przed przerwą obliczeniową, ale to małe blokowanie strony przed faktycznym jej opuszczeniem zapewnia klientowi przeglądarki (nie javascript) wystarczająco dużo czasu, aby naprawdę zakończyć wysyłanie tego żądania poprawnie . Zauważyłem, że gdy tylko wysyłam żądanie (bez blokowania) i zaraz po opuszczeniu strony, żądanie nie zawsze jest wysyłane.
Remi
6
Nie użyłbym tego w produkcji. console.log nie jest crossbrowser; każdy postRequest cierpi z powodu opóźnienia poprzednich (co oznacza również, że strona będzie się tam zawieszać na czas trwania Twojej pętli * zaplanowane żądania); żądania nie są uruchamiane równolegle; nie masz gwarancji, że naprawdę wyślesz swoje prośby. Gdybym był zmuszony zmierzyć się z tym problemem, poszedłbym z synchronicznym żądaniem Ajax, jeśli jest tylko jedno żądanie. W przypadku wielu żądań użyłbym asynchronicznych żądań ajax i pętli, która sprawdza wyniki (sukces lub błąd) z wywołań zwrotnych żądań.
framp
Nie wiedziałem, że console.log nie jest między przeglądarkami, a synchroniczne żądanie Ajax powinno być rzeczywiście preferowanym rozwiązaniem (dla pojedynczego żądania). Wspomniany SCHEDULED_REQUESTobiekt, który gromadzi wszelkie niepilne informacje, które należy przesłać na serwer, jak bufor danych, skutecznie łącząc wiele żądań w jedno (oczywiście adres URL tych żądań jest taki sam). Jak powiedziałeś, użycie synchronicznego żądania Ajax przed wyładowaniem powinno rozwiązać problem między przeglądarkami. Dziękuję za cenny komentarz!
Remi,
0

Mam użytkowników, którzy nie uzupełnili wszystkich wymaganych danych.

<cfset unloadCheck=0>//a ColdFusion precheck in my page generation to see if unload check is needed
var erMsg="";
$(document).ready(function(){
<cfif q.myData eq "">
    <cfset unloadCheck=1>
    $("#myInput").change(function(){
        verify(); //function elsewhere that checks all fields and populates erMsg with error messages for any fail(s)
        if(erMsg=="") window.onbeforeunload = null; //all OK so let them pass
        else window.onbeforeunload = confirmExit(); //borrowed from Jantimon above;
    });
});
<cfif unloadCheck><!--- if any are outstanding, set the error message and the unload alert --->
    verify();
    window.onbeforeunload = confirmExit;
    function confirmExit() {return "Data is incomplete for this Case:"+erMsg;}
</cfif>
Gordon
źródło