Get the hashchange event to work in all browsers (including IE7) - javascript

I have some code (written by another developer) that is doing AJAX page loading inside of WordPress (e.g. no page reloads) when you click a nav item, AJAX refreshes the primary content area. My problem is that it's broken in IE7 and I have no idea where to start in terms of debugging.
The original opening lines were
var queue = 0;
$('document').ready(function() {
window.addEventListener("hashchange", hashChange, false);
// Define window location variables
var windowHost = window.location.host,
windowHash = window.location.hash,
windowPath = window.location.pathname;
But I changed them to make the addEventListener conditional on the basis of whether that method was present or not. Some research told me that the method is not available in older versions of IE (e.g. 7 in my case). Also, the IE7 debug console was identifying that as an unavailable method, so that's pretty clear. I rewrote the lines as follows, but the code is still not working:
var queue = 0;
$('document').ready(function() {
if(window.addEventListener) {
window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
window.attachEvent("hashchange", hashchange, false);
}
// Define window location variables
var windowHost = window.location.host,
windowHash = window.location.hash,
windowPath = window.location.pathname;
The full original script can be viewed in this pastebin: http://pastebin.com/Jc9ySvrb

attachEvent requires events to be prefixed with on.
You've different capitalizations for the method. Change hashchange in attachEvent tohashChange to get the event to work in IE8.
Use the suggested implementation to support the hashchange implementation for IE7- and other old browsers.
I have created a cross-browser implementation, which adds the hashchange feature to browsers, even those who do not support it. The fallback is based on the specification.
//function hashchange is assumed to exist. This function will fire on hashchange
if (!('onhashchange' in window)) {
var oldHref = location.href;
setInterval(function() {
var newHref = location.href;
if (oldHref !== newHref) {
var _oldHref = oldHref;
oldHref = newHref;
hashChange.call(window, {
'type': 'hashchange',
'newURL': newHref,
'oldURL': _oldHref
});
}
}, 100);
} else if (window.addEventListener) {
window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
window.attachEvent("onhashchange", hashChange);
}
Note: This code is useful for one hashchange event. If you want to add multiple hashchange handlers, use the following method.
It defines two functions, addHashChange and removeHashChange. Both methods take a function as an argument.
(function() {
if ('onhashchange' in window) {
if (window.addEventListener) {
window.addHashChange = function(func, before) {
window.addEventListener('hashchange', func, before);
};
window.removeHashChange = function(func) {
window.removeEventListener('hashchange', func);
};
return;
} else if (window.attachEvent) {
window.addHashChange = function(func) {
window.attachEvent('onhashchange', func);
};
window.removeHashChange = function(func) {
window.detachEvent('onhashchange', func);
};
return;
}
}
var hashChangeFuncs = [];
var oldHref = location.href;
window.addHashChange = function(func, before) {
if (typeof func === 'function')
hashChangeFuncs[before?'unshift':'push'](func);
};
window.removeHashChange = function(func) {
for (var i=hashChangeFuncs.length-1; i>=0; i--)
if (hashChangeFuncs[i] === func)
hashChangeFuncs.splice(i, 1);
};
setInterval(function() {
var newHref = location.href;
if (oldHref !== newHref) {
var _oldHref = oldHref;
oldHref = newHref;
for (var i=0; i<hashChangeFuncs.length; i++) {
hashChangeFuncs[i].call(window, {
'type': 'hashchange',
'newURL': newHref,
'oldURL': _oldHref
});
}
}
}, 100);
})();
// Usage, infinitely many times:
addHashChange(function(e){alert(e.newURL||location.href);});

attachEvent takes on two params:
bSuccess = object.attachEvent(sEvent, fpNotify)
[And is needed for all versions of IE below IE9! :( See MDN reference
]
This could work:
if(window.addEventListener) {
window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
window.attachEvent("onhashchange", hashchange);//SEE HERE...
//missed the on. Fixed thanks to #Robs answer.
}
Of course if it is possible, you should just use JQuery, since it encapsulates all this for your.
And as always there is a plugin out there:
http://benalman.com/projects/jquery-hashchange-plugin/

Related

Stop clock when page is not on screen [duplicate]

I have JavaScript that is doing activity periodically. When the user is not looking at the site (i.e., the window or tab does not have focus), it'd be nice to not run.
Is there a way to do this using JavaScript?
My reference point: Gmail Chat plays a sound if the window you're using isn't active.
Since originally writing this answer, a new specification has reached recommendation status thanks to the W3C. The Page Visibility API (on MDN) now allows us to more accurately detect when a page is hidden to the user.
document.addEventListener("visibilitychange", onchange);
Current browser support:
Chrome 13+
Internet Explorer 10+
Firefox 10+
Opera 12.10+ [read notes]
The following code falls back to the less reliable blur/focus method in incompatible browsers:
(function() {
var hidden = "hidden";
// Standards:
if (hidden in document)
document.addEventListener("visibilitychange", onchange);
else if ((hidden = "mozHidden") in document)
document.addEventListener("mozvisibilitychange", onchange);
else if ((hidden = "webkitHidden") in document)
document.addEventListener("webkitvisibilitychange", onchange);
else if ((hidden = "msHidden") in document)
document.addEventListener("msvisibilitychange", onchange);
// IE 9 and lower:
else if ("onfocusin" in document)
document.onfocusin = document.onfocusout = onchange;
// All others:
else
window.onpageshow = window.onpagehide
= window.onfocus = window.onblur = onchange;
function onchange (evt) {
var v = "visible", h = "hidden",
evtMap = {
focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
};
evt = evt || window.event;
if (evt.type in evtMap)
document.body.className = evtMap[evt.type];
else
document.body.className = this[hidden] ? "hidden" : "visible";
}
// set the initial state (but only if browser supports the Page Visibility API)
if( document[hidden] !== undefined )
onchange({type: document[hidden] ? "blur" : "focus"});
})();
onfocusin and onfocusout are required for IE 9 and lower, while all others make use of onfocus and onblur, except for iOS, which uses onpageshow and onpagehide.
I would use jQuery because then all you have to do is this:
$(window).blur(function(){
//your code here
});
$(window).focus(function(){
//your code
});
Or at least it worked for me.
There are 3 typical methods used to determine if the user can see the HTML page, however none of them work perfectly:
The W3C Page Visibility API is supposed to do this (supported since: Firefox 10, MSIE 10, Chrome 13). However, this API only raises events when the browser tab is fully overriden (e.g. when the user changes from one tab to another one). The API does not raise events when the visibility cannot be determined with 100% accuracy (e.g. Alt+Tab to switch to another application).
Using focus/blur based methods gives you a lot of false positive. For example, if the user displays a smaller window on top of the browser window, the browser window will lose the focus (onblur raised) but the user is still able to see it (so it still need to be refreshed). See also http://javascript.info/tutorial/focus
Relying on user activity (mouse move, clicks, key typed) gives you a lot of false positive too. Think about the same case as above, or a user watching a video.
In order to improve the imperfect behaviors described above, I use a combination of the 3 methods: W3C Visibility API, then focus/blur and user activity methods in order to reduce the false positive rate. This allows to manage the following events:
Changing browser tab to another one (100% accuracy, thanks to the W3C Page Visibility API)
Page potentially hidden by another window, e.g. due to Alt+Tab (probabilistic = not 100% accurate)
User attention potentially not focused on the HTML page (probabilistic = not 100% accurate)
This is how it works: when the document lose the focus, the user activity (such as mouse move) on the document is monitored in order to determine if the window is visible or not. The page visibility probability is inversely proportional to the time of the last user activity on the page: if the user makes no activity on the document for a long time, the page is most probably not visible. The code below mimics the W3C Page Visibility API: it behaves the same way but has a small false positive rate. It has the advantage to be multibrowser (tested on Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id="x"></div>
<script>
/**
Registers the handler to the event for the given object.
#param obj the object which will raise the event
#param evType the event type: click, keypress, mouseover, ...
#param fn the event handler function
#param isCapturing set the event mode (true = capturing event, false = bubbling event)
#return true if the event handler has been attached correctly
*/
function addEvent(obj, evType, fn, isCapturing){
if (isCapturing==null) isCapturing=false;
if (obj.addEventListener){
// Firefox
obj.addEventListener(evType, fn, isCapturing);
return true;
} else if (obj.attachEvent){
// MSIE
var r = obj.attachEvent('on'+evType, fn);
return r;
} else {
return false;
}
}
// register to the potential page visibility change
addEvent(document, "potentialvisilitychange", function(event) {
document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s<br>";
});
// register to the W3C Page Visibility API
var hidden=null;
var visibilityChange=null;
if (typeof document.mozHidden !== "undefined") {
hidden="mozHidden";
visibilityChange="mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden="msHidden";
visibilityChange="msvisibilitychange";
} else if (typeof document.webkitHidden!=="undefined") {
hidden="webkitHidden";
visibilityChange="webkitvisibilitychange";
} else if (typeof document.hidden !=="hidden") {
hidden="hidden";
visibilityChange="visibilitychange";
}
if (hidden!=null && visibilityChange!=null) {
addEvent(document, visibilityChange, function(event) {
document.getElementById("x").innerHTML+=visibilityChange+": "+hidden+"="+document[hidden]+"<br>";
});
}
var potentialPageVisibility = {
pageVisibilityChangeThreshold:3*3600, // in seconds
init:function() {
function setAsNotHidden() {
var dispatchEventRequired=document.potentialHidden;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent();
}
function initPotentiallyHiddenDetection() {
if (!hasFocusLocal) {
// the window does not has the focus => check for user activity in the window
lastActionDate=new Date();
if (timeoutHandler!=null) {
clearTimeout(timeoutHandler);
}
timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms to avoid rounding issues under Firefox
}
}
function dispatchPageVisibilityChangeEvent() {
unifiedVisilityChangeEventDispatchAllowed=false;
var evt = document.createEvent("Event");
evt.initEvent("potentialvisilitychange", true, true);
document.dispatchEvent(evt);
}
function checkPageVisibility() {
var potentialHiddenDuration=(hasFocusLocal || lastActionDate==null?0:Math.floor((new Date().getTime()-lastActionDate.getTime())/1000));
document.potentiallyHiddenSince=potentialHiddenDuration;
if (potentialHiddenDuration>=potentialPageVisibility.pageVisibilityChangeThreshold && !document.potentialHidden) {
// page visibility change threshold raiched => raise the even
document.potentialHidden=true;
dispatchPageVisibilityChangeEvent();
}
}
var lastActionDate=null;
var hasFocusLocal=true;
var hasMouseOver=true;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
var timeoutHandler = null;
addEvent(document, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/doc:<br>";
});
addEvent(document, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/doc:<br>";
});
addEvent(window, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/win:<br>"; // raised when the page first shows
});
addEvent(window, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/win:<br>"; // not raised
});
addEvent(document, "mousemove", function(event) {
lastActionDate=new Date();
});
addEvent(document, "mouseover", function(event) {
hasMouseOver=true;
setAsNotHidden();
});
addEvent(document, "mouseout", function(event) {
hasMouseOver=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "blur", function(event) {
hasFocusLocal=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "focus", function(event) {
hasFocusLocal=true;
setAsNotHidden();
});
setAsNotHidden();
}
}
potentialPageVisibility.pageVisibilityChangeThreshold=4; // 4 seconds for testing
potentialPageVisibility.init();
</script>
Since there is currently no working cross-browser solution without false positive, you should better think twice about disabling periodical activity on your web site.
Using : Page Visibility API
document.addEventListener( 'visibilitychange' , function() {
if (document.hidden) {
console.log('bye');
} else {
console.log('well back');
}
}, false );
Can i use ? http://caniuse.com/#feat=pagevisibility
I started off using the community wiki answer, but realised that it wasn't detecting alt-tab events in Chrome. This is because it uses the first available event source, and in this case it's the page visibility API, which in Chrome seems to not track alt-tabbing.
I decided to modify the script a bit to keep track of all possible events for page focus changes. Here's a function you can drop in:
function onVisibilityChange(callback) {
var visible = true;
if (!callback) {
throw new Error('no callback given');
}
function focused() {
if (!visible) {
callback(visible = true);
}
}
function unfocused() {
if (visible) {
callback(visible = false);
}
}
// Standards:
if ('hidden' in document) {
visible = !document.hidden;
document.addEventListener('visibilitychange',
function() {(document.hidden ? unfocused : focused)()});
}
if ('mozHidden' in document) {
visible = !document.mozHidden;
document.addEventListener('mozvisibilitychange',
function() {(document.mozHidden ? unfocused : focused)()});
}
if ('webkitHidden' in document) {
visible = !document.webkitHidden;
document.addEventListener('webkitvisibilitychange',
function() {(document.webkitHidden ? unfocused : focused)()});
}
if ('msHidden' in document) {
visible = !document.msHidden;
document.addEventListener('msvisibilitychange',
function() {(document.msHidden ? unfocused : focused)()});
}
// IE 9 and lower:
if ('onfocusin' in document) {
document.onfocusin = focused;
document.onfocusout = unfocused;
}
// All others:
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
};
Use it like this:
onVisibilityChange(function(visible) {
console.log('the page is now', visible ? 'focused' : 'unfocused');
});
This version listens for all the different visibility events and fires a callback if any of them causes a change. The focused and unfocused handlers make sure that the callback isn't called multiple times if multiple APIs catch the same visibility change.
There is a neat library available on GitHub:
https://github.com/serkanyersen/ifvisible.js
Example:
// If page is visible right now
if( ifvisible.now() ){
// Display pop-up
openPopUp();
}
I've tested version 1.0.1 on all browsers I have and can confirm that it works with:
IE9, IE10
FF 26.0
Chrome 34.0
... and probably all newer versions.
Doesn't fully work with:
IE8 - always indicate that tab/window is currently active (.now() always returns true for me)
I create a Comet Chat for my app, and when I receive a message from another user I use:
if(new_message){
if(!document.hasFocus()){
audio.play();
document.title="Have new messages";
}
else{
audio.stop();
document.title="Application Name";
}
}
This is really tricky. There seems to be no solution given the following requirements.
The page includes iframes that you have no control over
You want to track visibility state change regardless of the change being triggered by a TAB change (ctrl+tab) or a window change (alt+tab)
This happens because:
The page Visibility API can reliably tell you of a tab change (even with iframes), but it can't tell you when the user changes windows.
Listening to window blur/focus events can detect alt+tabs and ctrl+tabs, as long as the iframe doesn't have focus.
Given these restrictions, it is possible to implement a solution that combines
- The page Visibility API
- window blur/focus
- document.activeElement
That is able to:
1) ctrl+tab when parent page has focus: YES
2) ctrl+tab when iframe has focus: YES
3) alt+tab when parent page has focus: YES
4) alt+tab when iframe has focus: NO <-- bummer
When the iframe has focus, your blur/focus events don't get invoked at all, and the page Visibility API won't trigger on alt+tab.
I built upon #AndyE's solution and implemented this (almost good) solution here:
https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html
(sorry, I had some trouble with JSFiddle).
This is also available on Github: https://github.com/qmagico/estante-components
This works on chrome/chromium.
It kind works on firefox, except that it doesn't load the iframe contents (any idea why?)
Anyway, to resolve the last problem (4), the only way you can do that is to listen for blur/focus events on the iframe.
If you have some control over the iframes, you can use the postMessage API to do that.
https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html
I still haven't tested this with enough browsers.
If you can find more info about where this doesn't work, please let me know in the comments below.
var visibilityChange = (function (window) {
var inView = false;
return function (fn) {
window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
if ({focus:1, pageshow:1}[e.type]) {
if (inView) return;
fn("visible");
inView = true;
} else if (inView) {
fn("hidden");
inView = false;
}
};
};
}(this));
visibilityChange(function (state) {
console.log(state);
});
http://jsfiddle.net/ARTsinn/JTxQY/
this works for me on chrome 67, firefox 67,
if(!document.hasFocus()) {
// do stuff
}
this worked for me
document.addEventListener("visibilitychange", function() {
document.title = document.hidden ? "I'm away" : "I'm here";
});
demo: https://iamsahilralkar.github.io/document-hidden-demo/
This works in all modern browsers:
when changing tabs
when changing windows(Alt+Tab)
when maximizing another program from the taskbar
var eventName;
var visible = true;
var propName = "hidden";
if (propName in document) eventName = "visibilitychange";
else if ((propName = "msHidden") in document) eventName = "msvisibilitychange";
else if ((propName = "mozHidden") in document) eventName = "mozvisibilitychange";
else if ((propName = "webkitHidden") in document) eventName = "webkitvisibilitychange";
if (eventName) document.addEventListener(eventName, handleChange);
if ("onfocusin" in document) document.onfocusin = document.onfocusout = handleChange; //IE 9
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = handleChange;// Changing tab with alt+tab
// Initialize state if Page Visibility API is supported
if (document[propName] !== undefined) handleChange({ type: document[propName] ? "blur" : "focus" });
function handleChange(evt) {
evt = evt || window.event;
if (visible && (["blur", "focusout", "pagehide"].includes(evt.type) || (this && this[propName]))){
visible = false;
console.log("Out...")
}
else if (!visible && (["focus", "focusin", "pageshow"].includes(evt.type) || (this && !this[propName]))){
visible = true;
console.log("In...")
}
}
In HTML 5 you could also use:
onpageshow: Script to be run when the window becomes visible
onpagehide: Script to be run when the window is hidden
See:
https://developer.mozilla.org/en-US/docs/Web/Events/pageshow
https://developer.mozilla.org/en-US/docs/Web/Events/pagehide
u can use :
(function () {
var requiredResolution = 10; // ms
var checkInterval = 1000; // ms
var tolerance = 20; // percent
var counter = 0;
var expected = checkInterval / requiredResolution;
//console.log('expected:', expected);
window.setInterval(function () {
counter++;
}, requiredResolution);
window.setInterval(function () {
var deviation = 100 * Math.abs(1 - counter / expected);
// console.log('is:', counter, '(off by', deviation , '%)');
if (deviation > tolerance) {
console.warn('Timer resolution not sufficient!');
}
counter = 0;
}, checkInterval);
})();
A slightly more complicated way would be to use setInterval() to check mouse position and compare to last check. If the mouse hasn't moved in a set amount of time, the user is probably idle.
This has the added advantage of telling if the user is idle, instead of just checking if the window is not active.
As many people have pointed out, this is not always a good way to check whether the user or browser window is idle, as the user might not even be using the mouse or is watching a video, or similar. I am just suggesting one possible way to check for idle-ness.
This is an adaptation of the answer from Andy E.
This will do a task e.g. refresh the page every 30 seconds,
but only if the page is visible and focused.
If visibility can't be detected, then only focus will be used.
If the user focuses the page, then it will update immediately
The page won't update again until 30 seconds after any ajax call
var windowFocused = true;
var timeOut2 = null;
$(function(){
$.ajaxSetup ({
cache: false
});
$("#content").ajaxComplete(function(event,request, settings){
set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
});
// check visibility and focus of window, so as not to keep updating unnecessarily
(function() {
var hidden, change, vis = {
hidden: "visibilitychange",
mozHidden: "mozvisibilitychange",
webkitHidden: "webkitvisibilitychange",
msHidden: "msvisibilitychange",
oHidden: "ovisibilitychange" /* not currently supported */
};
for (hidden in vis) {
if (vis.hasOwnProperty(hidden) && hidden in document) {
change = vis[hidden];
break;
}
}
document.body.className="visible";
if (change){ // this will check the tab visibility instead of window focus
document.addEventListener(change, onchange,false);
}
if(navigator.appName == "Microsoft Internet Explorer")
window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
else
window.onfocus = window.onblur = onchangeFocus;
function onchangeFocus(evt){
evt = evt || window.event;
if (evt.type == "focus" || evt.type == "focusin"){
windowFocused=true;
}
else if (evt.type == "blur" || evt.type == "focusout"){
windowFocused=false;
}
if (evt.type == "focus"){
update_page(); // only update using window.onfocus, because document.onfocusin can trigger on every click
}
}
function onchange () {
document.body.className = this[hidden] ? "hidden" : "visible";
update_page();
}
function update_page(){
if(windowFocused&&(document.body.className=="visible")){
set_refresh_page(1000);
}
}
})();
set_refresh_page();
})
function get_date_time_string(){
var d = new Date();
var dT = [];
dT.push(d.getDate());
dT.push(d.getMonth())
dT.push(d.getFullYear());
dT.push(d.getHours());
dT.push(d.getMinutes());
dT.push(d.getSeconds());
dT.push(d.getMilliseconds());
return dT.join('_');
}
function do_refresh_page(){
// do tasks here
// e.g. some ajax call to update part of the page.
// (date time parameter will probably force the server not to cache)
// $.ajax({
// type: "POST",
// url: "someUrl.php",
// data: "t=" + get_date_time_string()+"&task=update",
// success: function(html){
// $('#content').html(html);
// }
// });
}
function set_refresh_page(interval){
interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
if(timeOut2 != null) clearTimeout(timeOut2);
timeOut2 = setTimeout(function(){
if((document.body.className=="visible")&&windowFocused){
do_refresh_page();
}
set_refresh_page();
}, interval);
}
For a solution without jQuery check out Visibility.js which provides information about three page states
visible ... page is visible
hidden ... page is not visible
prerender ... page is being prerendered by the browser
and also convenience-wrappers for setInterval
/* Perform action every second if visible */
Visibility.every(1000, function () {
action();
});
/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
action();
});
A fallback for older browsers (IE < 10; iOS < 7) is also available
The Chromium team is currently developing the Idle Detection API. It is available as an origin trial since Chrome 88, which is already the 2nd origin trial for this feature. An earlier origin trial went from Chrome 84 through Chrome 86.
It can also be enabled via a flag:
Enabling via chrome://flags
To experiment with the Idle Detection API locally, without an
origin trial token, enable the
#enable-experimental-web-platform-features flag in
chrome://flags.
A demo can be found here:
https://idle-detection.glitch.me/
It has to be noted though that this API is permission-based (as it should be, otherwise this could be misused to monitor a user's behaviour!).
For angular.js, here is a directive (based on the accepted answer) that will allow your controller to react to a change in visibility:
myApp.directive('reactOnWindowFocus', function($parse) {
return {
restrict: "A",
link: function(scope, element, attrs) {
var hidden = "hidden";
var currentlyVisible = true;
var functionOrExpression = $parse(attrs.reactOnWindowFocus);
// Standards:
if (hidden in document)
document.addEventListener("visibilitychange", onchange);
else if ((hidden = "mozHidden") in document)
document.addEventListener("mozvisibilitychange", onchange);
else if ((hidden = "webkitHidden") in document)
document.addEventListener("webkitvisibilitychange", onchange);
else if ((hidden = "msHidden") in document)
document.addEventListener("msvisibilitychange", onchange);
else if ("onfocusin" in document) {
// IE 9 and lower:
document.onfocusin = onshow;
document.onfocusout = onhide;
} else {
// All others:
window.onpageshow = window.onfocus = onshow;
window.onpagehide = window.onblur = onhide;
}
function onchange (evt) {
//occurs both on leaving and on returning
currentlyVisible = !currentlyVisible;
doSomethingIfAppropriate();
}
function onshow(evt) {
//for older browsers
currentlyVisible = true;
doSomethingIfAppropriate();
}
function onhide(evt) {
//for older browsers
currentlyVisible = false;
doSomethingIfAppropriate();
}
function doSomethingIfAppropriate() {
if (currentlyVisible) {
//trigger angular digest cycle in this scope
scope.$apply(function() {
functionOrExpression(scope);
});
}
}
}
};
});
You can use it like this example: <div react-on-window-focus="refresh()">, where refresh() is a scope function in the scope of whatever Controller is in scope.
Simple/immediate check:
if(document.hidden) {
// do something
}
Visibility change event:
document.addEventListener("visibilitychange", function() {
console.log(document.visibilityState); // "hidden" or "visible"
}, false);
Promise-based event:
// An `await`able function that resolves when page visibility changes:
function visibilityChange(state="") {
return new Promise(resolve => {
document.addEventListener("visibilitychange", function() {
if(!state || document.visibilityState === state) {
resolve(document.visibilityState);
document.removeEventListener("visibilitychange", arguments.callee);
}
});
});
}
// Use it like this:
await visibilityChange();
console.log(document.visibilityState);
// Or wait for page to become...
await visibilityChange("visible");
await visibilityChange("hidden");
(Note: I was the one who added the latter two solutions into this answer, since that question is now closed and I couldn't add my own answer. Just in case someone thinks I've copied them from that post without crediting.)
If you want to act on whole browser blur:
As I commented, if browser lose focus none of the suggested events fire. My idea is to count up in a loop and reset the counter if an event fire. If the counter reach a limit I do a location.href to an other page. This also fire if you work on dev-tools.
var iput=document.getElementById("hiddenInput");
,count=1
;
function check(){
count++;
if(count%2===0){
iput.focus();
}
else{
iput.blur();
}
iput.value=count;
if(count>3){
location.href="http://Nirwana.com";
}
setTimeout(function(){check()},1000);
}
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();
This is a draft successful tested on FF.
I reread the #daniel-buckmaster version
I didn't make the multiple attempt, however, the code seems more elegant to me...
// on-visibility-change.js v1.0.1, based on https://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#38710376
function onVisibilityChange(callback) {
let d = document;
let visible = true;
let prefix;
if ('hidden' in d) {
prefix = 'h';
} else if ('webkitHidden' in d) {
prefix = 'webkitH';
} else if ('mozHidden' in d) {
prefix = 'mozH';
} else if ('msHidden' in d) {
prefix = 'msH';
} else if ('onfocusin' in d) { // ie 9 and lower
d.onfocusin = focused;
d.onfocusout = unfocused;
} else { // others
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
};
if (prefix) {
visible = !d[prefix + 'idden'];
d.addEventListener(prefix.substring(0, prefix.length - 1) + 'visibilitychange', function() {
(d[prefix + 'idden'] ? unfocused : focused)();
});
};
function focused() {
if (!visible) {
callback(visible = true);
};
};
function unfocused() {
if (visible) {
callback(visible = false);
};
};
};
Here is a solid, modern solution. (Short a sweet πŸ‘ŒπŸ½)
document.addEventListener("visibilitychange", () => {
console.log( document.hasFocus() )
})
This will setup a listener to trigger when any visibility event is fired which could be a focus or blur.
my code
let browser_active = ((typeof document.hasFocus != 'undefined' ? document.hasFocus() : 1) ? 1 : 0);
if (!browser_active) {
// active
}

Object doesn't support property or method 'addEventListener'

I am receiving an error on IE 8-7 saying that Object doesn't support property or method 'addEventListener' when I used the code below. Does anyone know how I can make the code below compatible with IE8-7? thanks
document.getElementById('clear').addEventListener('click', function () {
["A1","A2","A3","A4","A5","A6", "A1_flip",
].forEach(function(id) {
document.getElementById(id).checked = false;
});
return false;
})
That's because they don't support addEventListener. See this question's answers for details.
But since you've said you're using jQuery, you can...use jQuery to get around this issue:
$('#clear').on('click', function () {
["A1","A2","A3","A4","A5","A6", "A1_flip"
].forEach(function(id) {
$("#" + id).prop("checked", false);
});
return false;
});
or actually, we can be a bit more direct:
$('#clear').on('click', function () {
$("#" + ["A1","A2","A3","A4","A5","A6", "A1_flip"].join(", #")).prop("checked", false);
return false;
});
...which works by building a selector string from the array.
I just realized I'm assuming the array's contents vary. If they don't, if you literally just want those specific elements:
$('#clear').on('click', function () {
$("#A1, #A2, #A3, #A4, #A5, #A6, #A1_flip").prop("checked", false);
return false;
});
...or better yet, give them a common class and use
$('#clear').on('click', function () {
$(".the-class").prop("checked", false);
return false;
});
If you don't use jQuery and just mis-tagged it, see the linked question above. One of the answers there is mine, providing a hookEvent function that deals with cross-browser event handling:
var hookEvent = (function() {
var div;
// The function we use on standard-compliant browsers
function standardHookEvent(element, eventName, handler) {
element.addEventListener(eventName, handler, false);
return element;
}
// The function we use on browsers with the previous Microsoft-specific mechanism
function oldIEHookEvent(element, eventName, handler) {
element.attachEvent("on" + eventName, function(e) {
e = e || window.event;
e.preventDefault = oldIEPreventDefault;
e.stopPropagation = oldIEStopPropagation;
handler.call(element, e);
});
return element;
}
// Polyfill for preventDefault on old IE
function oldIEPreventDefault() {
this.returnValue = false;
}
// Polyfill for stopPropagation on old IE
function oldIEStopPropagation() {
this.cancelBubble = true;
}
// Return the appropriate function; we don't rely on document.body
// here just in case someone wants to use this within the head
div = document.createElement('div');
if (div.addEventListener) {
div = undefined;
return standardHookEvent;
}
if (div.attachEvent) {
div = undefined;
return oldIEHookEvent;
}
throw "Neither modern event mechanism (addEventListener nor attachEvent) is supported by this browser.";
})();
Then:
hookEvent(document.getElementById('clear'), 'click', function(e) {
["A1","A2","A3","A4","A5","A6", "A1_flip"
].forEach(function(id) {
document.getElementById(id).prop("checked", false);
});
e.preventDefault();
});
Support for addEventListener() function isn't available in older versions of Internet Explorer (i.e. 7-8), so you won't be able to use it on the browsers you are attempting to target.
You could consider wiring this up using jQuery's on() function, assuming you are using a version of jQuery that is designed to target older browsers as it will generally have the necessary fallbacks to support it.
$('#clear').on('click', function () {
var elements = ["A1","A2","A3","A4","A5","A6", "A1_flip"];
elements.forEach(function(id) {
$("#" + id).prop("checked", false);
});
return false;
})
If you want a better compatibility to obsolete browsers plus not really all new features of jQuery are needed. You might consider just switch to jQuery 1.1X(currently 1.12). It will save you lots of time from handling compatible issues.
ref: jQuery - Browser support

Browser version specification for Outdated browser tool

I am using the following toll to check for browser version accessing my website and display a message at the top to notify the user to update his browser:
https://github.com/mikemaccana/outdated-browser
In the following example I am targeting < IE10 browsers.
<!-- plugin call -->
<script>
//event listener form DOM ready
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
//call function after DOM ready
addLoadEvent(function(){
outdatedBrowser({
bgColor: '#f25648',
color: '#ffffff',
lowerThan: 'IE10',
languagePath: ''
})
});
</script>
Can I use the tool to detect a specific version of Firefox also in the lowerThan: field. If yes how should I proceed?
I would recommend detecting individual features. Try initializing whatever you need and if it fails then display the message. You can test all your features in a startup function, or try initializing plugins and see if it fails.
for example:
function supportsWebAudio() {
var hasWebKitAudio = 'webkitAudioContext' in window;
var hasAudioContext = 'AudioContext' in window;
if (!(hasWebKitAudio || hasAudioContext)) {
var audioElement = document.createElement('audio');
return audioElement.canPlayType;
}
return true;
}

Javascript / jQuery: delete iframe after contentWindow.print

I'm using this code, which has stemmed from here and here.
$('#my_button').on('click', function (e) {
var iframe = document.createElement('iframe');
iframe.id = "my_iframe";
iframe.onload = function() {
var doc = iframe.contentDocument || iframe.contentWindow.document;
doc.getElementsByTagName('body')[0].innerHTML = "<p>test123</p>";
iframe.contentWindow.focus();
iframe.contentWindow.print();
$("#my_iframe", top.document).remove();
};
document.getElementsByTagName('body')[0].appendChild(iframe);
});
Without the remove line, it prints fine. However, the remove line removes the iframe before it has a chance to execute the print(). How can I set up some kind of callback so that it prints and only then removes the iframe?
Thanks.
I found this solution when I was searching for print event detection:
http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/
Here I add the JS code as requested by Stano, but it is important to also read the whole linked post as there are limitations to this approach. In general the post is about the onafterprint event that only works in IE and a solution to make that event work in other browsers.
(function() {
var beforePrint = function() {
console.log('Functionality to run before printing.');
};
var afterPrint = function() {
console.log('Functionality to run after printing');
};
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener(function(mql) {
if (mql.matches) {
beforePrint();
} else {
afterPrint();
}
});
}
window.onbeforeprint = beforePrint;
window.onafterprint = afterPrint;
}());
create a function like this:
function printIframe(iframe,callback) {
iframe.contentWindow.print();
if (callback && typeof(callback) === "function") {
// execute the callback, passing parameters as necessary
callback();
}
}
and call it instead of the other two functions like this.
printIframe(iframe,function(){ $("#my_iframe", top.document).remove();})
if you like you can also put in a delay using the setTimeout.
setTimeout(function() {alert('hello');},1250);

