如何跨浏览器检测在线/离线事件?

118

我正在尝试使用HTML5的在线和离线事件准确地检测浏览器何时离线。

这是我的代码:

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

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

当我在Firefox或IE上仅点击“脱机工作”时,它的表现很好,但是当我实际拔掉线时,它有点随机。

最好的方法是检测这种变化的方式是什么? 我想避免使用带有超时的重复ajax调用。


2
我同意Trefex的观点,但我也想补充一点,大多数应用程序的连接检测支持都很糟糕:仅仅因为电线被拔掉并不立即构成失去连接。依赖于一个不能真正测试连接是否打开的方法,无法保证准确的结果。 - mattbasta
谢谢您的建议。那么您会推荐使用Ajax方法吗?即保持发送带有超时的XHR调用? - Pierre Duplouy
Firefox(以及IE和Opera)的实现是错误的。请参见我在此处发表的评论:https://bugzilla.mozilla.org/show_bug.cgi?id=654579#c9 - thewoolleyman
4
您或许想要查看 Offline.js,这是一个专门为此目的构建的开源库。 - Adam
15个回答

73

目前在2011年,各种浏览器厂商无法就如何定义离线达成一致。一些浏览器有一个"工作脱机"功能,他们认为这与缺乏网络访问是不同的,而后者又不同于互联网访问。整个事情一团糟。一些浏览器厂商在实际网络访问丢失时更新“navigator.onLine”标志,而另一些则没有。

根据规范:

如果用户代理程序明确处于脱机状态(与网络断开连接),则返回false。如果用户代理程序可能在线,则返回true。

当此属性的值更改时,将触发online和offline事件。

如果用户代理程序在用户跟随链接或脚本请求远程页面时(或知道这样的尝试将失败),不会联系网络,则navigator.onLine属性必须返回false,否则必须返回true。

最后,规范指出:

该属性本质上是不可靠的。一台计算机可以连接到网络,但没有Internet访问权限。


24
只有 Chrome 在连接丢失时正确设置 navigator.onLine,而 Safari 和 Firefox 如果断开互联网连接就从不将该标志设置为 false。 - chovy
2
@chovy 现在怎么样?我最近在 Firefox/Chrome 中进行了测试,并得到了预期的结果,看到当我关闭和打开互联网连接时,标志被设置。 - James Cazzetta
13
今天,我打开了OSX Chrome 55.0.2883.95、Safari 10.0.3和FF 50.1.0。当我保持在我的网络上时,所有的window.navigator.onLine都运行良好,但当我从我的路由器上拔掉线缆时,它们都正确地检测到了离线状态。 - nycynik
4
navigator.onLine被所有主流浏览器所支持(并且已经支持了一段时间):https://caniuse.com/#feat=online-status。 - rlueder
如果你正在使用Reactjs,你可以使用react-detect-offline npm包。 - Shubham Chopra
显示剩余5条评论

34
主要的浏览器供应商对于“离线”定义存在差异。
Chrome、Safari和Firefox(自版本41起)会自动检测您何时进入“离线”状态——也就是说,当您拔出网络电缆时,“在线”事件和属性将自动触发。
Mozilla Firefox(41版本之前)、Opera和IE采取不同的方法,并且只有在您明确选择浏览器中的“离线模式”时才认为您处于“离线”状态,即使您没有可用的网络连接。
对于Firefox/Mozilla的行为,有合理的论据,这些论据在此错误报告的评论中概述。

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

但是,回答这个问题 - 你不能依靠在线/离线事件/属性来检测是否实际上存在网络连接。

相反,您必须使用替代方法。

此 Mozilla 开发人员文章的 "Notes" 部分提供了两种替代方法的链接:

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

"If the API isn't implemented in the browser, you can use other signals to detect if you are offline including listening for AppCache error events and responses from XMLHttpRequest."
这里提到了一种“监听AppCache错误事件”的方法示例。

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

...以及“监听XMLHttpRequest失败”的方法示例:

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

HTH, -- Chad


2
从Firefox 41开始:“根据您提到的文档,当Windows、Linux和OS X报告网络连接变化时,更新此属性。”因此,如果您使用浏览器的“离线模式”进行浏览,它并不仅仅是离线。 - dude

17

今天有一个开源的JavaScript库可以完成这项工作,它叫做Offline.js

自动向您的用户显示在线/离线指示。

https://github.com/HubSpot/offline

请务必检查完整的README文件。它包含了你可以挂钩的事件。

下面是一个测试页面。顺便说一下,它很漂亮/有一个不错的反馈UI!:)

Offline.js Simulate UI是一个Offline.js插件,它允许您测试您的页面如何响应不同的连通状态,而无需使用蛮力方法来禁用您的实际连通性。


2
该库实际上是通过在后台重复获取本地favicon来工作的。在我看来,这个库太“大”了,有太多的功能;主要的技巧就是不断地获取favicon。 - Karel Bílek
1
当我拔掉网络电缆时,它无法检测到离线状态。 - poshest

16
现在在所有主流浏览器上运行的最佳方式是以下脚本:
(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;
    }
})();

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

在Web开发中,有时候需要检测用户是否在线。HTML5提供了一种解决方案 - Online/Offline事件。

