Najlepszy sposób na wykrycie, kiedy użytkownik opuszcza stronę internetową?

195

Jaki jest najlepszy sposób na wykrycie, czy użytkownik opuszcza stronę internetową?

onunloadZdarzenie JavaScript nie działa za każdym razem (żądanie HTTP trwa dłużej niż czas wymagany do zakończenia przeglądarkę).

Utworzenie jednego z nich prawdopodobnie zostanie zablokowane przez obecne przeglądarki.

ANaimi
źródło

Odpowiedzi:

219

Wypróbuj onbeforeunloadwydarzenie: Jest uruchamiane tuż przed rozładowaniem strony. Pozwala również zapytać, czy użytkownik naprawdę chce odejść. Zobacz demo onbeforeunload Demo .

Alternatywnie możesz wysłać prośbę Ajaxa, gdy ten odejdzie.

Andreas Petersson
źródło
3
Byłoby miło dla użytkownika, gdybyś śledził, czy formularz się zmienił i pytał tylko, jeśli tak się stało - więc nie jest to denerwujące.
adam0101,
3
Należy również pamiętać, że różne przeglądarki mobilne ignorują wynik zdarzenia (tzn. Nie proszą użytkownika o potwierdzenie). Firefox ma ukryte preferencje w about: config, aby zrobić to samo. Zasadniczo oznacza to, że użytkownik zawsze potwierdza, że ​​dokument może zostać rozładowany.
MJB
2
Zauważ, że ta metoda wywołuje również przy odświeżaniu strony lub przesłaniu formularza przez użytkownika.
Zajcew Dmitrij
@Andreas Petersson, mam takie samo wyzwanie w celu przerwania sesji użytkownika. Jak mogę przechwycić wpisany adres URL, aby sprawdzić, czy należy on do projektu?
Jcc.Sanabria,
21

Mozilla Developer Network ma ładny opis i przykład onbeforeunload .

Jeśli chcesz ostrzec użytkownika przed opuszczeniem strony, jeśli strona jest brudna (tj. Jeśli użytkownik wprowadził jakieś dane):

window.addEventListener('beforeunload', function(e) {
  var myPageIsDirty = ...; //you implement this logic...
  if(myPageIsDirty) {
    //following two lines will cause the browser to ask the user if they
    //want to leave. The text of this dialog is controlled by the browser.
    e.preventDefault(); //per the standard
    e.returnValue = ''; //required for Chrome
  }
  //else: user is allowed to leave without a warning dialog
});
DuckMaestro
źródło
10

Oto alternatywne rozwiązanie - ponieważ w większości przeglądarek elementy sterujące nawigacją (pasek nawigacyjny, karty itp.) Znajdują się nad obszarem zawartości strony, możesz wykryć wskaźnik myszy opuszczający stronę u góry i wyświetlić „ zanim wyjedziesz ” dialog. Jest to całkowicie dyskretne i pozwala na interakcję z użytkownikiem, zanim faktycznie wykona akcję opuszczenia.

$(document).bind("mouseleave", function(e) {
    if (e.pageY - $(window).scrollTop() <= 1) {    
        $('#BeforeYouLeaveDiv').show();
    }
});

Minusem jest to, że oczywiście jest to przypuszczenie , że użytkownik rzeczywiście zamierza opuścić, ale w większości przypadków jest to poprawne.

rustyx
źródło
szukam czegoś takiego, chcę pokazać coś dyskretnego mojemu użytkownikowi, przed opuszczeniem strony, szukam czegoś w rodzaju zdarzenia najechania myszką na przycisku Wstecz, ponieważ muszę uruchomić zdarzenie, zanim użytkownik kliknie, aby wrócić. Uważam, że najlepszym sposobem jest naprawdę wykrycie dokumentu pozostawienia myszy i zrobienie logiki, aby ustalić, czy użytkownik wchodził w interakcję ze stroną.
Leonardo Souza Paiva,
8

Do tego używam:

window.onbeforeunload = function (e) {

}

Jest uruchamiany tuż przed rozładowaniem strony.

suhailvs
źródło
1
Opera 12 i starsze wersje nie obsługują tego ... zachleat.com/web/…
Cyrus
Jak to jest lepsze niż dwie poprzednie odpowiedzi, które dają to samo rozwiązanie onbeforeunload?
Dan Dascalescu
5

Wiem, że odpowiedziano na to pytanie, ale jeśli chcesz, aby coś uruchomiło się tylko wtedy, gdy aktualny PRZEGLĄDARKA jest zamknięta, a nie tylko wtedy, gdy nastąpi ładowanie strony, możesz użyć tego kodu:

window.onbeforeunload = function (e) {
        if ((window.event.clientY < 0)) {
            //window.localStorage.clear();
            //alert("Y coords: " + window.event.clientY)
        }
};

W moim przykładzie wyczyszczam pamięć lokalną i ostrzegam użytkownika za pomocą myszy i współrzędnych, tylko gdy przeglądarka jest zamknięta, zostanie to zignorowane przy wszystkich ładowaniach stron z poziomu programu.

Merr Leader
źródło
window.event jest dla mnie niezdefiniowany
Karl Glaser
Przykład Merr Leadera jest niepoprawny, window.event powinien być używany jako rezerwowy, na przykład dla starszych wersji IE, w innych przypadkach enależy użyć parametru zdarzenia ( w tym przypadku zmiennej): stackoverflow.com/questions/9813445/ …
Sk8erPeter,
Nie posunąłbym się aż do stwierdzenia, że ​​się mylę. Moje przykłady działają dobrze w IE i Chrome. Mój przykład dotyczył scenariusza, w którym użytkownik kliknie X (zamknij) w przeglądarce internetowej. Być może nie jest to najładniejszy sposób, ale działa.
Merr Leader
Z MDN: This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. developer.mozilla.org/en-US/docs/Web/API/Window/event
Robin Neal
3