addLoadEvent is not helping with onload conflict

I'm using the popular addLoadEvent as follows for all my JS loading:
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent( locationToggle );
addLoadEvent( step1 );
addLoadEvent( step2 );
addLoadEvent( step3 );
addLoadEvent( getCounties );
addLoadEvent( mapSelection);
Everything I've read suggests this is a fairly bullet proof way of avoiding onload conflicts. And yet this method doesn't appear to working any better than wrapping the functions in an anonymous window.onload function. Both methods are causing identical onload conflicts with this set of functions.
I am loading these functions from within the same file as the addLoadEvent function itself. I'm also using calender.js which is a third party file which uses mootools 1.2.4 in an additional file. My files are otherwise free of Javascript.
First, could someone verify I've not damaged the code and I'm using it right. Second could someone suggest why the above is not resolving the conflicts?
edit
The problem persists with all other Javascript files disabled.
Your code is fine. The problem is that setting event handlers in the DOM 0 way doesn't ensure that they won't replaced by other code.
You may try the new W3C standard addEventListener and the IE version attachEvent, because the handlers you attach by them cannot be replaced by 3rd party code.
// window.onload W3C cross-browser with a fallback
function addLoadEvent(func) {
if (window.addEventListener)
window.addEventListener("load", func, false);
else if (window.attachEvent)
window.attachEvent("onload", func);
else { // fallback
var old = window.onload;
window.onload = function() {
if (old) old();
func();
};
}
}
Note, that IE will execute the function in reversed order not in the order you added them (if this is a concern).
Finally, I don't know when you want to run your code, but if you don't want to wait for images to load you can execute your functions earlier then window.onload.
Dean Edwards has a nice script which will let you to do that.
With this you can attach your functions for an earlier event: document.ready (DOMContentLoaded)
// document.ready
function addLoadEvent(func) {
if (typeof func == "function") {
addLoadEvent.queue.push(func);
}
}
addLoadEvent.queue = [];
//////////////////////////////////////////////////////////////////////////////
// Dean Edwards/Matthias Miller/John Resig
function init() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// kill the timer
if (_timer) clearInterval(_timer);
// do stuff: execute the queue
var que = addLoadEvent.queue;
var len = que.length;
for(var i = 0; i < len; i++) {
if (typeof que[i] == "function") {
que[i]();
}
}
};
/* for Mozilla/Opera9 */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
/* for Internet Explorer */
/*#cc_on #*/
/*#if (#_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)>"
+"<\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
init(); // call the onload handler
}
};
/*#end #*/
/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
init(); // call the onload handler
}
}, 10);
}
/* for other browsers */
window.onload = init;
Note: the usage is the same for both methods as it was for your version.
​

Categories

Resources