¿Cómo detectar eventos en línea / fuera de línea en varios navegadores?

111

Estoy tratando de detectar con precisión cuándo el navegador se desconecta, utilizando los eventos en línea y fuera de línea de HTML5.

Aquí está mi código:

<script>
    // FIREFOX
    $(window).bind("online", applicationBackOnline); 
    $(window).bind("offline", applicationOffline);

    //IE
    window.onload = function() {
        document.body.ononline = IeConnectionEvent;
        document.body.onoffline = IeConnectionEvent;
    } 
</script>

Funciona bien cuando presiono "Trabajar sin conexión" en Firefox o IE, pero funciona de forma aleatoria cuando desconecto el cable.

¿Cuál es la mejor forma de detectar este cambio? Me gustaría evitar repetir llamadas ajax con tiempos de espera.

Pierre Duplouy
fuente
2
Estoy de acuerdo con Trefex, pero también me gustaría agregar que el soporte de detección de conexión es de mala calidad en el mejor de los casos para la mayoría de las aplicaciones: solo porque el cable está desenchufado no constituye una conexión perdida de inmediato. Confiar en un método que no prueba físicamente si la conexión está abierta no puede garantizar resultados precisos.
mattbasta
Gracias por su consejo. Entonces, ¿recomendaría el método Ajax? es decir. sigue enviando llamadas XHR con tiempos de espera?
Pierre Duplouy
La implementación de Firefox (y de IE y Opera) es incorrecta. Ver mi comentario en ese sentido aquí: bugzilla.mozilla.org/show_bug.cgi?id=654579#c9
thewoolleyman
4
Es posible que desee consultar Offline.js , una biblioteca de código abierto creada solo para este propósito.
Adam

Respuestas:

70

Los proveedores de navegadores no pueden ponerse de acuerdo sobre cómo definir sin conexión. Algunos navegadores tienen una función de trabajo sin conexión, que consideran separada de la falta de acceso a la red, que nuevamente es diferente al acceso a Internet. Todo es un desastre. Algunos proveedores de navegadores actualizan el indicador navigator.onLine cuando se pierde el acceso real a la red, otros no.

De la especificación:

Devuelve falso si el agente de usuario está definitivamente fuera de línea (desconectado de la red). Devuelve verdadero si el agente de usuario puede estar en línea.

Los eventos en línea y fuera de línea se activan cuando cambia el valor de este atributo.

El atributo navigator.onLine debe devolver falso si el agente de usuario no se comunicará con la red cuando el usuario sigue enlaces o cuando un script solicita una página remota (o sabe que tal intento fallaría), y debe devolver verdadero en caso contrario.

Finalmente, las notas de la especificación:

Este atributo es intrínsecamente poco fiable. Una computadora puede conectarse a una red sin tener acceso a Internet.

Rebecca
fuente
23
Solo Chrome configura navigator.onLine correctamente cuando se pierde la conectividad. Tanto Safari como Firefox nunca configuran la bandera como falsa si eliminas la conexión a Internet.
chovy
2
@chovy y ¿qué tal ahora? Últimamente lo probé en Firefox / Chrome y obtuve los resultados esperados, al ver que la bandera se está configurando, cuando
apago y enciendo
12
Hoy 31/01/2017 abrí OSX Chrome 55.0.2883.95, Safari 10.0.3 y FF 50.1.0. Todo window.navigator.onLine parece funcionar muy bien cuando permanecí en mi red, pero quité el cable de mi enrutador. Todos se detectaron correctamente sin conexión.
nycynik
3
navigator.onLine es compatible con todos los navegadores principales (y lo ha sido durante algún tiempo): caniuse.com/#feat=online-status
Rafael Lüder
@ RafaelLüder Correcto a partir de hoy, ¡pero esta respuesta fue escrita en enero de 2011!
Rebecca
34

Los principales proveedores de navegadores difieren en lo que significa "sin conexión".

