There's no definitive way of detecting why/how a page is being unloaded. You could rebuild your site to use the now ever so popular "anchor navigation" method which stores data in the HTML anchor, such as http://www.example.com/page#something=something. This would at least typically solve the problem for the back/forward buttons but not when the user is reloading the page.
Other than that, you could employ various ad hoc ways of tracking the mouse and keyboard action before the user tries to unload the page. You could for example track when the user drags the mouse diagonally up to the right – that probably means he's just about to close the the window/tab, so keep the message. Diagonally up to the left – that probably means he's just about to click the back forward buttons or maybe enter something to the address field. If you're really serious, conduct a study of how people move the cursor and correlate that with whether they're about to close the page or do something "allowed". Then again, on a Mac the close button is in the upper left corner of the window. And so on and so forth. It'll still just be best guesses.
You could also track upward mouse movements and show a big red message in the browser viewport (not a popup/alert) to warn the user before he even considers leaving the page.
Tracking keyboard events is a little bit more deterministic, but still requires some cross browser and platform research. I leave you with this code, which I'm hoping may work as a boilerplate. It logs the key presses and suppresses the message if F5 or Apple+R (Mac) was pressed. Otherwise it will show a message containing a list of all logged key presses.
The analysis needs testing and extension; it's only been tested on Firefox Mac. One bug that I can immediately point out is that if you press Apple+R,R you'll still get prompted because the second page instance never recorded any keydown event for the Apple key – only for the R key. It will also fail if the user presses something inbetween, like Apple+L,R. You might be fine with just checking if the last key pressed was R.
<script>
// Create an empty array.
window.keys = [];
// Log every key press
window.onkeydown = function (e) {
var evt = window.event || e;
var keyCode = e.keyCode || e.which;
window.keys.push(keyCode)
}
function analyzeKeyPresses(){
keys.reverse(); // Reverse the array so it's easier to handle.
var doBlock = true;
// Here we only apply certain checks if there are enough keys in the array. Don't want a JS error...
switch(window.keys.length){
case 0:
doBlock = true; // Redundant. If there are no key presses logged, assume we should prompt the user.
break;
default: // Two or more key presses logged.
if(keys[0] == 82 && keys[1] == 224) doBlock = false; // User pressed apple+r on a Mac - don't prompt!
if(keys[0] == 82 && keys[1] == 17) doBlock = false; // User pressed ctrl+r on Windovs (untested) - don't prompt!
// Note: No break! Intentional fall-through! We still want to check for F5!
case 1: // One or more key presses logged.
if(keys[0] == 116) doBlock = false; // User pressed F5 - don't prompt!
}
keys.reverse(); // Un-reverse the array in case we need to use it again. (Easier to read...)
return doBlock;
}
window.onbeforeunload = function (e) {
var closewindowmessage=window.keys.join(" ");
var blockUnload = analyzeKeyPresses();
if(blockUnload){
e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = closewindowmessage;
}
// For Safari
return closewindowmessage;
}
};
</script>
<a href="#1">1</a> <a href="#2">2</a>