web-dev-qa-db-fra.com

JavaScript, navigateurs, fermeture de fenêtre - envoyer une AJAX ou exécuter un script à la fermeture de la fenêtre

J'essaie de savoir quand un utilisateur a quitté une page spécifiée. Il n'y a aucun problème à savoir quand il a utilisé un lien à l'intérieur de la page pour s'éloigner, mais j'ai en quelque sorte besoin de marquer quelque chose comme lorsqu'il a fermé la fenêtre ou tapé une autre URL et appuyé sur Entrée. Le second n'est pas si important mais le premier l'est. Voici donc la question:

Comment puis-je voir quand un utilisateur a fermé ma page (événement capture window.close), et puis ... n'a pas vraiment d'importance (j'ai besoin d'envoyer une demande AJAX, mais si je le peux) faire exécuter une alerte, je peux faire le reste).

29
zozo

Il existe unload et beforeunload événements javascript, mais ceux-ci ne sont pas fiables pour une demande Ajax (il n'est pas garanti qu'une demande lancée dans l'un de ces événements atteindra le serveur).

Par conséquent, il est fortement recommandé de le faire pas, et vous devriez chercher une alternative.

Si vous en avez vraiment besoin, envisagez une solution de style "ping". Envoyez une demande toutes les minutes en disant au serveur "je suis toujours là". Ensuite, si le serveur ne reçoit pas une telle demande pendant plus de deux minutes (vous devez tenir compte des latences, etc.), vous considérez le client hors ligne.


Une autre solution serait d'utiliser unload ou beforeunload pour faire une requête Sjax (Synchronous JavaScript And XML), mais ce n'est absolument pas recommandé. Cela gèrera essentiellement le navigateur de l'utilisateur jusqu'à ce que la demande soit terminée, ce qu'il n'aimera pas (même si la demande prend peu de temps).

32
Felix

1) Si vous cherchez un moyen de travailler dans tous les navigateurs, alors le moyen le plus sûr est d'envoyer un AJAX synchrone au serveur. Ce n'est pas une bonne méthode, mais au moins assurez-vous que vous n'envoyez pas trop de données au serveur, et le serveur est rapide.

2) Vous pouvez également utiliser une fonction asynchrone AJAX, et utiliser la fonction ignore_user_abort sur le serveur (si vous utilisez PHP). Cependant ignore_user_abort dépend beaucoup de la configuration du serveur. Assurez-vous de bien le tester.

3) Pour les navigateurs modernes, vous ne devez pas envoyer de demande AJAX. Vous devez utiliser la nouvelle méthode navigator.sendBeacon pour envoyer des données au serveur de manière asynchrone, et sans bloquer le chargement de la page suivante. Puisque vous souhaitez envoyer des données au serveur avant que l'utilisateur ne quitte la page, vous pouvez utiliser cette méthode dans un déchargement gestionnaire d'événements.

$(window).on('unload', function() {
    var fd = new FormData();
    fd.append('ajax_data', 22);
    navigator.sendBeacon('ajax.php', fd);
});

Il semble également y avoir un polyfill pour sendBeacon . Il a recours à l'envoi d'un AJAX synchrone si la méthode n'est pas disponible en natif.

IMPORTANT POUR LES APPAREILS MOBILES: Veuillez noter que décharger le gestionnaire d'événements n'est pas garanti d'être déclenché pour les mobiles . Mais l'événement visibilitéchange est garanti d'être déclenché. Ainsi, pour les appareils mobiles, votre code de collecte de données peut avoir besoin d'un peu de peaufinage.

Vous pouvez vous référer à mon article de blog pour l'implémentation du code des 3 façons.

18
Useful Angle

Baser cela sur autre réponse .

Pour clarifier les choses, en 2018, Beacon API est la solution à ce problème (sur presque tous les navigateurs )

Les demandes de balise sont garanties d'être lancées avant le déchargement d'une page et elles sont exécutées jusqu'à la fin, sans nécessiter de demande de blocage.

Vous pouvez l'utiliser dans l'événement onunload et être sûr qu'il sera envoyé.

$(window).on('unload', function() {

    var URL = "https://example.com/foo";
    var data = "bar";

    navigator.sendBeacon(URL, data);

});

Excellent article de blog: http://usefulangle.com/post/62/javascript-send-data-to-server-on-page-exit-reload-redirect

API Beacon: https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API/Using_the_Beacon_API

16
Baishu

