Firefox is firing onchange events in my webapp after tab restore.
When reloading the same URL in Firefox there is no problem, no onchange events get fired on page load, all changed values since last visit are displayed correctly.
But when reopening the same page with the same URL, after closing Firefox and reopening the page with "restored tabs" (from the Firefox option "show my windows and tabs from last time") then it is firing onchange events for all values that have been changed since last visit.
Actual workflow ot reproduce the problem:
My update events are in background (JavaScript/AJAX) and are fired
with onchange events;
Firefox setting "show my windows and tabs from last time" enabled;
Change some values in my page (select fields);
Close Firefox;
Open the same URL on another browser or computer, and change some values;
Reopen Firefox, select the tab with the page on it, it reloads and fires onchange events again for all changed values since last visit.
Tried to reproduce this behaviour with completely different pages (not created by me and using other script libraries and stuff) and the result is the same, it is always firing the onchange events.
Chrome is not doing this with the "restore tabs" option.
Why is it firing onchange events? How can I prevent it?
A few suggestions on how to deal with this depending on the wanted result. Note that this is tested on my machine, behaviors may vary.
They way it seems to work is that Firefox tries to restore data that was entered by user. So it modifies the page, triggering the change event. This event is slighlty different than the one triggered by the user. It is a UIEvent while the user triggered one is a straight Event. And this Event is cancelable and triggered before the window load event. So this gives a couple of ways to deal with this. I'll take a select element for example.
If you want the select to keep value entered before window closing, but not trigger the onchange event, you can set the onchange call on the window.onload. Like this:
window.onload = function(){
element.onchange = function(){
Since the setting of the select occurs before onload, this specific change won't trigger your onchange function.
Other way would be to target behaviors you don't to trigger by putting a condition validating if the element is cancelable or not. If it's cancelable, it means it's called from a restore session and won't trigger what's inside. Like this:
element.onchange = function(e){
if(e.cancelable == true){
Other way, to clear out all data would be to set a document.onchange event and reload the page if the event is cancelable. Like this:
document.onchange = function(e){
if(e.cancelable == true){
window.location = window.location
}
}
Of course you need to make sure you don't have any other cancelable change event called in your page.
EDIT:
To clarify order of events fired, see this jsfiddle, not in iframes, iframes seems to behave differently, so if you have iframes, it may be a bit more complicated. But without iframe, you'll see how the different events are triggered depending on your interactions:
document.onchange = function (e) {
//this will be triggered on restore before the window load event
alert('restore onchange called, e.cancelable = ' + e.cancelable)
}
window.onload = function (e) {
//You'll see that on restore, this will be triggered but after page has been updated
alert('window load called')
document.onchange = function () {
//This onchange will be called normally, not on restore
alert('after onload change, e.cancelable = ' + e.cancelable)
}
}
http://fiddle.jshell.net/nozp9uhk/6/show/
Firefox is caching your files when you load the page, so when you restore the tab, the differences between your cached values and the new ones may be firing onchange events.
Try to clear the cache when restoring the tab. I see two ways to do this :
Call window.location.reload(true) to reload the current page
or
Change the name of the JavaScript file that initializes the onchange events for this :
<script language="JavaScript" type="text/javascript" src="yourscript.js?n=1"></script>
This (?n=1) will force Firefox to load a new copy of the file "yourscript.js"
Related
I've tried a few methods to see if I can create a cross browser solution for delaying a popstate event but have not had any luck.
Anyone have any ideas or thoughts?
Below obviously does not work, but something to the effect of:
$(window).on('popstate', function(e) {
// something here to delay the history pageload
console.log('a wild console has appeared!');
});
So the flow would follow this sequence:
Browser "back" or "forward" button clicked
run initial code
A delay before the page changes
page change
According to the Documentation the popstate event is
only triggered by doing a browser action such as a click on the back button
So I do not believe it will get triggered when user clicks 'forward' (and it varies in some browsers)
For reference, here's the full text:
The popstate event is fired when the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event's state property contains a copy of the history entry's state object.
Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by doing a browser action such as a click on the back button (or calling history.back() in JavaScript).
Browsers tend to handle the popstate event differently on page load. Chrome (prior to v34) and Safari always emit a popstate event on page load, but Firefox doesn't.
UPDATED ANSWER ABOVE
Your code works. (see below) - I added an element to trigger the event (you can see the results in the console)
Make sure you include the jQuery library on your HTML prior to using it though.
$(window).on('popstate', function(e) {
console.log('fired instantly!');
var timer = setTimeout(function() {
console.log('delayed popstate!');
clearTimeout(timer);
}, 1000);
});
$(window).trigger( 'popstate') ;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I got some controls which are visible on each site. To move those controls to the page currently active, I am using the pagebeforechange event.
$(document).bind('pagebeforechange',function(e,data) {
var topageid=$(data.toPage).attr('id'); //MY QUESTION IS ON THIS LINE
console.log("Changing page to "+topageid);
$header = $('#myheader');
$footer = $('#myfooter');
$header.detach();
$footer.detach();
$header.prependTo(topageid);
$footer.appendTo(topageid);
});
The source code shown above works sometimes, sometimes not. That's because from time to time data.toPage contains only an url like ...index.html#mypage.
How to detect the id of the toPage in any case?
You could use the pagebeforeshow event rather than the pagebeforechange event. The difference is that the former fires after external pages have been added to the DOM and the latter fires before that occurs.
There are other page-events that could work for you: http://jquerymobile.com/demos/1.2.0/docs/api/events.html
From the documentation (linked-to above):
pagebeforechange
This event is triggered prior to any page loading or
transition.
.
pagebeforeshow
Triggered on the "toPage" we are transitioning to,
before the actual transition animation is kicked off.
The pagebeforechange event is fired twice, the first time it is fired before the destination page is loaded, that's when you get data.toPage containing an URL.
The second time the event is fired the destination page is loaded, in that case you will get a query object containing the page DOM in data.toPage.
If you want to stick to the pagebeforechange event then you can do something like :
if (typeof data.toPage != "string") {
... Do your stuff ...
}
If you are flexible about the event you want to use then follow Jasper advice and use pagebeforeshow which will allow you to modify your detination page DOM before the page is transitioned to.
i have that problem: i need to have a variable set to false/true depending on whether the page is loaded in the current tab or in an inactive tab. so i tried to do it with the focus-event, more or less like this (it's jquery):
var hasFocus = false;
$(function() {
$(window).focus(function() {
hasFocus = true;
});
});
firefox and ie it do what i want: if the page is loaded in the active tab the event is triggered immediately, loaded in a background tab the event is only triggered when the tab gets active.
in chrome however the event does not get triggered when the page is loaded in the current active tab. does anybody know a workaround for this? i also tried events like mouseenter, hover but unfortunately they get executed on pageload in an inactive tab too... thanks in advance!
A tricky way would be this.
setInterval/setTimeout is only fired once a second at most for inactive tabs in Chrome. So, you could set an interval (or timeout) to be run after e.g. 10ms. If it only runs after a much longer time (e.g. 1 second), the page must be inactive. Otherwise, it would be run in 10ms (like you set).
I woulds suggest that you try mousemove as an event -- e.g.
var humanHasInteracted = false;
$(function() {
$(window).mousemove(function() {
humanHasInteracted = true;
});
});
alternatively use bind/unbind so that the event handler can removed when the first mousemovement is detected.
It seems that Google+ checks for notification updates when I activate the tab in Firefox
It'd show "0" every time I activate it, but change to a number of new notifications in a couple of seconds after that.
What's the mechanism allowing to tap into that event? Is there a specific DOM event for that? Or are they using something like onmouseover handler and just consider any kind of activity to be a sufficient indicator of tab activation?
Just a guess because I haven't all relevant browsers available for testing.
What about using the focus event on the window. Whenever a user clicks somewhere this is invoked but also on switching of tabs. To distinguish between a user's actions on the page and a user switching to the page you could check if the event's explicitOriginalTarget points to the window.
window.onfocus=function(event){
if(event.explicitOriginalTarget===window){
console.log('switched from tab');
}
}
There is Page visibility document, which describes document.onvisibilitychange event handler.
The usage
document.onvisibilitychange = function() {
console.log("Visibility of page has changed!");
};
Unfortunately there's no 100% accurate solution
onvisibilitychange correctly triggers on tab changes, but does not trigger on window changes (ALT+TAB) visibilitychange event is not triggered when switching program/window with ALT+TAB or clicking in taskbar
window.onfocus triggers when the document becomes focused. This works as expected if the tab's focus is already inside the web page, then it correctly triggers when window or tab becomes focused.
But if you have the focus on the URL bar, or in the console, you are already "out of focus", and when you get out of the window or tab and return, you will remain "out of focus", so this event won't trigger until you click inside the page, or navigate into it through TAB key
You can test below how each event triggers (click inside the white iframe to test onfocus/onblur events)
window.onfocus = () => console.log("focus");
window.onblur = () => console.log("out of focus");
document.onvisibilitychange = () => console.log("visibilityState: ", document.visibilityState);
Is it possible to determine whether a user is active on the current web page or, say, focused on a different tab or window?
It seems that if you switch tabs, any JavaScript set on a timeout/interval continues running. It would be nice to be able to 'pause' the events when the user is not on the page.
Would something like attaching a mouseover event to the body work, or would that be too resource-intensive?
You can place onfocus/onblur events on the window.
There's wide support for those events on the window.
Example: http://jsfiddle.net/xaTt4/
window.onfocus = function() {
// do something when this window object gets focus.
};
window.onblur = function() {
// do something when this window object loses focus.
};
Open Web Analytics (and perhaps some other tracking tools) has action tracking
You could keep an alive variable going using mousemove events (assuming the user does not leave the mouse still on the page). When this variable (a timestamp likely) has not been updated in x seconds, you could say the page is not active and pause any script.
As long as you do not do a lot of processing in the body event handler you should be okay. It should just update the variable, and then have a script poll it at a certain interval to do the processing/checks (say every 1000ms).
Attach listeners to mousemove, keyup and scroll to the document.
I use this throttle/debounce function (which works without jQuery, even though it's a jQuery plugin if jQuery is present) to only run code in response to them once in ~250ms, so that you're not firing some code on every pixel of the mouse moving.
You can also use the visibilityState of the document:
document.addEventListener("visibilitychange", function() {
if( document.visibilityState === 'visible' ) {
// Do your thing
}
});
There is a wide acceptance of this API.