Chrome y Safari detectarán cuando te desconectes automáticamente, lo que significa que los eventos y propiedades "online" se activarán automáticamente cuando desconectes el cable de red.

Firefox (Mozilla), Opera e IE adoptan un enfoque diferente y lo consideran "en línea" a menos que elija explícitamente "Modo sin conexión" en el navegador, incluso si no tiene una conexión de red que funcione.

Hay argumentos válidos para el comportamiento de Firefox / Mozilla, que se describen en los comentarios de este informe de error:

https://bugzilla.mozilla.org/show_bug.cgi?id=654579

Pero, para responder a la pregunta, no puede confiar en los eventos / propiedades en línea / fuera de línea para detectar si realmente hay conectividad de red.

En su lugar, debe utilizar enfoques alternativos.

La sección "Notas" de este artículo para desarrolladores de Mozilla proporciona enlaces a dos métodos alternativos:

https://developer.mozilla.org/en/Online_and_offline_events

"Si la API no está implementada en el navegador, puede utilizar otras señales para detectar si está desconectado, incluida la escucha de los eventos de error de AppCache y las respuestas de XMLHttpRequest"

Esto enlaza con un ejemplo del enfoque "escuchar eventos de error de AppCache":

http://www.html5rocks.com/en/mobile/workingoffthegrid/#toc-appcache

... y un ejemplo del enfoque de "escucha de fallas XMLHttpRequest":

http://www.html5rocks.com/en/mobile/workingoffthegrid/#toc-xml-http-request

HTH, - Chad

thewoolleyman
fuente
1
Comenzando con Firefox 41: updates this property when the OS reports a change in network connectivity on Windows, Linux, and OS X.(de acuerdo con los documentos que ha mencionado). Por lo tanto, no solo está fuera de línea si está navegando con el navegador "Modo sin conexión"
amigo
17

Hoy en día existe una biblioteca JavaScript de código abierto que hace este trabajo: se llama Offline.js.

Muestre automáticamente la indicación en línea / fuera de línea a sus usuarios.

https://github.com/HubSpot/offline

Asegúrese de consultar el archivo README completo . Contiene eventos a los que puede conectarse.

Aquí tienes una página de prueba . ¡Es hermoso / tiene una interfaz de usuario de comentarios agradable por cierto! :)

Offline.js Simulate UI es un complemento de Offline.js que le permite probar cómo responden sus páginas a diferentes estados de conectividad sin tener que usar métodos de fuerza bruta para deshabilitar su conectividad real.

Leniel Maccaferri
fuente
2
La biblioteca en realidad funciona buscando favicon locales repetidamente debajo del capó. En mi opinión, la biblioteca es demasiado "grande" y tiene demasiadas funciones; el truco principal es buscar favicon repetidamente.
Karel Bílek
1
No detecta fuera de línea cuando desconecto el cable de red
poshest
15

La mejor forma que funciona ahora en todos los navegadores principales es el siguiente script:

(function () {
    var displayOnlineStatus = document.getElementById("online-status"),
        isOnline = function () {
            displayOnlineStatus.innerHTML = "Online";
            displayOnlineStatus.className = "online";
        },
        isOffline = function () {
            displayOnlineStatus.innerHTML = "Offline";
            displayOnlineStatus.className = "offline";
        };

    if (window.addEventListener) {
        /*
            Works well in Firefox and Opera with the 
            Work Offline option in the File menu.
            Pulling the ethernet cable doesn't seem to trigger it.
            Later Google Chrome and Safari seem to trigger it well
        */
        window.addEventListener("online", isOnline, false);
        window.addEventListener("offline", isOffline, false);
    }
    else {
        /*
            Works in IE with the Work Offline option in the 
            File menu and pulling the ethernet cable
        */
        document.body.ononline = isOnline;
        document.body.onoffline = isOffline;
    }
})();

Fuente: http://robertnyman.com/html5/offline/online-offline-events.html

