Detectar o fechamento do navegador ou da guia

306

Existe algum código JavaScript / jQuery entre navegadores para detectar se o navegador ou uma guia do navegador está sendo fechada, mas não devido ao clique no link?

cometta
fonte
1
Veja minha resposta a esta pergunta: stackoverflow.com/questions/3735198/…
Nivas
Use sessionStorage Property, expirará assim que o navegador fechar. w3schools.com/jsref/prop_win_sessionstorage.asp
csandreas1
Verifique esta resposta! Achei extremamente útil e abrange a maioria dos casos. stackoverflow.com/a/26275621/7192927
SukanyaPai

Respostas:

273

Se eu entendi corretamente, você quer saber quando uma guia / janela é efetivamente fechada. Bem, o AFAIK é a única maneira Javascriptde detectar esse tipo de coisas onunloade onbeforeunloadeventos.

Infelizmente (ou felizmente?), Esses eventos também são disparados quando você sai de um site por um linkbotão ou no navegador de seu navegador. Portanto, esta é a melhor resposta que posso dar, acho que você não consegue detectar nativamente um puro closeem Javascript. Corrija-me se eu estiver errado aqui.

jAndy
fonte
39
Corrigir. Você só pode detectar quando a página está descarregada, não quando a janela é fechada. Além disso, o onbeforeunload não é padrão, portanto, não é suportado por todos os navegadores.
Guffa 8/10/10
7
Eu também queria usar o recurso 'fechar', mas notei que meus usuários ocasionalmente atualizam a tela pressionando F5. Como meus manipuladores 'de perto' são invocados em tal atualização, isso significa que não posso usá-lo da maneira que eu precisava (que era uma verdadeira 'guia ou janela próxima') ... caramba, precisamos de um novo modelo de evento por tudo isso!
Jeach
1
Não funciona mais no chrome / opera ... chromestatus.com/feature/5349061406228480
Samir Seetal
2
É claro que isso ainda funcionará, eles apenas removeram um Stringvalor de retorno personalizado onbeforeunload. Portanto, agora você não poderá mais exibir uma mensagem personalizada antes de descarregar.
Jandy
@jAndy @Guffa Como posso descobrir qual janela disparou unload?
Egor Dudyak 27/02
125

Da documentação do Firefox

Por alguns motivos, os navegadores baseados no Webkit não seguem as especificações da caixa de diálogo. Um exemplo quase transversal poderia estar próximo do exemplo abaixo.

window.addEventListener("beforeunload", function (e) {
  var confirmationMessage = "\o/";

  (e || window.event).returnValue = confirmationMessage; //Gecko + IE
  return confirmationMessage;                            //Webkit, Safari, Chrome
});

Este exemplo para lidar com todos os navegadores.

mohamed-ibrahim
fonte
2
Esta é a única solução que funcionou para o meu navegador Chrome. Eu não tentei outros.
Perseguição Roberts
4
sim, eu tento muitas soluções e venho com isso no final, combinação para obter melhores respostas, e testo em todos os navegadores.
mohamed-ibrahim
1
Este método não está funcionando perfeitamente no fechamento da guia enquanto estiver usando o Firefox (> = ver. 44) com apenas a guia aberta.
Rajkishore 24/05
Atualmente, usando todas as versões mais recentes de navegadores, o prompt funciona ao fechar a guia, navegar para longe ou fechar o navegador para Chrome, Firefox, Opera, IE11 e Edge e também funciona quando você tem várias guias abertas, exceto o Chrome, onde o Chrome fecha todas as guias sem avisar, pelo menos na minha máquina.
Thierry
não dando uma saída adequada, conforme definido em questão. Isso também desencadeada em caso de licença página não apenas o navegador fechar ou guia perto
Sunil Acharya
48

Solução Simples

window.onbeforeunload = function () {
    return "Do you really want to close?";
};

fonte
7
Que tipo de bruxaria é essa? (y) funciona em todos os navegadores? funciona muito bem em cromo
Ziv Weissman
5
é acionado quando você atualiza a página. esse é o meu único problema.
Leo Leoncio 25/09
6
Esta solução não mais no Firefox e Chrome
Mehdi Dehghani
Isso também acionou o evento page leave, não apenas o navegador fechar ou fechar a guia
Sunil Acharya
20
<body onbeforeunload="ConfirmClose()" onunload="HandleOnClose()">

