Related
The websockets server example works as expected. On browser refresh (e.g. S-F5 with chrome), the websocket disconnects, still working as expected. After refresh, the user has to give name again to connect to the server.
How would you capture the refresh-event and keep the user connected? E.g.
Is this doable only on server side or does the client require modifications as well? Haskell examples or links to such would be nice as well as hints on how to do this!
How would you capture the refresh-event...
There isn't really such a thing as a refresh event to detect (I would love to be proved wrong in this!)
... and keep the user connected...
The refresh, or rather, the leaving of the page before loading it again, causes the websocket to disconnect, and (especially if this is the only page on the site that is open), there isn't really much you can do about it.
So the only thing that can be done, is have some sort of auto-reconnect the next time the page loads. A solution that allows this is one where..
when the name is initially entered, the name is saved somewhere in the browser;
when the page reloads, it checks for a previously saved name;
and if it's found, it connects again using that name.
Local storage is one such place to save this, as in the below example, modified from https://github.com/jaspervdj/websockets/tree/master/example to save/retrieve the name from local storage.
$(document).ready(function () {
var savedUser = sessionStorage.getItem("rejoin-user");
if (savedUser) {
joinChat(savedUser);
}
$('#join-form').submit(function () {
joinChat($('#user').val())
});
function joinChat(user) {
sessionStorage.setItem("rejoin-user", user);
$('#warnings').html('');
var ws = createChatSocket();
ws.onopen = function() {
ws.send('Hi! I am ' + user);
};
ws.onmessage = function(event) {
if(event.data.match('^Welcome! Users: ')) {
/* Calculate the list of initial users */
var str = event.data.replace(/^Welcome! Users: /, '');
if(str != "") {
users = str.split(", ");
refreshUsers();
}
$('#join-section').hide();
$('#chat-section').show();
$('#users-section').show();
ws.onmessage = onMessage;
$('#message-form').submit(function () {
var text = $('#text').val();
ws.send(text);
$('#text').val('');
return false;
});
} else {
$('#warnings').append(event.data);
ws.close();
}
};
$('#join').append('Connecting...');
return false;
};
});
... Is this doable only on server side or does the client require modifications as well?
It definitely needs something done in the client to auto-reconnect. The bare bones version above needs no changes to the server, but if you wanted something fancier, like having the cases of initial connect and auto reconnect handled/shown differently somehow, then the server might need to be modified.
I am really new to Selenium. I managed to open a website using the below nodejs code
var webdriver = require('selenium-webdriver');
var driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
console.log(driver);
driver.get('https://web.whatsapp.com');
//perform all other operations here.
https://web.whatsapp.com is opened and I manually scan a QR code and log in. Now I have different javascript files to perform actions like delete, clear chat inside web.whatsapp.com etc...
Now If I get some error, I debug and when I run the script again using node test.js, it takes another 2 minutes to load page and do the steps I needed. I just wanted to reopen the already opened tab and continue my script instead new window opens.
Edit day 2 : Still searching for solution. I tried below code to save object and reuse it.. Is this the correct approach ? I get a JSON parse error though.
var o = new chrome.Options();
o.addArguments("user-data-dir=/Users/vishnu/Library/Application Support/Google/Chrome/Profile 2");
o.addArguments("disable-infobars");
o.addArguments("--no-first-run");
var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).setChromeOptions(o).build();
var savefile = fs.writeFile('data.json', JSON.stringify(util.inspect(driver)) , 'utf-8');
var parsedJSON = require('./data.json');
console.log(parsedJSON);
It took me some time and a couple of different approaches, but I managed to work up something I think solves your problem and allows to develop tests in a rather nice way.
Because it does not directly answer the question of how to re-use a browser session in Selenium (using their JavaScript API), I will first present my proposed solution and then briefly discuss the other approaches I tried. It may give someone else an idea and help them to solve this problem in a nicer/better way. Who knows. At least my attempts will be documented.
Proposed solution (tested and works)
Because I did not manage to actually reuse a browser session (see below), I figured I could try something else. The approach will be the following.
Idea
Have a main loop in one file (say init.js) and tests in a separate file (test.js).
The main loop opens a browser instance and keeps it open. It also exposes some sort of CLI that allows one to run tests (from test.js), inspect errors as they occur and to close the browser instance and stop the main loop.
The test in test.js exports a test function that is being executed by the main loop. It is passed a driver instance to work with. Any errors that occur here are being caught by the main loop.
Because the browser instance is opened only once, we have to do the manual process of authenticating with WhatsApp (scanning a QR code) only once. After that, running a test will reload web.whatsapp.com, but it will have remembered that we authenticated and thus immediately be able to run whatever tests we define in test.js.
In order to keep the main loop alive, it is vital that we catch each and every error that might occur in our tests. I unfortunately had to resort to uncaughtException for that.
Implementation
This is the implementation of the above idea I came up with. It is possible to make this much fancier if you would want to do so. I went for simplicity here (hope I managed).
init.js
This is the main loop from the above idea.
var webdriver = require('selenium-webdriver'),
by = webdriver.By,
until = webdriver.until,
driver = null,
prompt = '> ',
testPath = 'test.js',
lastError = null;
function initDriver() {
return new Promise((resolve, reject) => {
// already opened a browser? done
if (driver !== null) {
resolve();
return;
}
// open a new browser, let user scan QR code
driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('https://web.whatsapp.com');
process.stdout.write("Please scan the QR code within 30 seconds...\n");
driver.wait(until.elementLocated(by.className('chat')), 30000)
.then(() => resolve())
.catch((timeout) => {
process.stdout.write("\b\bTimed out waiting for code to" +
" be scanned.\n");
driver.quit();
reject();
});
});
}
function recordError(err) {
process.stderr.write(err.name + ': ' + err.message + "\n");
lastError = err;
// let user know that test failed
process.stdout.write("Test failed!\n");
// indicate we are ready to read the next command
process.stdout.write(prompt);
}
process.stdout.write(prompt);
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
var chunk = process.stdin.read();
if (chunk === null) {
// happens on initialization, ignore
return;
}
// do various different things for different commands
var line = chunk.trim(),
cmds = line.split(/\s+/);
switch (cmds[0]) {
case 'error':
// print last error, when applicable
if (lastError !== null) {
console.log(lastError);
}
// indicate we are ready to read the next command
process.stdout.write(prompt);
break;
case 'run':
// open a browser if we didn't yet, execute tests
initDriver().then(() => {
// carefully load test code, report SyntaxError when applicable
var file = (cmds.length === 1 ? testPath : cmds[1] + '.js');
try {
var test = require('./' + file);
} catch (err) {
recordError(err);
return;
} finally {
// force node to read the test code again when we
// require it in the future
delete require.cache[__dirname + '/' + file];
}
// carefully execute tests, report errors when applicable
test.execute(driver, by, until)
.then(() => {
// indicate we are ready to read the next command
process.stdout.write(prompt);
})
.catch(recordError);
}).catch(() => process.stdin.destroy());
break;
case 'quit':
// close browser if it was opened and stop this process
if (driver !== null) {
driver.quit();
}
process.stdin.destroy();
return;
}
});
// some errors somehow still escape all catches we have...
process.on('uncaughtException', recordError);
test.js
This is the test from the above idea. I wrote some things just to test the main loop and some WebDriver functionality. Pretty much anything is possible here. I have used promises to make test execution work nicely with the main loop.
var driver, by, until,
timeout = 5000;
function waitAndClickElement(selector, index = 0) {
driver.wait(until.elementLocated(by.css(selector)), timeout)
.then(() => {
driver.findElements(by.css(selector)).then((els) => {
var element = els[index];
driver.wait(until.elementIsVisible(element), timeout);
element.click();
});
});
}
exports.execute = function(d, b, u) {
// make globally accessible for ease of use
driver = d;
by = b;
until = u;
// actual test as a promise
return new Promise((resolve, reject) => {
// open site
driver.get('https://web.whatsapp.com');
// make sure it loads fine
driver.wait(until.elementLocated(by.className('chat')), timeout);
driver.wait(until.elementIsVisible(
driver.findElement(by.className('chat'))), timeout);
// open menu
waitAndClickElement('.icon.icon-menu');
// click profile link
waitAndClickElement('.menu-shortcut', 1);
// give profile time to animate
// this prevents an error from occurring when we try to click the close
// button while it is still being animated (workaround/hack!)
driver.sleep(500);
// close profile
waitAndClickElement('.btn-close-drawer');
driver.sleep(500); // same for hiding profile
// click some chat
waitAndClickElement('.chat', 3);
// let main script know we are done successfully
// we do so after all other webdriver promise have resolved by creating
// another webdriver promise and hooking into its resolve
driver.wait(until.elementLocated(by.className('chat')), timeout)
.then(() => resolve());
});
};
Example output
Here is some example output. The first invocation of run test will open up an instance of Chrome. Other invocations will use that same instance. When an error occurs, it can be inspected as shown. Executing quit will close the browser instance and quit the main loop.
$ node init.js
> run test
> run test
WebDriverError: unknown error: Element <div class="chat">...</div> is not clickable at point (163, 432). Other element would receive the click: <div dir="auto" contenteditable="false" class="input input-text">...</div>
(Session info: chrome=57.0.2987.133)
(Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.9.0-2-amd64 x86_64)
Test failed!
> error
<prints complete stacktrace>
> run test
> quit
You can run tests in other files by simply calling them. Say you have a file test-foo.js, then execute run test-foo in the above prompt to run it. All tests will share the same Chrome instance.
Failed attempt #1: saving and restoring storage
When inspecting the page using my development tools, I noticed that it appears to use the localStorage. It is possible to export this as JSON and write it to a file. On a next invocation, this file can be read, parsed and written to the new browser instance storage before reloading the page.
Unfortunately, WhatsApp still required me to scan the QR code. I have tried to figure out what I missed (cookies, sessionStorage, ...), but did not manage. It is possible that WhatsApp registers the browser as being disconnected after some time has passed. Or that it uses other browser properties (session ID?) to recognize the browser. This is pure speculating from my side though.
Failed attempt #2: switching session/window
Every browser instance started via WebDriver has a session ID. This ID can be retrieved, so I figured it may be possible to start a session and then connect to it from the test cases, which would then be run from a separate file (you can see this is the predecessor of the final solution). Unfortunately, I have not been able to figure out a way to set the session ID. This may actually be a security concern, I am not sure. People more expert in the usage of WebDriver might be able to clarify here.
I did find out that it is possible to retrieve a list of window handles and switch between them. Unfortunately, windows are only shared within a single session and not across sessions.
I need some help on how I could check the internet connection using Javascript or jQuery or any library if available. cause i'm developing an offline application and I want to show a version if the user is offline and another version if the user is online.
For the moment i'm using this code :
if (navigator.onLine) {
alert('online');
} else {
alert('offline');
}
But this is working very slow to detect. sometimes it's just connected to a network without internet, it takes 5 to 10 seconds to alert false (No internet).
I took a look at Offline.js library, but I'm not sure if this library is useful in my case. and I don't know how to use it
I just got this bit of code functionality from a Mozilla Site:
window.addEventListener('load', function(e) {
if (navigator.onLine) {
console.log('We\'re online!');
} else {
console.log('We\'re offline...');
}
}, false);
window.addEventListener('online', function(e) {
console.log('And we\'re back :).');
}, false);
window.addEventListener('offline', function(e) {
console.log('Connection is down.');
}, false);
They even have a link to see it working. I tried it in IE, Firefox and Chrome. Chrome appeared the slowest but it was only about half a second.
i think you should try OFFLINE.js.. it looks pretty easy to use, just give it a try.
it even provides the option checkOnLoad which checks the connection immediately on page load.
Offline.check(): Check the current status of the connection.
Offline.state: The current state of the connection 'up' or 'down'
haven't tried it, would be nice to know if it works as intended.
EDIT took a little peak into the code, it uses the method with FAILED XHR REQUEST suggested in THIS SO Question
Take a look at Detect that the Internet connection is offline? Basically, make an ajax request to something you know is likely to be up (say google.com) and if it fails, there is no internet connection.
navigator.onLine is a property that maintains a true/false value (true for online, false for offline). This property is updated whenever the user switches into "Offline Mode".
window.addEventListener('load', function() {
function updateOnlineStatus(event) {
document.body.setAttribute("data-online", navigator.onLine);
}
updateOnlineStatus();
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
});
// check if online/offline
// http://www.kirupa.com/html5/check_if_internet_connection_exists_in_javascript.htm
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "http://www.yoursite.com/somefile.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
My solution is to grab a very small image (1x1), not cached and always onLine.
<head>
<script src="jquery.min.js"></script>
</head>
<body>
<script>
$( document ).ready(function() {
function onLine() {
alert("onLine")
}
function offLine() {
alert("offLine")
}
var i = new Image();
i.onload = onLine;
i.onerror = offLine;
i.src = 'http://www.google-analytics.com/__utm.gif';
});
</script>
<body>
Notes:
Use a local copy of jQuery otherwise it won't work offLine.
I've tested the code onLine/offLine and it works without delay.
Works with all browsers, Desktop or Mobile.
In case you wonder, there's no tracking made from Google Analytics as we don't use any arguments.
Feel free to change the image, just make sure it doesn't get cached and it's small in size.
Try utilizing WebRTC , see diafygi/webrtc-ips; in part
Additionally, these STUN requests are made outside of the normal
XMLHttpRequest procedure, so they are not visible in the developer
console or able to be blocked by plugins such as AdBlockPlus or
Ghostery. This makes these types of requests available for online
tracking if an advertiser sets up a STUN server with a wildcard
domain.
modified minimally to log "online" or "offline" at console
// https://github.com/diafygi/webrtc-ips
function online(callback){
//compatibility for firefox and chrome
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var useWebKit = !!window.webkitRTCPeerConnection;
//bypass naive webrtc blocking using an iframe
if(!RTCPeerConnection) {
//NOTE: you need to have an iframe in the page
// right above the script tag
//
//<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>
//<script>...getIPs called in here...
//
var win = iframe.contentWindow;
RTCPeerConnection = win.RTCPeerConnection
|| win.mozRTCPeerConnection
|| win.webkitRTCPeerConnection;
useWebKit = !!win.webkitRTCPeerConnection;
}
//minimal requirements for data connection
var mediaConstraints = {
optional: [{RtpDataChannels: true}]
};
//firefox already has a default stun server in about:config
// media.peerconnection.default_iceservers =
// [{"url": "stun:stun.services.mozilla.com"}]
var servers = undefined;
//add same stun server for chrome
if(useWebKit)
servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};
//construct a new RTCPeerConnection
var pc = new RTCPeerConnection(servers, mediaConstraints);
//create a bogus data channel
pc.createDataChannel("");
var fn = function() {};
//create an offer sdp
pc.createOffer(function(result){
//trigger the stun server request
pc.setLocalDescription(result, fn, fn);
}, fn);
//wait for a while to let everything done
setTimeout(function(){
//read candidate info from local description
var lines = pc.localDescription.sdp.split("\n");
// return `true`:"online" , or `false`:"offline"
var res = lines.some(function(line) {
return line.indexOf("a=candidate") === 0
});
callback(res);
}, 500);
}
//Test: Print "online" or "offline" into the console
online(function(connection) {
if (connection) {
console.log("online")
} else {
console.log("offline")
}
});
You can use SignalR, if you're developing using MS web technologies. SignalR will establish either long polling or web sockets depending on your server/client browser technology, transparent to you the developer. You don't need to use it for anything else than determining if you have an active connection to the site or not.
If SignalR disconnects for any reason, then you have lost connection to the site, as long as your SignalR server instance is actually installed on the site. Thus, you can use $.connection.hub.disconnected() event/method on the client to set a global var which holds your connection status.
Read up about SignalR and how to use it for determining connection states here...
http://www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events#clientdisconnect
See How do I check connection type (WiFi/LAN/WWAN) using HTML5/JavaScript? answers:
Rob W suggests navigator.connection;
Bergi suggests Windows.Networking.Connectivity API through this tutorial;
Gerard Sexton suggests Gmail approach.
You can use the new Fetch API which will trigger an error almost immediately if no network is present.
The problem with this is that the Fetch API has infant support at the moment (currently Chrome has the most stable implementation, Firefox and Opera is getting there, IE does not support it). There exists a polyfill to support the fetch principle but not necessarily the rapid return as with a pure implementation. On the other hand, an offline app would require a modern browser...
An example which will try to load a plain text file over HTTPS to avoid CORS requirements (link is picked at random, you should set up a server with a tiny text file to test against - test in Chrome, for now):
fetch("https://link.to/some/testfile")
.then(function(response) {
if (response.status !== 200) { // add more checks here, ie. 30x etc.
alert("Not available"); // could be server errors
}
else
alert("OK");
})
.catch(function(err) {
alert("No network"); // likely network errors (incl. no connection)
});
Another option is to set up a Service worker and use fetch from there. This way you could serve an optional/custom offline page or a cached page when the requested page is not available. Also this is a very fresh API.
best one liner
console.log(navigator.onLine ? 'online' : 'offline');
I'm writing a Firefox extension that creates a socket server which will output the active tab's URL when a client makes a connection to it. I have the following code in my javascript file:
var serverSocket;
function startServer()
{
var listener =
{
onSocketAccepted : function(socket, transport)
{
try {
var outputString = gBrowser.currentURI.spec + "\n";
var stream = transport.openOutputStream(0,0,0);
stream.write(outputString,outputString.length);
stream.close();
} catch(ex2){ dump("::"+ex2); }
},
onStopListening : function(socket, status){}
};
try {
serverSocket = Components.classes["#mozilla.org/network/server-socket;1"]
.createInstance(Components.interfaces.nsIServerSocket);
serverSocket.init(7055,true,-1);
serverSocket.asyncListen(listener);
} catch(ex){ dump(ex); }
document.getElementById("status").value = "Started";
}
function stopServer ()
{
if (serverSocket)
serverSocket.close();
}
window.addEventListener("load", function() { startServer(); }, false);
window.addEventListener("unload", function() { stopServer(); }, false);
As it is, it works for multiple tabs in a single window. If I open multiple windows, it ignores the additional windows. I think it is creating a server socket for each window, but since they are using the same port, the additional sockets fail to initialize. I need it to create a server socket when the browser launches and continue running when I close the windows (Mac OS X). As it is, when I close a window but Firefox remains running, the socket closes and I have to restart firefox to get it up an running. How do I go about that?
Firefox extension overlays bind to window objects. One way around this is to create an XPCOM component or find one that someone else already created to allow you to build functionality without binding it to the window objects.
Of course, section #2 below on Observer Notifications may be helpful as well.
Possible workaround: #1
Instead of calling "startServer()" each time a window is opened, you could have a flag called windowCount that you could increment each time you open a new window. If windowCount is greater than 0, don't call startServer().
As windows close, you could decrement the count. Once it hits 0, stop the server.
Here is information from the Mozilla forums on this problem:
http://forums.mozillazine.org/viewtopic.php?f=19&t=2030279
Possible workaround #2:
With that said, I've also found documentation for Observer Notifications, which may be helpful as there is a section on Application Startup and Shutdown:
https://developer.mozilla.org/en/Observer_Notifications
UPDATE:
Here are some resources on creating XPCOM components in JavaScript and in C++:
https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript
http://www.codeproject.com/KB/miscctrl/XPCOM_Creation.aspx
https://developer.mozilla.org/en/creating_xpcom_components
You probably want to:
Move your code into a JavaScript component
Register your component as a profile-after-change observer
Whenever someone makes a connection to your socket, find the active window and return its URL.
Use something like
var wm = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var spec = win ? win.getBrowser().currentURI.spec : "";
var outputString = spec + "\n";
etc.
I'm trying to accurately detect when the browser goes offline, using the HTML5 online and offline events.
Here's my code:
<script>
// FIREFOX
$(window).bind("online", applicationBackOnline);
$(window).bind("offline", applicationOffline);
//IE
window.onload = function() {
document.body.ononline = IeConnectionEvent;
document.body.onoffline = IeConnectionEvent;
}
</script>
It works fine when I just hit "Work offline" on either Firefox or IE, but it's kind of randomly working when I actually unplug the wire.
What's the best way to detect this change? I'd like to avoid repeating ajax calls with timeouts.
Currently in 2011, the various browser vendors cannot agree on how to define offline. Some browsers have a Work Offline feature, which they consider separate to a lack of network access, which again is different to internet access. The whole thing is a mess. Some browser vendors update the navigator.onLine flag when actual network access is lost, others don't.
From the spec:
Returns false if the user agent is
definitely offline (disconnected from
the network). Returns true if the user
agent might be online.
The events online and offline are
fired when the value of this attribute
changes.
The navigator.onLine attribute must
return false if the user agent will
not contact the network when the user
follows links or when a script
requests a remote page (or knows that
such an attempt would fail), and must
return true otherwise.
Finally, the spec notes:
This attribute is inherently
unreliable. A computer can be
connected to a network without having
Internet access.
The major browser vendors differ on what "offline" means.
Chrome, Safari, and Firefox (since version 41) will detect when you go "offline" automatically - meaning that "online" events and properties will fire automatically when you unplug your network cable.
Mozilla Firefox (before version 41), Opera, and IE take a different approach, and consider you "online" unless you explicitly pick "Offline Mode" in the browser - even if you don't have a working network connection.
There are valid arguments for the Firefox/Mozilla behavior, which are outlined in the comments of this bug report:
https://bugzilla.mozilla.org/show_bug.cgi?id=654579
But, to answer the question - you can't rely on the online/offline events/property to detect if there is actually network connectivity.
Instead, you must use alternate approaches.
The "Notes" section of this Mozilla Developer article provides links to two alternate methods:
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"
This links to an example of the "listening for AppCache error events" approach:
http://www.html5rocks.com/en/mobile/workingoffthegrid/#toc-appcache
...and an example of the "listening for XMLHttpRequest failures" approach:
http://www.html5rocks.com/en/mobile/workingoffthegrid/#toc-xml-http-request
HTH,
-- Chad
Today there's an open source JavaScript library that does this job: it's called Offline.js.
Automatically display online/offline indication to your users.
https://github.com/HubSpot/offline
Be sure to check the full README. It contains events that you can hook into.
Here's a test page. It's beautiful/has a nice feedback UI by the way! :)
Offline.js Simulate UI is an Offline.js plug-in
that allows you to test how your pages respond to different
connectivity states without having to use brute-force methods to
disable your actual connectivity.
The best way which works now on all Major Browsers is the following 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;
}
})();
Source: http://robertnyman.com/html5/offline/online-offline-events.html
Since recently, navigator.onLine shows the same on all major browsers, and is thus useable.
if (navigator.onLine) {
// do things that need connection
} else {
// do things that don't need connection
}
The oldest versions that support this in the right way are: Firefox 41, IE 9, Chrome 14 and Safari 5.
Currently this will represent almost the whole spectrum of users, but you should always check what the users of your page have of capabilities.
Previous to FF 41, it would only show false if the user put the browser manually in offline mode. In IE 8, the property was on the body, instead of window.
source: caniuse
The window.navigator.onLine attribute and its associated events are currently unreliable on certain web browsers (especially Firefox desktop) as #Junto said, so I wrote a little function (using jQuery) that periodically checks the network connectivity status and raise the appropriate offline and online events:
// 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
}
}
});
}
You can use it like this:
// 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
}
To listen to the offline and online events (with the help of jQuery):
$(window).bind('online offline', function(e) {
if (!IS_ONLINE || !window.navigator.onLine) {
alert('We have a situation here');
} else {
alert('Battlestation connected');
}
});
navigator.onLine is a mess
I face this when trying to make an ajax call to the server.
There are several possible situations when the client is offline:
the ajax call timouts and you receive error
the ajax call returns success, but the msg is null
the ajax call is not executed because browser decides so (may be this is when navigator.onLine becomes false after a while)
The solution I am using is to control the status myself with javascript. I set the condition of a successful call, in any other case I assume the client is offline.
Something like this:
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);
}
});
}
In HTML5 you can use the navigator.onLine property. Look here:
http://www.w3.org/TR/offline-webapps/#related
Probably your current behavior is random as the javascript only ready the "browser" variable and then knows if you're offline and online, but it doesn't actually check the Network Connection.
Let us know if this is what you're looking for.
Kind Regards,
Please find the require.js module that I wrote for 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;
});
Please read this blog post and let me know your thoughts. http://zen-and-art-of-programming.blogspot.com/2014/04/html-5-offline-application-development.html It contains a code sample using offline.js to detect when the client is offline.
you can detect offline cross-browser way easily like below
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; }
});
you can replace yoururl.com by document.location.pathname.
The crux of the solution is, try to connect to your domain name, if you are not able to connect - you are offline. works cross browser.
I use the FALLBACK option in the HTML5 cache manifest to check if my html5 app is online or offline by:
FALLBACK:
/online.txt /offline.txt
In the html page i use javascript tot read the contents of the online/offline txt file:
<script>$.get( "urlto/online.txt", function( data ) {
$( ".result" ).html( data );
alert( data );
});</script>
When offline the script will read the contents of the offline.txt.
Based on the text in the files you can detect if the webpage is online of offline.
Using Document Body:
<body ononline="onlineConditions()" onoffline="offlineConditions()">(...)</body>
Using Javascript Event:
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 );
});
Reference:
Document-Body: ononline Event
Javascript-Event: Online and offline events
Additional Thoughts:
To ship around the "network connection is not the same as internet connection" Problem from the above methods: You can check the internet connection once with ajax on the application start and configure an online/offline mode. Create a reconnect button for the user to go online. And add on each failed ajax request a function that kick the user back into the offline mode.
Here is my solution.
Tested with IE, Opera, Chrome, FireFox, Safari, as Phonegap WebApp on IOS 8 and as Phonegap WebApp on Android 4.4.2
This solution isn't working with FireFox on localhost.
=================================================================================
onlineCheck.js (filepath: "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 (filepath: "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 (filepath: "root"):
<?php echo 'true'; ?>
well, you can try the javascript plugin which can monitor the browser connection in real time and notifies the user if internet or the browsers connection with the internet went down.
Wiremonkey Javascript plugin
and the demo you can find here
http://ryvan-js.github.io/
<html>
<head>
<script>
window.addEventListener("online",function(){
document.getElementById('note').
innerHTML='you are online';
});
window.addEventListener("offline",function(){
document.getElementById('note').
innerHTML='you are offline';
});
</script>
</head>
<body>
<div id="note"> </div>
</body>
</html>