tmaximini
fuente
3
Como dicen claramente los comentarios en el código , no funciona en Firefox / Chrome si desconecta el cable ethernet o apaga el wifi.
Manish
Intenté visitar el enlace "Fuente" y desconecté el cable ethernet, mostraba "Estás desconectado" en IE, pero no en Firefox / Chrome para mí (usando la última versión de todos los navegadores). ¿Puede que me esté perdiendo algo?
Manish
13

Desde hace poco, navigator.onLinemuestra lo mismo en todos los navegadores principales y, por lo tanto, es utilizable.

if (navigator.onLine) {
  // do things that need connection
} else {
  // do things that don't need connection
}

Las versiones más antiguas que lo soportan correctamente son: Firefox 41 , IE 9, Chrome 14 y Safari 5.

Actualmente esto representará casi todo el espectro de usuarios, pero siempre debes comprobar qué capacidades tienen los usuarios de tu página.

Antes de FF 41, solo se mostraba falsesi el usuario colocaba el navegador manualmente en modo fuera de línea. En IE 8, la propiedad estaba en body, en lugar de window.

fuente: caniuse

Haroen Viaene
fuente
11

El window.navigator.onLineatributo y sus eventos asociados son actualmente poco fiable en algunos navegadores web ( especialmente de escritorio de Firefox ) cuando dicha @Junto, así que escribí un poco de función (usando jQuery) que comprobar periódicamente el estado de conectividad de la red y elevar la adecuada offliney onlineeventos:

// Global variable somewhere in your app to replicate the 
// window.navigator.onLine variable (it is not modifiable). It prevents
// the offline and online events to be triggered if the network
// connectivity is not changed
var IS_ONLINE = true;

function checkNetwork() {
  $.ajax({
    // Empty file in the root of your public vhost
    url: '/networkcheck.txt',
    // We don't need to fetch the content (I think this can lower
    // the server's resources needed to send the HTTP response a bit)
    type: 'HEAD',
    cache: false, // Needed for HEAD HTTP requests
    timeout: 2000, // 2 seconds
    success: function() {
      if (!IS_ONLINE) { // If we were offline
        IS_ONLINE = true; // We are now online
        $(window).trigger('online'); // Raise the online event
      }
    },
    error: function(jqXHR) {
      if (jqXHR.status == 0 && IS_ONLINE) {
        // We were online and there is no more network connection
        IS_ONLINE = false; // We are now offline
        $(window).trigger('offline'); // Raise the offline event
      } else if (jqXHR.status != 0 && !IS_ONLINE) {
        // All other errors (404, 500, etc) means that the server responded,
        // which means that there are network connectivity
        IS_ONLINE = true; // We are now online
        $(window).trigger('online'); // Raise the online event
      }
    }
  });
}

Puedes usarlo así:

// Hack to use the checkNetwork() function only on Firefox 
// (http://stackoverflow.com/questions/5698810/detect-firefox-browser-with-jquery/9238538#9238538)
// (But it may be too restrictive regarding other browser
// who does not properly support online / offline events)
if (!(window.mozInnerScreenX == null)) {
    window.setInterval(checkNetwork, 30000); // Check the network every 30 seconds
}

Para escuchar los eventos en línea y fuera de línea (con la ayuda de jQuery):

$(window).bind('online offline', function(e) {
  if (!IS_ONLINE || !window.navigator.onLine) {
    alert('We have a situation here');
  } else {
    alert('Battlestation connected');
  }
});
Epoc
fuente
7

navigator.onLine es un desastre

Me enfrento a esto cuando trato de hacer una llamada ajax al servidor.

Hay varias situaciones posibles cuando el cliente está desconectado:

  • los tiempos de espera de la llamada ajax y recibe un error
  • la llamada ajax devuelve el éxito, pero el mensaje es nulo
  • la llamada ajax no se ejecuta porque el navegador así lo decide (puede ser aquí cuando navigator.onLine se vuelve falso después de un tiempo)