var myclose = false;

function ConfirmClose()
{
    if (event.clientY < 0)
    {
        event.returnValue = 'You have closed the browser. Do you want to logout from your application?';
        setTimeout('myclose=false',10);
        myclose=true;
    }
}

function HandleOnClose()
{
    if (myclose==true) 
    {
        //the url of your logout page which invalidate session on logout 
        location.replace('/contextpath/j_spring_security_logout') ;
    }   
}

// Isso está funcionando no IE7, se você estiver fechando a guia ou o navegador com apenas uma guia

jyothis
fonte
2
ya. mas para o navegador de várias guias, como ie8, firefox. há problema em usar isso?
Cometa 9/04
Acontece que o uso desse código no IE9 fará com que essa mensagem seja exibida quando o usuário clicar nos botões voltar, avançar ou atualizar os botões com o mouse.
Steve Wortham
Apenas um aviso de que implementamos uma solução semelhante em 2007. Essa solução não funciona mais no IE9 há cerca de uma semana. Ele parou de funcionar (ou possivelmente nunca funcionou) em navegadores com guias há muito tempo.
Tjfo 17/07/2013
Injetei window.onunload = "alert('wait')"e window.onbeforeunload = "alert('wait... chrome!')"usei o console no Chromium / Ubuntu, mas nenhum dos dois disparou quando fechei a guia.
icedwater
window.onunload = "alert('wait')"e coisas assim não deveriam funcionar realmente. "A função deve atribuir um valor de string à propriedade returnValue do objeto Event e retornar a mesma string". Por favor, dê uma olhada no artigo MDN
Dmitry Vyprichenko
8

Eu precisava desconectar o usuário automaticamente quando o navegador ou a guia fecha, mas não quando o usuário navega para outros links. Eu também não queria que um prompt de confirmação fosse mostrado quando isso acontecesse. Depois de lutar por algum tempo, especialmente com o IE e o Edge, eis o que eu acabei de fazer (verificado trabalhando com o IE 11, Edge, Chrome e Firefox) depois de basear a abordagem nesta resposta .

Primeiro, inicie um cronômetro de contagem regressiva no servidor no beforeunloadmanipulador de eventos em JS. As chamadas ajax precisam ser síncronas para que o IE e o Edge funcionem corretamente. Você também precisa usar return;para impedir que a caixa de diálogo de confirmação seja exibida assim:

    window.addEventListener("beforeunload", function (e) {        
      $.ajax({
          type: "POST",
          url: startTimerUrl,
          async: false           
      });
      return;
    });

Iniciar o timer define o cancelLogoutsinalizador como false . Se o usuário atualizar a página ou navegar para outro link interno, o cancelLogoutsinalizador no servidor será definido como verdadeiro . Depois que o evento do timer termina, ele verifica o cancelLogoutsinalizador para ver se o evento de logout foi cancelado. Se o cronômetro tiver sido cancelado, o cronômetro será interrompido. Se o navegador ou guia estivesse fechado, o cancelLogoutsinalizador permaneceria falso e o manipulador de eventos desconectaria o usuário.

Nota de implementação: estou usando o ASP.NET MVC 5 e estou cancelando o logout em um Controller.OnActionExecuted()método substituído .

Ionian316
fonte
Isso funcionou muito bem. Existe uma razão para você criar "async: false"? Isso gera avisos de que chamadas AJAX síncronas foram descontinuadas. Obrigado!
22618 Cat_in_hat #
1
@FoundWaldoException, conforme observado na resposta, descobri que o assíncrono precisava ser definido como falso para que isso funcionasse no IE e no Edge.
Ionian316
Como cancelar o logout OnActionExecuted?
Kiquenet
@Kiquenet Basta parar o cronômetro iniciado no seu código.
Ionian316
6