Je voulais également obtenir la même fonctionnalité et suis tombé sur cette réponse de Felix ( il n'est pas garanti qu'une demande lancée dans l'un de ces événements atteindra le serveur ).

Pour que la demande atteigne le serveur, nous avons essayé le code ci-dessous: -

onbeforeunload = function() {

    //Your code goes here.

    return "";
} 

Nous utilisons IE navigateur et maintenant lorsque l'utilisateur ferme le navigateur, il obtient la boîte de dialogue de confirmation à cause de return ""; & attend la confirmation de l'utilisateur et ce temps d'attente fait la demande pour atteindre le serveur.

3
Pradeep Yadav

Essaye celui-là. J'ai résolu ce problème en javascript, en envoyant un appel ajax au serveur lors de la navigation ou de la fermeture des onglets. J'ai eu un problème avec l'actualisation de la page car sur la fonction onbeforeunload, y compris l'actualisation de la page. performance.navigation.type == 1 devrait isoler l'actualisation de la fermeture (sur le navigateur mozzila).

$(window).bind('mouseover', (function () { // detecting DOM elements
    window.onbeforeunload = null;
}));

$(window).bind('mouseout', (function () { //Detecting event out of DOM
      window.onbeforeunload = ConfirmLeave;
}));

function ConfirmLeave() {
   if (performance.navigation.type == 1) { //detecting refresh page(doesnt work on every browser)
   }
   else {
       logOutUser();
   }
}

$(document).bind('keydown', function (e) { //detecting alt+F4 closing
    if (e.altKey && e.keyCode == 115) {
        logOutUser();
    }    
});
function logOutUser() {
   $.ajax({
     type: "POST",
     url: GWA("LogIn/ForcedClosing"), //example controller/method
     async: false
   });
}
0
Stidljivi

Des années après avoir posté la question, j'ai fait une meilleure implémentation, y compris nodejs et socket.io ( https://socket.io ) (vous pouvez utiliser n'importe quel type de socket d'ailleurs, mais c'était mon choix personnel ).

Fondamentalement, j'ouvre une connexion avec le client, et quand il raccroche, je sauvegarde simplement les données/fais tout ce dont j'ai besoin. Évidemment, cela ne peut pas être utilisé pour montrer quoi que ce soit/rediriger le client (puisque vous le faites côté serveur), mais c'est ce dont j'avais réellement besoin à l'époque.

 io.on('connection', function(socket){
      socket.on('disconnect', function(){
          // Do stuff here
      });
 });

Donc ... de nos jours, je pense que ce serait une meilleure approche (bien que plus difficile à mettre en œuvre car vous avez besoin d'un nœud, d'une socket, etc., mais ce n'est pas si difficile; devrait prendre environ 30 minutes si vous le faites pour la première fois) décharger la version.

0
zozo

La réponse sélectionnée est correcte: vous ne pouvez pas garantir que le navigateur envoie la demande xhr, mais selon le navigateur, vous pouvez envoyer une demande de manière fiable dans l'onglet ou la fenêtre fermée.

Normalement, le navigateur se ferme avant que xhr.send () ne s'exécute réellement. Chrome et Edge semblent attendre que la boucle d'événement javascript se vide avant de fermer la fenêtre. Ils déclenchent également la demande xhr dans un thread différent de la boucle d'événement javascript. Cela signifie que si vous pouvez garder la boucle d'événement pleine assez longtemps, la xhr se déclenchera avec succès. Par exemple, j'ai testé l'envoi d'une demande xhr, puis en comptant jusqu'à 100 000 000. Cela a fonctionné de manière très cohérente dans les deux chrome et Edge pour moi Si vous utilisez angularjs, encapsuler votre appel à $ http dans $ apply accomplit la même chose.

IE semble être un peu différent. Je ne pense pas que IE attend que la boucle d'événement se vide, ou même que le cadre de pile actuel se vide. Bien qu'il envoie de temps en temps correctement une demande, ce qui semble se produire beaucoup plus souvent ( 80% -90% du temps) signifie que IE fermera la fenêtre ou l'onglet avant que la requête xhr ne soit complètement exécutée, ce qui entraîne l'envoi d'un message partiel uniquement). après la demande, mais il n'y a pas de corps.

Pour la postérité, voici le code que j'ai utilisé ci-joint comme fonction d'écoute window.onbeforeunload:

  var xhr = new XMLHttpRequest();
  xhr.open("POST", <your url here>);
  xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
  var payload = {id: "123456789"};
  xhr.send(JSON.stringify(payload));

  for(var i = 0; i < 100000000; i++) {}

J'ai testé dans:

Chrome 61.0.3163.100

IE 11.608.15063.0CO

Edge 40.15063.0.0

0
codepuzzler