La solución que estoy usando es controlar el estado yo mismo con javascript. Establezco la condición de una llamada exitosa; en cualquier otro caso, asumo que el cliente está desconectado. Algo como esto:

var offline;
pendingItems.push(item);//add another item for processing
updatePendingInterval = setInterval("tryUpdatePending()",30000);
tryUpdatePending();

    function tryUpdatePending() {

        offline = setTimeout("$('#offline').show()", 10000);
        $.ajax({ data: JSON.stringify({ items: pendingItems }), url: "WebMethods.aspx/UpdatePendingItems", type: "POST", dataType: "json", contentType: "application/json; charset=utf-8",
          success: function (msg) {
            if ((!msg) || msg.d != "ok")
              return;
            pending = new Array(); //empty the pending array
            $('#offline').hide();
            clearTimeout(offline);
            clearInterval(updatePendingInterval);
          }
        });
      }
Kit de herramientas
fuente
5

En HTML5 puede utilizar la navigator.onLinepropiedad. Mira aquí:

http://www.w3.org/TR/offline-webapps/#related

Probablemente su comportamiento actual sea aleatorio ya que el javascript solo prepara la variable "navegador" y luego sabe si está desconectado y conectado, pero en realidad no comprueba la conexión de red.

Háganos saber si esto es lo que está buscando.

Saludos cordiales,

Trefex
fuente
Gracias por tu ayuda Trefex. Cambié mi código y ahora solo verifico la propiedad navigator.onLine, sin embargo, obtengo el mismo comportamiento que antes. Por favor, eche un vistazo al comentario de Mattbasta.
Pierre Duplouy
Hola Pedro, estoy de acuerdo con mattbasta pero esperaba que funcionara para ti :) Definitivamente usaría el método Ajax para consultar alguna URL que sabes que siempre está activa y luego sabrías si se pierde una conexión o no. En otra nota, ¿por qué necesita la detección precisa del estado en línea / fuera de línea? Tal vez si supiéramos más, habría otra solución para su problema. Háganos saber,
Trefex
1
Ok, gracias :) Pensé que sería mejor para el usuario si la aplicación pudiera detectar automáticamente un cambio en la conectividad (no es necesario habilitar manualmente el modo fuera de línea en FF o IE). De esta forma, cuando la aplicación se desconecte, utilizará su caché local en lugar de consultar al servidor. Encontré esta publicación de John Resig, que prácticamente explica por qué esto no funciona: ejohn.org/blog/offline-events
Pierre Duplouy
Gracias por esa publicación de blog. Confíe en un análisis profundo y directo al grano. Creo que lo que desea lograr es mejor si consulta algún servidor (tal vez el suyo) y luego cambia a la caché local cuando hay x número de tiempos de espera. Qué piensas ?
Trefex
Sí, supongo que es la mejor opción, dado el estado actual de la técnica. Espero que todos los navegadores eventualmente puedan detectar la pérdida real de conexión por sí mismos: usar navigator.onLine es bastante simple y esto no debería ser más complejo. ¿No te parece?
Pierre Duplouy
3

Busque el módulo require.js que escribí para Offline.