Desculpe, não foi possível adicionar um comentário a uma das respostas existentes, mas caso você desejasse implementar um tipo de caixa de diálogo de aviso, só queria mencionar que qualquer função do manipulador de eventos possui um argumento - evento. No seu caso, você pode chamar event.preventDefault () para impedir a saída automática da página e emitir seu próprio diálogo. Considero isso uma opção muito melhor do que usar o alerta padrão feio e inseguro (). Pessoalmente, implementei meu próprio conjunto de caixas de diálogo com base no objeto kendoWindow (a interface do usuário Kendo da Telerik, que é quase totalmente de código aberto, exceto o kendoGrid e o kendoEditor). Você também pode usar caixas de diálogo na interface do usuário do jQuery. Observe, porém, que essas coisas são assíncronas e você precisará vincular um manipulador ao evento onclick de cada botão, mas tudo isso é muito fácil de implementar.

No entanto, concordo que a falta do evento real de fechamento é terrível: se você, por exemplo, deseja redefinir o estado da sessão no back-end apenas no caso do fechamento real, é um problema.

Alex Iurovetski
fonte
6

Para tarefas semelhantes, você pode usar sessionStorage para armazenar dados localmente até que a guia do navegador seja fechada.

O sessionStorageobjeto armazena dados para apenas uma sessão (os dados são excluídos quando a guia do navegador é fechada). ( W3Schools )

Esta é a minha caneta .

<div id="Notice">
    <span title="remove this until browser tab is closed"><u>dismiss</u>.</span>
</div>
<script>
    $("#Notice").click(function() {
     //set sessionStorage on click
        sessionStorage.setItem("dismissNotice", "Hello");
        $("#Notice").remove();
    });
    if (sessionStorage.getItem("dismissNotice"))
    //When sessionStorage is set Do stuff...
        $("#Notice").remove();
</script>
csandreas1
fonte
5

Não há evento, mas há uma propriedade window.closedque é suportada em todos os principais navegadores até o momento da redação deste documento. Portanto, se você realmente precisava saber, poderia pesquisar na janela para verificar essa propriedade.

if(myWindow.closed){do things}

Nota: pesquisar qualquer coisa geralmente não é a melhor solução. O window.onbeforeunloadevento deve ser usado, se possível, a única ressalva é que ele também é acionado se você sair.

dlsso
fonte
4
$(window).unload( function () { alert("Bye now!"); } );
Poonam Bhatt
fonte
9
Isso não funcionará no Firefox ou Chrome. Tente beforeunloadse deseja exibir um alerta. Assim:$(window).on('beforeunload', function(){ alert ('Bye now')});
AdrianoRR
5
como por docs jQuery "A maioria dos navegadores irá ignorar chamadas para alerta (), confirm () e prompt de ()" api.jquery.com/unload
katzmopolitan
4

Tente usá-lo:

window.onbeforeunload = function (event) {
    var message = 'Important: Please click on \'Save\' button to leave this page.';
    if (typeof event == 'undefined') {
        event = window.event;
    }
    if (event) {
        event.returnValue = message;
    }
    return message;
};

$(function () {
    $("a").not('#lnkLogOut').click(function () {
        window.onbeforeunload = null;
    });
    $(".btn").click(function () {
        window.onbeforeunload = null;
});
});
Fezal halai
fonte
3

Eu encontrei uma maneira, que funciona em todos os meus navegadores .

Testado nas seguintes versões: Firefox 57, Internet Explorer 11, Edge 41, um dos mais recentes do Chrome (não mostrará minha versão)

Nota : onbeforeunload é acionado se você deixar a página de qualquer maneira possível (atualizar, fechar o navegador, redirecionar, vincular, enviar ..). Se você deseja que isso ocorra apenas no fechamento do navegador, basta vincular os manipuladores de eventos.

  $(document).ready(function(){         

        var validNavigation = false;

        // Attach the event keypress to exclude the F5 refresh (includes normal refresh)
        $(document).bind('keypress', function(e) {
            if (e.keyCode == 116){
                validNavigation = true;
            }
        });

        // Attach the event click for all links in the page
        $("a").bind("click", function() {
            validNavigation = true;
        });

        // Attach the event submit for all forms in the page
        $("form").bind("submit", function() {
          validNavigation = true;
        });

        // Attach the event click for all inputs in the page
        $("input[type=submit]").bind("click", function() {
          validNavigation = true;
        }); 

        window.onbeforeunload = function() {                
            if (!validNavigation) {     
                // ------->  code comes here
            }
        };

  });