当网络连接断开或重新连接时,会分别触发offline和online事件。可以通过监听这些事件来执行相应的操作,例如更新UI界面、保存数据等。


6
正如代码本身中所清楚注明的那样 - 如果你拔掉以太网线或关闭wifi,它在Firefox/Chrome中是无法使用的 - Manish
1
我尝试访问“源”链接并断开以太网电缆,IE 显示“您处于离线状态”,但 Firefox/Chrome 没有(使用所有浏览器的最新版本)。也许我错过了什么? - Manish

15

最近,所有主要浏览器的navigator.onLine表现一致,因此可用。

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

支持此功能的最早版本为:Firefox 41、IE 9、Chrome 14和Safari 5。
目前,这几乎代表了所有用户的范围,但您应该始终检查页面用户的能力。
在FF 41之前,只有当用户手动将浏览器设置为离线模式时,它才会显示false。在IE 8中,该属性位于body而不是window
来源:caniuse

13

如@Junto所说,特别是Firefox桌面版,某些浏览器的 window.navigator.onLine 属性及其相关事件目前不可靠。因此,我编写了一个小函数(使用jQuery),定期检查网络连接状态并引发相应的 offlineonline 事件:

// Global variable somewhere in your app to replicate the 
// window.navigator.onLine variable (this last 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
      }
    }
  });
}

您可以这样使用它:
// Hack to use the checkNetwork() function only on Firefox 
// (https://dev59.com/Um025IYBdhLWcg3w9qyQ#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
}

通过jQuery监听在线和离线事件:

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

8

navigator.onLine很混乱

当我试图向服务器发起ajax请求时,我会遇到这个问题。

当客户端离线时,可能会出现几种情况:

  • ajax调用超时并返回错误
  • ajax调用返回成功,但消息为null
  • ajax调用未被执行,因为浏览器决定不执行(可能是navigator.onLine在一段时间后变为false时)

我正在使用的解决方案是使用JavaScript手动控制状态。我设定了一个成功调用的条件,在任何其他情况下,我假设客户端处于离线状态。 类似于以下内容:

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);
          }
        });
      }

5
在HTML5中,您可以使用navigator.onLine属性。请参见此处:http://www.w3.org/TR/offline-webapps/#related。可能您当前的行为是随机的,因为JavaScript只读取“browser”变量,然后知道您是离线还是在线,但实际上它并没有检查网络连接。如果这正是您要寻找的,请告诉我们。祝好!

谢谢你的帮助Trefex。我修改了我的代码,现在只检查navigator.onLine属性,但我得到了与之前相同的行为。请看一下mattbasta的评论。 - Pierre Duplouy
嗨,Pedro,我同意mattbasta的看法,但我希望它能对你有用 :) 我肯定会使用Ajax方法来查询一些你知道始终在线的URL,然后你就会知道连接是否丢失。另外,为什么你需要准确检测在线/离线状态?也许如果我们了解更多,你的问题可能会有其他解决方案。告诉我们吧。 - Trefex
1
好的,谢谢 :) 我只是认为如果应用程序能够自动检测到连接状态的变化(无需手动在FF或IE中启用离线模式),那么对用户来说会更好。这样,当应用程序离线时,它将使用其本地缓存而不是查询服务器。我发现了John Resig的这篇文章,它几乎解释了为什么这行不通:http://ejohn.org/blog/offline-events/ - Pierre Duplouy
感谢您的博客文章。真正深入的分析并且直戳要点。我认为,如果您查询一些服务器(也许是您自己的),然后在出现x次超时时切换到本地缓存,这将是您想要实现的最佳方式。您觉得呢? - Trefex
是的,我想这是目前最好的选择。我希望所有的浏览器最终都能够自己检测到实际的连接丢失:使用navigator.onLine非常简单,这不应该更复杂。你不觉得吗? - Pierre Duplouy
Chrome是唯一在连接丢失时更新navigator.onLine的浏览器。 - chovy

3
您可以轻松地像下面这样跨浏览器检测离线状态:
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; }
   });

您可以用document.location.pathname替换yoururl.com。

解决方案的关键是尝试连接到您的域名,如果无法连接,则表示您处于离线状态。适用于跨浏览器。


有时候会出现这种情况,比如我的API主页是404。 - Ben Aubin
不仅仅是我的API,还有很多网站都没有主页。检查状态码和收到的数据是否为空,这是确保它不仅仅是一个正常、可预期的错误的最佳方法。 - Ben Aubin
@penne12 我使用了这个答案,在我的 Web API 中也能正常工作。 - algos
它能够工作,但会带来意想不到的后果,正如我所解释的那样(错误指的是一个错误或者不在200之内的错误,有时服务器也会宕机)。 - Ben Aubin
运行得非常好,如果无法连接到主机,则返回0状态,即response.status == 0 - Niels
显示剩余2条评论

3
请找到我为离线写的require.js模块。
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;
});

请阅读这篇博客文章并告诉我您的想法。http://zen-and-art-of-programming.blogspot.com/2014/04/html-5-offline-application-development.html它包含了一个使用offline.js的代码示例,用于检测客户端是否离线。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接