define(['offline'], function (Offline) {
    //Tested with Chrome and IE11 Latest Versions as of 20140412
    //Offline.js - http://github.hubspot.com/offline/ 
    //Offline.js is a library to automatically alert your users 
    //when they've lost internet connectivity, like Gmail.
    //It captures AJAX requests which were made while the connection 
    //was down, and remakes them when it's back up, so your app 
    //reacts perfectly.

    //It has a number of beautiful themes and requires no configuration.
    //Object that will be exposed to the outside world. (Revealing Module Pattern)

    var OfflineDetector = {};

    //Flag indicating current network status.
    var isOffline = false;

    //Configuration Options for Offline.js
    Offline.options = {
        checks: {
            xhr: {
                //By default Offline.js queries favicon.ico.
                //Change this to hit a service that simply returns a 204.
                url: 'favicon.ico'
            }
        },

        checkOnLoad: true,
        interceptRequests: true,
        reconnect: true,
        requests: true,
        game: false
    };

    //Offline.js raises the 'up' event when it is able to reach
    //the server indicating that connection is up.
    Offline.on('up', function () {
        isOffline = false;
    });

    //Offline.js raises the 'down' event when it is unable to reach
    //the server indicating that connection is down.
    Offline.on('down', function () {
        isOffline = true;
    });

    //Expose Offline.js instance for outside world!
    OfflineDetector.Offline = Offline;

    //OfflineDetector.isOffline() method returns the current status.
    OfflineDetector.isOffline = function () {
        return isOffline;
    };

    //start() method contains functionality to repeatedly
    //invoke check() method of Offline.js.
    //This repeated call helps in detecting the status.
    OfflineDetector.start = function () {
        var checkOfflineStatus = function () {
            Offline.check();
        };
        setInterval(checkOfflineStatus, 3000);
    };

    //Start OfflineDetector
    OfflineDetector.start();
    return OfflineDetector;
});

Por favor, lea esta publicación de blog y déjeme saber lo que piensa. http://zen-and-art-of-programming.blogspot.com/2014/04/html-5-offline-application-development.html Contiene una muestra de código que utiliza offline.js para detectar cuando el cliente está desconectado.

Srihari Sridharan
fuente
3
Tenga en cuenta que se desaconsejan las respuestas de solo enlace , las respuestas SO deben ser el punto final de la búsqueda de una solución (frente a otra escala de referencias, que tienden a quedarse obsoletas con el tiempo). Considere agregar una sinopsis independiente aquí, manteniendo el enlace como referencia.
kleopatra
Hola, he publicado el módulo require.js junto con la referencia del enlace. Gracias por la sugerencia.
Srihari Sridharan
2

puede detectar fácilmente en varios navegadores sin conexión, como a continuación

var randomValue = Math.floor((1 + Math.random()) * 0x10000)

$.ajax({
      type: "HEAD",
      url: "http://yoururl.com?rand=" + randomValue,
      contentType: "application/json",
      error: function(response) { return response.status == 0; },
      success: function() { return true; }
   });

puede reemplazar yoururl.com por document.location.pathname .

El quid de la solución es intentar conectarse a su nombre de dominio, si no puede conectarse, está desconectado. funciona en varios navegadores.

harishr
fuente
dado que no hay un nombre de URL después del nombre de dominio, no hay cambio de obtener 404
harishr
A veces no, como si la página de inicio de mi api fuera un 404
Ben Aubin
¿Cómo importa la página de inicio de tu API? no lo entendí. porque cuando llega al servidor, no hay URL, por lo que no hay procesamiento, incluso si su página de inicio es 404, no importa. puede ser si puede proporcionar algún código de muestra, que puedo intentar y comprender el problema que está indicando
harishr
No solo mi API, sino que muchos sitios no tienen una página de inicio. Verifique para asegurarse de que el código de estado y los datos recibidos sean nulos, esa es la mejor manera de asegurarse de que no sea solo un error normal y esperado
Ben Aubin
mi webapi tampoco tiene una página de inicio, pero lo anterior aún funciona. por eso estoy confundido. ¿Cómo importa la página de inicio, dado que está funcionando?
harishr
2

Utilizo la opción FALLBACK en el manifiesto de caché HTML5 para verificar si mi aplicación html5 está en línea o fuera de línea mediante:

FALLBACK:
/online.txt /offline.txt

En la página html utilizo javascript para leer el contenido del archivo txt online / offline:

<script>$.get( "urlto/online.txt", function( data ) {
$( ".result" ).html( data );
alert( data );
});</script>

Cuando esté fuera de línea, el script leerá el contenido del offline.txt. Según el texto de los archivos, puede detectar si la página web está en línea o fuera de línea.