Syno
fonte
Como será adicionado um manipulador separado para o fechamento do navegador. Acho que não temos eventos separados para isso.
Bloquehub 15/08/19
Isso foi muito útil. Eu precisava ajustar a âncora A para disparar apenas para hiperlinks reais. Caso contrário, clicar nas guias de inicialização estragou tudo: $ ("a [href! = '']"). Not ('[href ^ = "#"]'). Bind ("click", function ()
Ken Forslund
3

Como ninguém o mencionou ainda (mais de 8 anos depois): um WebSocket pode ser outra maneira eficaz de detectar uma guia fechada. Enquanto a guia estiver aberta e apontada para o host, o cliente poderá manter uma conexão WebSocket ativa com o host.

Advertência: Observe que esta solução é realmente viável apenas para um projeto se um WebSocket não exigir nenhuma sobrecarga adicional significativa do que você já está fazendo.

Dentro de um período de tempo limite razoável (por exemplo, 2 minutos), o lado do servidor pode determinar que o cliente foi embora depois que o WebSocket foi desconectado e executar qualquer ação desejada, como remover arquivos temporários carregados. (No meu caso de uso extremamente especializado, meu objetivo era encerrar um servidor de aplicativos host local três segundos após a conexão com o WebSocket cair e toda a atividade CGI / FastCGI terminar - quaisquer outras conexões keep-alive não me afetam.)

Eu tive problemas para fazer com que o manipulador de eventos onunload funcionasse corretamente com beacons (conforme recomendado por esta resposta ). O fechamento da guia não pareceu acionar o farol e as abas abertas o acionaram de maneiras que poderiam causar problemas. Um WebSocket resolveu o problema que eu estava enfrentando de forma mais limpa, porque a conexão fecha aproximadamente na mesma hora em que a guia é fechada e a troca de páginas no aplicativo simplesmente abre uma nova conexão WebSocket dentro da janela de atraso.

CubicleSoft
fonte
2
está tudo bem. Mas, você não pode compartilhar nenhum código ou procedimento para lidar com isso? Sem que para um noob como eu não consigo entender
P Satish Patro
Na verdade, eu não estava claro no meu primeiro comentário. Eu não sei soquete da web. Então, eu só queria ver como lidar com
P Satish Patro
1
O link no meu comentário acima vai para algum exemplo de código WebSocket JS. A parte JS do cliente é a parte mais fácil. Esse código é para meu caso de uso muito específico, embora eu precise finalizar todo o servidor de aplicativos em tempo hábil. A decisão de como você escreve um servidor WebSocket é sua e requer a configuração do servidor para conectar tudo (muitas pessoas preferem um back-end do NodeJS com front-end do Nginx). Escrever um servidor habilitado para TCP / IP é um tópico complexo que não é adequado para discussão em comentários e é um exagero configurá-lo apenas para detectar esse problema específico.
CubicleSoft 4/03/19
2
//Detect Browser or Tab Close Events
$(window).on('beforeunload',function(e) {
  e = e || window.event;
  var localStorageTime = localStorage.getItem('storagetime')
  if(localStorageTime!=null && localStorageTime!=undefined){
    var currentTime = new Date().getTime(),
        timeDifference = currentTime - localStorageTime;

    if(timeDifference<25){//Browser Closed
       localStorage.removeItem('storagetime');
    }else{//Browser Tab Closed
       localStorage.setItem('storagetime',new Date().getTime());
    }

  }else{
    localStorage.setItem('storagetime',new Date().getTime());
  }
});

JSFiddle Link

Olá pessoal, consegui cliques em 'Detectar navegador e fechar evento da guia' usando o armazenamento local e o carimbo de data / hora do navegador. Espero que todos vocês resolvam seus problemas usando esta solução.

Após minha pesquisa inicial, descobri que, quando fechamos um navegador, o navegador fecha todas as guias uma por uma para fechar completamente o navegador. Portanto, observei que haverá muito pouco tempo de atraso entre o fechamento das guias. Portanto, tomei esse atraso como meu principal ponto de validação e consegui alcançar a detecção de eventos de fechamento do navegador e da guia.

Eu testei no navegador Chrome versão 76.0.3809.132 e achei funcionando

:) Vote se achou a minha resposta útil ....

