I want to put some logic before user leaves a page, but I want to handle reload button & close button action differently.
All I know is that there is onbeforeunload event that will be called before user unload the page, but it doesn't solve my use case.
I want to create some statistic/tracker, something like this:
$(window).beforeunload(function() {
if (user click browser's close button)
// track_user_click_close_button()
else if (user click browser's reload button)
// track_user_click_reload_button()
});
You can't know the URL user will be navigating to. This is not a jquery/js issue.
The bottomline is - you cannot do what you want.
Update
But if you try sometimes, you might find - you get what you need.
As stated in this SO answer to a similar question, there is something you can do to overcome this inability. I strongly suggest you to read the described approach.
If it's not suiting your needs, please open a new SO question with detailed explanation of what you're trying to do, and we'll try to help you.
Another update
Unfortunately, there's no way of determining this out-of-the-box. You can try the following approach:
$(window).beforeunload(function() {
// should be a synchronous AJAX-call, as async one won't work
persistLeaveOnServer();
});
$.ready(function() {
tellServerWeAreBack();
});
And on server side you should maintain the session state according to that. Once certain amount of time has passed (probably, several seconds), you consider the session ended and persist the statistics as UserClickClose. If tellServerWeAreBack request is received before this time passed, you save UserClickRefresh stat value.
beforeunload fires whenever the user leaves the page. If you want to differentiante between a refresh and a close you must be able to detect the page reload.
You could keep an information telling the page that it is unloading at xx:xx time. Log the fact to some place. When the page is loaded, check whether there is some logged evidence that the page was quit recently (depending on your definition of recently) and turn the event into a refresh event: (see here for an example)
This may not be able to differentiate between a refresh and a user closing then reopening the page quickly (under your time treshold).
Note: the linked page mentions using hidden form elements that are persisted between refresh, but says that browser support is all over the place; I'm wondering if this is still the case or not. Storing information in the window object will survive refreshes so you may want to explore this avenue
Related
A page presents a dialog with an OK button. Under certain circumstances clicking the OK button fires its click-handler twice (event.timeStamp is identical). Invoking event.stopPropagation() in the click handler did not make the problem go away.
I've 'fixed' the problem by comparing event.timeStamp with its previous value. That is not a very satisfactory fix. What else might do the job?
The Circumstances
This problem follows relatively minor changes to code that has been running, and evolving, over years. There is a lot of it, and it would be difficult to pare the thing down to a few lines for illustration. The changes involved adding a dialog to load tide data for a year other than the current year. FWIW here's a very brief summary of how the page works.
On opening, it sends a GET request for a year's worth of tidal current data. Subsequently, a method increments a time, draws a field of vectors appropriate for that time over a NOAA chart, and schedules itself to run after a few seconds. It iterates in that manner over a time-frame.
The user can define a course, run a vessel along it, and see how current affects its track. The course definition, which includes a launch time, can be saved (as a cookie) and restored. If, when attempting to restore a course, available current data don't bracket the launch time, a warning is given and the dialog closes without changing the state of the page.
The user can then open a second dialog to GET the appropriate tide data. They can then try again to restore the course. This dialog changes the tide data and parameters describing the time and time-frame.
The problem - two responses to a single click - occurs only when an attempt to load a course fails, a new dataset is loaded, and a second attempt to load a course is initiated.
On that second attempt, code handling a click on the OK button of the load-the-course dialog gets the same Click event twice. I conclude it is the "same event" because the timeStamp value is the same. Oddly, the
If the tide data are loaded first, and then the course retrieved, the problem does not arise.
I'm not optimistic about an answer; a detailed description would be impossibly long. Constructive suggestions welcome. (event.preventDefault() does not change the behavior. Did not expect it to, but Due Diligence and all that.)
I'm working with a web page that has a tabbed display that simply hides all but the selected tab using the display style property. When a new tab is selected, a function is called that gathers some data from the page and writes it to an indexedDB object store to be used to restore the previous state of the tab, such as the selected element and cursor position, none of which seems efficient to save at every individual change.
At the same time, the important data is saved right away, such as a change event on a textarea, which will write data to the same object store as above.
My question is what should take place when the user writes new text to one of the textarea elements and, while the textarea still has the focus, clicks a button to navigate away from the tab (not the browser tab but just the tabbed display within the web page)? This single user action triggers two database write transactions at the "same" time and on the same object store.
In testing, I have tried to place a large amount of data in the textarea, before navigating away from that tab, in attempt at causing the change write event to take more time to complete; but, the write transaction triggered by the change event always completes before the event capturing the last state of the tab.
A reason for this may be that, apart from the last scroll position, all the data is taken from the dataset object of a small number of HTML elements, and that step may take longer before the transaction is even opened.
However, I'd like to feel more confident that this is coded as close to "fail proof" as possible, and would like to know how to prevent these two transactions from ever blocking or interfering in any way causing the loss of data or program failure.
I don't really know what to provide for a code example because there is nothing to show but a textarea element with a change event and a set of radio buttons, also with a change event, that invokes a function to gather data and open a write transaction before hiding one tab and showing the other.
Everything works and has not failed once yet, but how can I code it to be certain?
Thank you.
Addition After agonizing over this for awhile, I think I misunderstood the concept of blocking in indexedDB. I just assumed it had to do with two write transactions attempting to write to the same object store at the same time, but after reading further I don't think that is the case. If I understand correctly, this MDN document states that transactions are processed in the order they appear in the code, and the example is of two write transactions.
Thus, for my question of a change event triggering a write transaction at the "same" time as another write transaction is triggered, it would appear the only concern would be that one transaction overwrites the data written by the other transaction. The browser will handle the rest and I don't have to worry about transactions interfering or blocking, apart from their order when important. Blocking appears to be version change concern on an open database.
Regarding my original question, the change event always completed first in testing because its transaction was always opened first since it fires, I assume, on a blur event on the textarea which takes place before the change event on the radio buttons which fires on mouseup triggering the second write transaction. I assume that the second write transaction opened on the same object store won't begin until the first completes; but there isn't that level of description in the referenced document apart from below.
After the code is executed the object store should contain the value "2", since trans2 should run after trans1.
The point being that even if the first write transaction takes considerably longer relative to the second, the second will not start until after the first completes, and the only concern is the second transaction potentially overwriting data written in the first.
Sometimes I get myself confused and make issues where there aren't any and the coders of the browser have already taken care of it.
If I am still misunderstanding, I'd appreciate being corrected. Thank you.
I am sure others have encountered this problem before, but this is doing my head in.
I have a page that generates a list of products. It's a long list so may go over several pages. I have Back and Next buttons to achieve this and they work if the user stays with the list.
The problem occurs when the user selects a product from the list and navigates away to a new page to display more details on the product. When the user has finished with this page and exits, the list of products is redisplayed at the point at which the user left it.
In chrome, the popstate is fired, and I have to adjust the number of go-backs required (due to pushStates I have already performed) on the next popstate in order to get the back button to then go to the correct previous page. In firefox, the page is being restored from the cache, and I am not being given the chance to intercept the return and make the necessary adjustments.
Is there some way to force Firefox to clear the cache and trigger the popstate when the back button or exit button is selected from the product detail page?
I've used this jQuery code before, and it works cross-platform:
$(window).bind('popstate', jQuery.proxy(function() {
// your code here
}, this));
The code inside the callback will run anytime the user presses the back button.
It's unlikely to be easy to force Firefox to clear its cache for security reasons. Here's one tip I found that might work (untested by me). But hopefully you can use this callback to do something else that's useful, like setting your navigation state correctly.
You might be able to find more useful information here.
I think you need a window.onunload handler. See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching which talks about the introduction of bfcache.
I've started to test History.js. After understanding how it works and that there is no popstate, instead there is statechange. I'm looking for a way to differ when the back button of the browser has been pressed.
The reason is that I need to know the URL before the state moved, from the one I'm going to. With the gist the project includes, only the URL we go to is looked.
I hope the solution is not to track latest URL visited in a global variable.
Thanks
I found the solutions on github to be a bit overdone for my purposes. I created a bool that is always true except right before when I used History to change the state.
var manualStateChange = true;
History.Adapter.bind(window,'statechange',function(){
if(manualStateChange == true){
// BACK BUTTON WAS PRESSED
}
manualStateChange = true;
});
Any time I change the state programmatically, set the bool to false:
manualStateChange = false;
History.pushState(null, null, currentPath);
I know this is a pretty dated question, but I came up to a solution to issues with the back/forward button when switching between a standard page and a History-state page.
Scenario:
Use HTML5 History (or history.js plugin) on a set of pages
Then other pages we need real loads, aka a standard page (don't ask why, but there are certain use cases that this may be needed)
When you go from a History-state page, and do a real load to a standard page. You cannot go BACK: it changes the url, but doesn't load the page--it only fires a statechange; which I assume is the issue of the original post. My personal opinion is that this is a browser bug (all browsers have the issue, though), because the browser should know the page you're on was reloaded outside of the History-state page.
I fixed this with something really simple:
Listening to the statechange event and tell the browser to refresh when it does fire. This way I don't care if they go back or forward out of this page. If the browser thinks the state is changing (links don't fire the statechange event), only back/forward INTO a History state page fires this event, so it solves the issue.
Code, using jQuery + History.js plugin:
$(window).on('statechange', function() {
window.location.reload();
});
If this doesn't make sense, you're probably not having this issue. However, I've noticed this on many sites that do use HTML 5 History (even pinterest.com has this issue if you reload when on an image modal and then try to go back).
Hopefully, if you do have this issue, you'll find this answer and have a huge sigh of relief :)
I finally managed all links using History.pushState, that way there is no need to differ a back button pressed. We handle all state changes the same way.
Edit: Found these two issues in History.js' Github, might help someone.
https://github.com/browserstate/history.js/issues/70
https://github.com/browserstate/history.js/issues/47
In the 'offical' version this feature is not available. Try to use this file in the main while:
https://github.com/danger89/history.js/blob/master/scripts/bundled/html5/jquery.history.js
I can't guarantee that his solution works for every browser. But it's a nice solution. If you applied his changes, you can use:
var State = History.getState();
if (State.navigation) {
// Back / forward button is pressed.
}
More information can be found on Github issue 47.
Similar answer than that of James Wagoner.
the 'statechange' event was being fired both ways, so instead I only check for a 'popstate' event, which in my case is only called when the user goes back.
window.addEventListener('popstate', function() {
window.location.reload();
});
Examining our web logs we find a significant number of clicks are other double-clicks, or repeat-clicks (e.g. when the system is busy and has not reacted quickly enough).
Double-Clicking a SUBMIT button may cause a form to process twice (generally we program against this, but I'd like to avoid possibility of errors that we have not programmed against), but even double clicking a link means that the server has to process the response twice (usually the server will detect a "disconnect" on the first click and abort processing for that - but we still incur the server-time for the effort, which is compounded when the server is under heavy load).
Having said that, there are times when I never get a response to a click, and its only the re-click that works.
One action we do see is a mis-click - click on a link, realise that it was not the desired link, and then click on the correct, adjacent, link - clearly we still need to allow that.
How do you handle this / what do you suggest? and what is the best way to achieve this, generically, across the whole application?
1) We could disable the link/button after click (perhaps for a set period of time, then re-enable)
2) We could hide the "body" of the page - we have done this in the past, just leaving the "banner" pane (which looks the same on all pages) which gives the appearance of the next page loading (but does not play well with the BACK button in some browsers) - this also mucks up users who mis-clicked
You could do this with a combination of delegate and data:
$(document).delegate('a, :button', 'click', function(e) {
var lastClicked = $.data(this, 'lastClicked'),
now = new Date().getTime();
if (lastClicked && (now - lastClicked < 1000)) {
e.preventDefault();
} else {
$.data(this, 'lastClicked', now);
}
});
This will prevent constant rebinding, so should have decent performance.
You can set custom attribute once the element is clicked then check for that attribute: if exists, ignore the click.
This will not change the UI of the element, just ignore repetative clicks.
Rough example using pure JavaScript (as you didn't tag your question with jQuery) is available here: http://jsfiddle.net/248g8/
If this is a big concern for you (and if the obvious answer of "make sure your server always responds really fast" isn't possible ;-) I would suggest a modified version of your (2) is the way forward.
The critical thing here is to give the user sufficient feedback that they feel that something is happening - ideally without blocking off the possibility of the user clicking again in those few cases where something genuinely has gone wrong.
Using javascript to make a small swirly "loading..." graphic may be effective here - and it's easy to set this up so that browsers that don't support javascript (or have it disabled) fall back to the standard link behaviour. Though I would only do this for forms where there is an expectation of taking a long time (or where this might scare the user) - it will make the site rather distracting to use, and in any case (a) users are used to links occasionally being slow on the internet, and (b) your server should be powerful enough to cope with the occasional extra hit :-)
You can disable the link or submit button - but this is frustrating for the user in the case where the submission fails for some reason (my bank does this, and TBH it scares me that they don't realise they should instead "program round" the double-submit issue as you described it!).
I certainly wouldn't disable the link and then re-enable it after a timeout - this would be very confusing for the user...
If you're using jQuery, then maybe you can listen for double clicks across the <BODY> tag and then prevent propagation.
$("body").live('dblClick',function()
{
return false;
});
I would say either:
Just leave it. As long as you've programmed against double-submissions on forms, who cares about a few extra processes?
Disable the link for a few seconds, as you've suggested. That was my first thought before I got to that part of your question. With jQuery (alter for your library of choice):
$('a').live('click',function()
{
var returnFalse = function () { return false; };
$(this).click(returnFalse);
window.setTimeout(function () { $(this).unbind('click',returnFalse) }, 3000);
}