Marcel Scholte
fuente
0

Aquí está mi solución.

Probado con IE, Opera, Chrome, FireFox, Safari, como Phonegap WebApp en IOS 8 y como Phonegap WebApp en Android 4.4.2

Esta solución no funciona con FireFox en localhost.

================================================ ===============================

onlineCheck.js (ruta de archivo: "root / js / onlineCheck.js):

var isApp = false;

function onLoad() {
        document.addEventListener("deviceready", onDeviceReady, false);
}

function onDeviceReady() {
    isApp = true;
    }


function isOnlineTest() {
    alert(checkOnline());
}

function isBrowserOnline(no,yes){
    //Didnt work local
    //Need "firefox.php" in root dictionary
    var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttp');
    xhr.onload = function(){
        if(yes instanceof Function){
            yes();
        }
    }
    xhr.onerror = function(){
        if(no instanceof Function){
            no();
        }
    }
    xhr.open("GET","checkOnline.php",true);
    xhr.send();
}

function checkOnline(){

    if(isApp)
    {
        var xhr = new XMLHttpRequest();
        var file = "http://dexheimer.cc/apps/kartei/neu/dot.png";

        try {
            xhr.open('HEAD', file , false); 
            xhr.send(null);

            if (xhr.status >= 200 && xhr.status < 304) {
                return true;
            } else {
                return false;
            }
        } catch (e) 
        {
            return false;
        }
    }else
    {
        var tmpIsOnline = false;

        tmpIsOnline = navigator.onLine;

        if(tmpIsOnline || tmpIsOnline == "undefined")
        {
            try{
                //Didnt work local
                //Need "firefox.php" in root dictionary
                var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttp');
                xhr.onload = function(){
                    tmpIsOnline = true;
                }
                xhr.onerror = function(){
                    tmpIsOnline = false;
                }
                xhr.open("GET","checkOnline.php",false);
                xhr.send();
            }catch (e){
                tmpIsOnline = false;
            }
        }
        return tmpIsOnline;

    }
}

================================================ ===============================

index.html (ruta de archivo: "root / index.html"):

<!DOCTYPE html>
<html>


<head>
    ...

    <script type="text/javascript" src="js/onlineCheck.js" ></script>

    ...

</head>

...

<body onload="onLoad()">

...

    <div onclick="isOnlineTest()">  
        Online?
    </div>
...
</body>

</html>

================================================ ===============================

checkOnline.php (ruta de archivo: "raíz"):

<?php echo 'true'; ?> 
JoJoWhatsUp
fuente
0

Usando el cuerpo del documento:

<body ononline="onlineConditions()" onoffline="offlineConditions()">(...)</body>

Usando el evento Javascript:

window.addEventListener('load', function() {

  function updateOnlineStatus() {

    var condition = navigator.onLine ? "online" : "offline";
    if( condition == 'online' ){
        console.log( 'condition: online')
    }else{
        console.log( 'condition: offline')
    }

  }

  window.addEventListener('online',  updateOnlineStatus );
  window.addEventListener('offline', updateOnlineStatus );

});

Referencia :
Cuerpo del documento: Evento en línea Evento
Javascript: Eventos en línea y fuera de línea

Pensamientos adicionales:
Para enviar la "conexión de red no es lo mismo que la conexión a Internet" Problema de los métodos anteriores: Puede verificar la conexión a Internet una vez con ajax en el inicio de la aplicación y configurar un modo en línea / fuera de línea. Cree un botón de reconexión para que el usuario se conecte. Y agregue en cada solicitud ajax fallida una función que devuelva al usuario al modo fuera de línea.

Milingu Kilu
fuente
1
Esto no funcionará: window.addEventListener('online', updateOnlineStatus(event) );porque está llamando a la función updateOnlineStatus () inmediatamente. Debería serwindow.addEventListener('online', updateOnlineStatus );
Sébastien Rosset