ramojuraju
fonte
Bem, isso me deu uma ideia. Estou definindo um carimbo de data e hora mais 10 segundos no armazenamento local após o login e no evento antes da descarga. No carregamento, verifico se o tempo passou e redireciono para a página de login. Obrigado :)
G4BB3R 20/02
1
window.onbeforeunload = function() {
  console.log('event');
  return false; //here also can be string, that will be shown to the user
}
IvanM
fonte
2
here also can be string, that will be shown to the userno entanto, no firefox, no Chrome, no entanto ...
TrySpace 13/02/14
Qualquer valor de retorno (string) é exibido apenas no MSIE <= 11. Portanto, basicamente inútil para todos os outros navegadores.
OSWorX 17/03/19
1

É possível verificá-lo com a ajuda de window.closed em um manipulador de eventos no evento 'unload' como este, mas o uso do tempo limite é necessário (portanto, o resultado não pode ser garantido se smth atrasar ou impedir o fechamento da janela):

Exemplo de JSFiddle (Testado em versões recentes Safari, FF, Chrome, Edge e IE11)

var win = window.open('', '', 'width=200,height=50,left=200,top=50');
win.document.write(`<html>
   <head><title>CHILD WINDOW/TAB</title></head>
   <body><h2>CHILD WINDOW/TAB</h2></body>
</html>`);
win.addEventListener('load',() => {
    document.querySelector('.status').innerHTML += '<p>Child was loaded!</p>';
});
win.addEventListener('unload',() => {
    document.querySelector('.status').innerHTML += '<p>Child was unloaded!</p>';
    setTimeout(()=>{
        document.querySelector('.status').innerHTML +=  getChildWindowStatus();
    },1000);
});
win.document.close()
document.querySelector('.check-child-window').onclick = ()=> {
    alert(getChildWindowStatus());
}
function getChildWindowStatus() {
  if (win.closed) { 
      return 'Child window has been closed!';
  } else {
      return 'Child window has not been closed!';
  }
}
Rvach.Flyver
fonte
1

Eu tentei todas as soluções acima, nenhuma delas realmente funcionou para mim, especialmente porque existem alguns componentes da Telerik no meu projeto que possuem o botão 'Fechar' para janelas pop-up e chama o evento 'beforeunload'. Além disso, o seletor de botões não funciona corretamente quando você possui uma grade Telerik em sua página (refiro-me a botões dentro da grade). Portanto, não pude usar nenhuma das sugestões acima. Finalmente, esta é a solução que funcionou para mim. Eu adicionei um evento onUnload na tag body de _Layout.cshtml. Algo assim:

<body onUnload="LogOff()">

e adicione a função LogOff para redirecionar para Account / LogOff, que é um método interno do Asp.Net MVC. Agora, quando fecho o navegador ou a guia, ele é redirecionado para o método LogOff e o usuário precisa fazer o login quando retornar. Eu testei no Chrome e Firefox. E funciona!

  function LogOff() {
        $.ajax({
            url: "/Account/LogOff",
            success: function (result) {

                                        }
               });
       }
Amir978
fonte
0
window.onbeforeunload = function ()
{       

    if (isProcess > 0) 
    {
        return true;       
    }   

    else
    { 
        //do something      
    }
}; 

Essa função mostra uma caixa de diálogo de confirmação se você fechar a janela ou atualizar a página durante qualquer processo no navegador.Esta função funciona em todos os navegadores.Você deve definir o isProcess var no processo do ajax.