Jednym (nieco zhackowanym) sposobem jest zastąpienie linków prowadzących z Twojej witryny wywołaniem AJAX po stronie serwera, wskazującym, że użytkownik odchodzi, a następnie skorzystanie z tego samego bloku javascript, aby przenieść użytkownika na stronę zewnętrzną poprosiłem.

Oczywiście to nie zadziała, jeśli użytkownik po prostu zamknie okno przeglądarki lub wpisze nowy adres URL.

Aby obejść ten problem, potencjalnie będziesz musiał skorzystać ze strony setTimeout () Javascript na stronie, wykonując wywołanie AJAX co kilka sekund (w zależności od tego, jak szybko chcesz wiedzieć, czy użytkownik odszedł).

Steve M.
źródło
4
Jeśli korzystasz z metody głównej raportu, nie musisz modyfikować każdego linku na stronie.
Vinko Vrsalovic
3

Dzięki pracownikom serwisowym możliwe jest wdrożenie rozwiązania podobnego do rozwiązania Adama wyłącznie po stronie klienta, pod warunkiem, że przeglądarka go obsługuje. Wystarczy obejść żądania pulsu:

// The delay should be longer than the heartbeat by a significant enough amount that there won't be false positives
const liveTimeoutDelay = 10000
let liveTimeout = null

global.self.addEventListener('fetch', event => {
  clearTimeout(liveTimeout)
  liveTimeout = setTimeout(() => {
    console.log('User left page')
    // handle page leave
  }, liveTimeoutDelay)
  // Forward any events except for hearbeat events
  if (event.request.url.endsWith('/heartbeat')) {
    event.respondWith(
      new global.Response('Still here')
    )
  }
})
Jeffrey Sweeney
źródło
Czy miałeś na myśli, że gdzieś w kodzie klienta musi być setInterval(() => fetch('/heartbeat'), 5000)?
Dan Dascalescu
@DanDascalescu Correct. Kod JS wysyła żądanie sieciowe do / heartbeat, a pracownik serwisu przechwytuje je.
Jeffrey Sweeney
2

W przypadku konieczności wykonania kodu asynchronicznego (np. Wysłania do serwera wiadomości, że użytkownik nie jest teraz skoncentrowany na stronie), zdarzenie beforeunloadnie da czasu na uruchomienie kodu asynchronicznego. W przypadku asynchronii stwierdziłem, że visibilitychangei mouseleavezdarzenia są najlepszymi opcjami. Te zdarzenia są uruchamiane, gdy użytkownik zmienia kartę, ukrywa przeglądarkę lub usuwa kuriera z zakresu okna.

document.addEventListener('mouseleave', e=>{
     //do some async code
})

document.addEventListener('visibilitychange', e=>{
     if (document.visibilityState === 'visible') {
   //report that user is in focus
    } else {
     //report that user is out of focus
    }  
})

Tal Yaron
źródło
0

Jeśli chodzi o jego wartość, to właśnie zrobiłem i może może pomóc innym, nawet jeśli artykuł jest stary.

PHP:

session_start();

$_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR'];

if(isset($_SESSION['userID'])){
    if(!strpos($_SESSION['activeID'], '-')){
        $_SESSION['activeID'] = $_SESSION['userID'].'-'.$_SESSION['activeID'];
    }
}elseif(!isset($_SESSION['activeID'])){
    $_SESSION['activeID'] = time();
}

JS

window.setInterval(function(){
            var userid = '<?php echo $_SESSION['activeID']; ?>';
            var ipaddress = '<?php echo $_SESSION['ipaddress']; ?>';
            var action = 'data';

            $.ajax({
                url:'activeUser.php',
                method:'POST',
                data:{action:action,userid:userid,ipaddress:ipaddress},
                success:function(response){
                     //alert(response);                 
                }
            });
          }, 5000);

Wywołanie Ajax do activeUser.php

if(isset($_POST['action'])){
    if(isset($_POST['userid'])){
        $stamp = time();
        $activeid = $_POST['userid'];
        $ip = $_POST['ipaddress'];

        $query = "SELECT stamp FROM activeusers WHERE activeid = '".$activeid."' LIMIT 1";
        $results = RUNSIMPLEDB($query);

        if($results->num_rows > 0){
            $query = "UPDATE activeusers SET stamp = '$stamp' WHERE activeid = '".$activeid."' AND ip = '$ip' LIMIT 1";
            RUNSIMPLEDB($query);
        }else{
            $query = "INSERT INTO activeusers (activeid,stamp,ip)
                    VALUES ('".$activeid."','$stamp','$ip')";
            RUNSIMPLEDB($query);
        }
    }
}

Baza danych:

CREATE TABLE `activeusers` (
  `id` int(11) NOT NULL,
  `activeid` varchar(20) NOT NULL,
  `stamp` int(11) NOT NULL,
  `ip` text
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Zasadniczo co 5 sekund js opublikuje w pliku php, który będzie śledził użytkownika i adres IP użytkownika. Aktywni użytkownicy to po prostu rekord bazy danych, który ma aktualizację znacznika czasu bazy danych w ciągu 5 sekund. Starzy użytkownicy przestają aktualizować bazę danych. Adres IP służy tylko do upewnienia się, że użytkownik jest unikalny, więc 2 osoby w witrynie jednocześnie nie rejestrują się jako 1 użytkownik.

Prawdopodobnie nie jest to najbardziej wydajne rozwiązanie, ale działa.

Adam
źródło