Hari
fonte
amostra completa para o conjunto isProcess?
113818 Kiquenet
0
window.addEventListener("beforeunload", function (e) {
 var confirmationMessage = "tab close";

 (e || window.event).returnValue = confirmationMessage;     //Gecko + IE
 sendkeylog(confirmationMessage);
 return confirmationMessage;                                //Webkit, Safari, Chrome etc.
}); 
user3098847
fonte
Na verdade, eu queria mostrar uma mensagem de confirmação se o usuário fechar a guia SOMENTE. Isso está funcionando para mim. Mas eu estou enfrentando um problema que, se eu estou clicando em qualquer link da mesma página, em seguida, também o seu mostrando confirmação message.For moe referem: w3schools.com/code/tryit.asp?filename=FEK2KWUN9R8Z @Tunaki
Mohammed
1
o que é sendkeylog?
Kiquenet
0

Como o @jAndy mencionou, não há código JavaScript apropriado para detectar uma janela sendo fechada. Comecei pelo que a @Syno havia proposto.

Passei por uma situação como essa e, desde que você siga estas etapas, poderá detectá-la.
Eu testei no Chrome 67+ e Firefox 61+.

var wrapper = function () { //ignore this

var closing_window = false;
$(window).on('focus', function () {
    closing_window = false; 
   //if the user interacts with the window, then the window is not being 
   //closed
});

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

    closing_window = true;
    if (!document.hidden) { //when the window is being minimized
        closing_window = false;
    }
    $(window).on('resize', function (e) { //when the window is being maximized
        closing_window = false;
    });
    $(window).off('resize'); //avoid multiple listening
});

$('html').on('mouseleave', function () {
    closing_window = true; 
    //if the user is leaving html, we have more reasons to believe that he's 
    //leaving or thinking about closing the window
});

$('html').on('mouseenter', function () {
    closing_window = false; 
    //if the user's mouse its on the page, it means you don't need to logout 
    //them, didn't it?
});

$(document).on('keydown', function (e) {

    if (e.keyCode == 91 || e.keyCode == 18) {
        closing_window = false; //shortcuts for ALT+TAB and Window key
    }

    if (e.keyCode == 116 || (e.ctrlKey && e.keyCode == 82)) {
        closing_window = false; //shortcuts for F5 and CTRL+F5 and CTRL+R
    }
});

// Prevent logout when clicking in a hiperlink
$(document).on("click", "a", function () {
    closing_window = false;
});

// Prevent logout when clicking in a button (if these buttons rediret to some page)
$(document).on("click", "button", function () {
    closing_window = false;

});
// Prevent logout when submiting
$(document).on("submit", "form", function () {
    closing_window = false;
});
// Prevent logout when submiting
$(document).on("click", "input[type=submit]", function () {
    closing_window = false;
});

var toDoWhenClosing = function() {

    //write a code here likes a user logout, example: 
    //$.ajax({
    //    url: '/MyController/MyLogOutAction',
    //    async: false,
    //    data: {

    //    },
    //    error: function () {
    //    },
    //    success: function (data) {
    //    },
    //});
};


window.onbeforeunload = function () {
    if (closing_window) {
        toDoWhenClosing();
    }
};

};

Bruno Henrique
fonte
1
Você tem alguns problemas no seu código javascript. A primeira coisa que notei é que você se ligará continuamente resizequando a janela estiver embaçada. Se ficar 100x borrado, você terá 100 ouvintes, o que acabará diminuindo a velocidade do navegador.
27418 Kyle
Coloquei um $ (window) .off ('redimensionar'), isso seria suficiente?
Bruno Henrique
Tecnicamente, mas também removerá quaisquer outros manipuladores que foram adicionados em outro lugar via jQuery.
28518 Kyle
-2

tente isso, tenho certeza que isso funcionará para você.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type='text/javascript'>
    $(function() {

        try{
            opera.setOverrideHistoryNavigationMode('compatible');
            history.navigationMode = 'compatible';
        }catch(e){}

        function ReturnMessage()
        {
            return "wait";
        }

        function UnBindWindow()
        {
            $(window).unbind('beforeunload', ReturnMessage);
        }

        $(window).bind('beforeunload',ReturnMessage );
    });
</script>
Tejash Raval Prajapati
fonte
-3

Tente isso. Vai funcionar. O método jquery unload está obsoleto.

window.onbeforeunload = function(event) {
    event.returnValue = "Write something clever here..";
};
Arda Ç.
fonte