Can I override onbeforeunload for a particular element? - javascript

I have a page which does quite a bit of work and I don't want the user to be able to navigate away from that page (close browser, hit back button, etc.) without getting a warning. I found that the onbeforeunload event (which I think is IE-specific, which works fine for me as the project uses lots of ActiveX) works great.
Problem is, I want the user to be able to click on a little "help" icon in the upper-right corner and pop up a help window at any time. This causes onbeforeunload to fire, even though the main window never goes anywhere and the page never unloads.
The JavaScript function that runs when the onbeforeunload event runs just puts text into event.returnValue. If I could ascertain, somehow, that the help icon is the one that was clicked then I could just not put text into event.returnValue in that situation. But how could I have the page figure that out?

Let me guess: the help "icon" is actually a link with a javascript: url? Change it to a real button, a real link, or at least put the functionality in an onclick event handler (that prevents the default behavior). Problem solved.
<!-- clicking this link will do nothing. No onbeforeunload handler triggered.
Nothing.
And you could put something in before the return false bit...
...and the onunload handler would still not get called... -->
blah1
<!-- this should also do nothing, but IE will trigger the onbeforeunload
handler -->
blah2

EDIT: My "workaround" below is complete overkill, based on my lack of understanding. Go with Shog9's answer above.
OK so while I was writing the question, I came up with a workaround which will work for now.
I put a global JavaScript variable in act as a boolean on whether or not the icon is being hovered over. Then, I attach events to the image's onmouseover and onmouseout events and write functions that will set this value. Finally, I just code in the function that handles onbeforeunload that will check this value before setting event.returnValue.
Probably not a flawless workaround but it will work for now.

on the internet you will find many people suggesting you use something like
window.onbeforeunload = null
but this does not work for me in IE6. reading up in the MSDN docs for the event object i found a reference to the event.cancelBubble property, which i thought was the solution. but thanks to Orso who pointed out that setting "event.cancelBubble=true" is useless, the way to get rid of the confirm prompt is to exclude the return statement altogether, i chose to use a boolean variable as a flag to decide whether to return something or not. in the example below i add the javascript code programattically in the code behind:
Page.ClientScript.RegisterStartupScript(typeof(String), "ConfirmClose", #" <script> window.onbeforeunload = confirmExit; function confirmExit() { if(postback == false) return ""Please don't leave this page without clicking the 'Save Changes' or 'Discard Changes' buttons.""; } </script>");
then the help button contains the following aspx markup:
OnClientClick="postback=true;return true;
this sets the 'postback' variable to true, which gets picked up in the confirmExit() function, having the effect of cancelling the event.
hope you find this useful. it is tested and works in IE6 and FF 1.5.0.2.

I have a method that is a bit clunky but it will work in most instances.
Create a "Holding" popup page containing a FRAMESET with one, 100% single FRAME and place the normal onUnload and onbeforeUnload event handlers in the HEAD.
<html>
<head>
<script language="Javascript" type="text/javascript">
window.onbeforeunload = exitCheck;
window.onunload = onCloseDoSomething;
function onCloseDoSomething()
{
alert("This is executed at unload");
}
function exitCheck(evt)
{
return "Any string here."}
</script>
</head>
<frameset rows="100%">
<FRAME name="main" src="http://www.yourDomain.com/yourActualPage.aspx">
</frameset>
<body>
</body>
</html>
Using this method you are free to use the actual page you want to see, post back and click hyperlinks without the outer frame onUnload or onbeforeUnload event being fired.
If the outer frame is refreshed or actually closed the events will fire.
Like i said, not full-proof but will get round the firing of the event on every click or postback.

Related

What is difference between onbeforeunload in body tag and beforeunload?

What is difference between
<body onbeforeunload="return myFunction()">
and
$(window).on('beforeunload', function () {
myFunction();
});
The onbeforeunload event occurs when the document is about to be unloaded.
This event allows you to display a message in a confirmation dialog box to inform the user whether he/she wants to stay or leave the current page shows in below code snippet.
The default message that appears in the confirmation box, is different in different browsers. However, the standard message is something like "Are you sure you want to leave this page?". This message cannot be removed.
<!DOCTYPE html>
<html>
<body onbeforeunload="return myFunction()">
<p>Reload this page, or click on the link below to invoke the onbeforeunload event.</p>
Click here to go to stackoverflow.com
<script>
function myFunction() {
return "Write something clever here...";
}
</script>
</body>
</html>
The beforeunload event is fired when the window, the document and its resources are about to be unloaded. The document is still visible and the event is still cancelable at this point.
window.addEventListener('beforeunload', (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = '';
});
There might be a difference in when the function is bound to the event depending on where your jQuery code is. If it's inline in the HEAD section than the jQuery code will run earlier binding the event sooner, otherwise under almost all circumstances the body attribute will bind the function to the event earlier.
Your code probably won't work in modern browsers. This hook is mostly used by less reputable often malicious sites that want to prevent the user from leaving their page and trick them into buying something. Adblockers often prevent the hook in the body tag from firing altogether.
You used to be able to return a string from the function to customize the message but this is now disabled in most browsers. All you can do is confirm that the user wants to leave the page. For future proofing I would put the following code in my head section:
<script>
window.addEventListener(function(event) {
event.preventDefault();
// ... stuff ...
return 'reason';
})
</script>
Event though there are legitimate, non sketchy reasons to use this event, I would avoid it unless it's absolutely necessary. There is no guarantee the event will fire and there is a chance that the user clicks the confirm button (or has a setting/extension to always confirm) before your de initialization code has a chance to run.

Postback not triggered after javascript on change event

In my asp.NET application I have implemented a control to validate forms input data using server side logic.
The idea is to drag the control to wherever it's needed, set it up in the code behind and the form will be validated.
This validation occurs on the onchange event of each field and on form submission - synchronous ajax call to the server side.
This was working fine until I published it to IIS.
The problem is the following: the user is writing something in a textbox. Then, with the focus on that textbox, clicks on a button or linkbutton, not related to the form being validated.
The validation ajax call occurs (jQuery onchange fires) and the button postback is not reached. Through debugging I found the problem to be the ajax call is somehow preventing the postback to fire (almost feels like a synchronism problem).
I reduced the problem to the point that an alert seems to be causing the same thing as the ajax call. Please have a look at the following project: EDIT: Link removed because I can't post more than 2 links on the same post - sorry!
Consider the first 2 textboxes and the button:
1) If you write something on the first, then click the button: the onchange fires, an alert is shown and the postback does not occurr.
2) If you write something on the second, then click the button: the onchange fires and the postback occurrs.
Can someone please explain why this behavior happens and if there's any solution to this, ie, make the postback fire after the javascript finishes running?
I can think of 2 ways to solve my problem but I need to know (inside the textbox change event) the ID of the control clicked by the user. Any way of getting it?
That way I could: trigger the control explicitly OR verifiy if it doesn't belong to the form controls and don't event validate the input in that moment (that would make sense).
Thanks in advance.
EDITED 22-10-2014:
The plot thickens. This seems to be a synchronism problem. Check this other test application where I removed the alerts (this concentrated too much attention and is not actually related to the issue as I'm not using alert boxes in my project - I'm using little balloons) and just left the AJAX call.
Now, on the server side (WebMethod) I put a Thread.Sleep(). If the thread sleeps for too long, it seems to miss the postback. In my case, on my development environment, the threshold seems to be 80ms. If the ajax call takes less than ~80ms, then the postback is done, if it takes more than that, it misses the postback. Any ideas or similar (resolved) issues you have seen? Note that my ajax call has async: false.
EDITED 24-10-2014:
Finally had another look into this. I think I may have come to a possible solution, although I don't like the idea of relying on a setTimeout to handle the submit button 'click' before the 'focusin' (same control).
I changed the logic (still achieving the same goal) and now use different events.
Now I need to distinguish when the submit control fires the 'focusin' event because it just gained focus (1) or it was clicked (2):
The user could just be tabbing (validates the last field that had focus - if it belongs to the form being validated)
The user could have clicked (does not validate the last field that had the focus, but the whole form and then submits or not)
Have a look at this new test app which is closer to what I have in my project.
Can you help me finding a better way to handle/process the click event before the focusin event on the same control without something unpredictable like a setTimeout? If I do need to rely on the setTimeout, how much do you think the wait should be set to? On my machine 150ms works, but on another persons, it may require more time? Something like some sort of callback would be ideal.
Thanks again
Use __doPostBack('',''); at end of your javascript function
It does appear that an alert box stops postback in this situation. The sensible solution I found was to use a jQuery dialog which doesn't seem to suppress a postback. Problem is of course the dialog doesn't persist itself through the postback but this is solved by a hidden field containing a 'flag' to display the dialog after postback or not.
You will need to add jquery-ui.js and some style for the dialog, if this is a serious application I suggest you download both files and put them in scripts folder as you already have with the jquery min.
<head runat="server">
<title>Postback with dialog</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#TextBox1").on('change', function(e) {
$("#doDisplayDialog").val("yes"); // Add a flag so it shows after postback.
$('#jqAlert').dialog({
closeOnEscape: true,
open: function(event, ui) {
$(this).parent().appendTo("form");
}
});
});
if ($("#doDisplayDialog").val() == "yes") {
$('#jqAlert').dialog({
closeOnEscape: true,
open: function(event, ui) {
$(this).parent().appendTo("form");
}
});
$("#doDisplayDialog").val("no"); // Clear the flag so it doesn't display after postback.
}
});
</script>
</head>
Add a hidden field:
<asp:HiddenField ID="doDisplayDialog" runat="server" />
And a Div to be the dialog box:
<div id="jqAlert" title="Alert" style="display: none;">
<p>Postback will happen!</p>
</div>
Code is based on your downloadable test website application.
-- not a working solution below --
Try this - copy/paste replace these lines in your test web application:
$(document).ready(function() {
$("#TextBox1").on('change', function() {
alert('T1 Postback does not fire');
return true;
//return AjaxCall();
});
$("#TextBox2").on('change', function() {
alert('T2 Postback does not fire');
return true;
//return AjaxCall();
});
});
The only change I did was replace single quotes with double quotes in jquery selector here
$('#TextBox2')
with
$("#TextBox2")
Edit: actually it doesn't work, I had a bug in my test code. Will look more into this.

Javascript conflict with onclick and onmouseout

Hi and thanks for looking at my post. I'm very new to scripting, and I'm having a simple problem that I can't figure out. So...
I have a button that freezes up when I press it, and its "onclick" function doesn't get triggered. There is a "onmouseout" function that is causing this, but I don't know why.
I'd like "onmouseout" and "onclick" functions to apply to one button, but it's not working. Please see my code:
Javascript:
function popup()
{
alert("Hello World")
}
function pop2()
{
alert("Good job")
}
function pop3()
{
alert("CLICK ME!")
}
HTML:
<input type="button" onclick="popup()" value="Hello World">
<input type="button" onclick="pop2()" onmouseout="pop3()" value="click me">
Adjusting your code to isolate and provide detail to what's going on, I've prepared the following:
http://jsfiddle.net/238Nz/8/
Skip below this part for the answer. This is intended for Mark.
Let's ignore whether or not using on* attributes to add handlers is considered good or bad$. Also, let's use some logging to check the order. I am using console.log() to send messages to the browser's Javascript console. In Firefox, I use Firebug, and Chrome has a built-in console. In both, right-click on the page and Inspect Element, then choose the Console tab for these tests.
One more thing: jsFiddle can be a little complicated to figure out at first. The Javascript in the bottom, left pane is actually put within the head block above the input (and body tag) within the source. Right-click and View Source on the bottom, right (Result) pane and you'll see what the browser is using. It's just like your own example HTML document, just reordered for jsFiddle. Try not to be confused by how that works, though.
The markup I am using to test your problem is the following:
<input type="button" onclick="doclick()" onmouseout="domouseout()" value="Click">
Notice I plainly label my functions and use descriptive error messages. This helps me keep track of what is going on by embedding that detail within the code and the message log information. Always try to be descriptive in your labels (function, var, log information, etc.), instead of doing things like yay! or what happened. It's simply too hard to follow once your scripts get to be longer and more complex.
Next, I used the following function to narrow down and consistently replicate the same action:
function popup(msg) {
// Note, console.log is first here. This is so that the
// alert does not "steal" focus before I get any result.
console.log(msg);
// Now, let's try the alert. Notice, the same msg is used.
alert(msg);
}
This allows me to call both console.log and alert in a specific order, from both event handlers. Next, I establish my two event handler functions:
function doclick() {
popup('onclick fired');
}
function domouseout() {
popup('onmouseout fired');
}
See, all they do is call popup() with an action-specific log message about what was supposed to happen. Since alerts are designed to "focus" to the user and block interaction, which appears to be a possible cause of the "frozen" browser frame.
What I do next is try this in different browsers. Always try to replicate and test across different browsers#. In this case, I notice a big difference between two...
Chrome: Does not block processing; both alerts fire, and the onmouseout-fired alert is not run through console.log until after I hit Ok on the alert and and I move the mouse pointer off of the input element. This, it seems, is the desired outcome. Right?
Firefox (17.0.1): Firefox does show the behavior you're describing. Note, when I click on the button, I get both doclick and domouseout() called at the same time. So Firefox is detecting the onclick as taking the mouse pointer away from the button, and you get the "freeze". If you watch the console, you'll see both logs fire immediately, and you seemingly get no alert to interact with (by clicking Ok).
IE (7-9, 9 Compatibility View): IE of course provides an interesting illustration. For instance, when I click the button in IE9, I see:
http://i.imgur.com/sWXWm.png
Which of course appears to be the same effect Firefox is having... But for some reason with Firefox, the onmouseout-fired alert does not focus on top of the onclick-fired alert. IE 7-9 plus Compatibility View all exhibit this particular behavior, with slight variations.
Opera (12.02): Opera does not fire the onmouseout-fired alert or console.log message until after the onclick-fired alert and log message, and you move the mouse (assuming you've moved it off of the input button element after clicking it). This seems weird, but more palatable than the Firefox and IE behaviors. Maybe I'm mistaken, though.
So what's happening? I'm not quite sure, but I think that the onmouseout is blocking the onclick's alert from focusing to the user. If you hit [Enter] while it's frozen, you get the onclick alert but no onmouseout. Chrome seems correct here; Firefox's "popunder" alert seems, well, sorta fishy.
In summary, at least the behavior of the two events in this case are not only specific to Firefox. What seems to be specific to Firefox (at least 17.0.1) is the fact the onmouseout-fired alert does not focus correctly, and the page "appears to freeze". This seems like a bug. I wonder if it's been reported?
$
It's not usually a good idea to use inline attribute event handlers like <input onclick="doclick()"...>, but let's ignore what's beside the point here. See MDN's DOM Event documentation, specifically the HTML attribute section, and realize this is a bit trickier and detailed than is worth going into here.
#
If you continue working with Javascript within the browser, you'll find out IE is... special. It has a special place in history, so weird or "abnormal" behavior is not unusual when checking your code with versions of IE. Personally, I suggest learning and working within Firefox or Chrome, and then checking in IE and other browsers that it works.
Javascript:
var clicked = false;
function popup()
{
alert("Hello World")
}
function pop2(ev)
{
alert("Good job");
clicked=true;
}
function pop3()
{
if(clicked == false)
{
alert("CLICK ME!");
}
clicked=false;
}
HTML:
<input type="button" onclick="popup()" value="Hello World">
<input type="button" onclick="return pop2(event)" onmouseout="pop3()" value="click me">
​link to demo
Since the click and mouseout/mouseleave events occur soon after the other on FireFox due to poor focusing during click event, we can add a sort of a delay for the code that will be processed during mouseout/mouseleave event. The following worked for me:
//Bind the element with classname to a function on click event
$(".rej").bind("click", showData);
//Bind the same element with classname to another function on mouseout event.
$(".rej").bind("mouseout", hideData);
Then you can proceed with your showData function. However in hideData function you will need to add a delay of few seconds before processing the code in that function, as show below:
function showData() {
//Do something
}
function hideData() {
var delay = 1000;
setTimeout(function(){
//Do something
}, delay);
}
And you're good to go. The mouseout event no longer overpowers the click event in Firefox. :) :)

stopping the right click and go to link function for a tag

I have a tag like below. The question I have is how do I make this <a/> tag not behave like a link when user right clicks it. Since on a regular click the onclick event will fire and return false I am good with the regular click on the link the issue comes when a user right clicks the mouse and then gets the option like open in new tab or open in new window I have to prevent this from happening. I found out I can use javascript:void(0) in the href to do that but for some reason I cannot change the href as it is used for some other stuff. Is there any even or something that I can use.
<A title="Test1" onclick="javascript:search1('search'); return false;"href="team">search</A>
Thanks
as often, there's no universal solution, every browser do it its way. HTML 5 says form.oncontextmenu event handler should be supported. So this
<script>
document.oncontextmenu=function("alert('dont play with sources');return false");
</script>
should work if you use HTML 5.
you can also remove the javascript word, onclick already waits for js code (as oncontextmenu does).
<a onclick="search1('....

Suppress Safari’s “You have entered text…” warning?

Safari helpfully (?) prompts before closing a tab or window when text has been entered into an input.
There are some cases where, as a web developer, this isn’t desirable — for example, when the input is a live search where the user has probably already gotten the results he’s looking for when the window is closed, even though there’s still text in the field.
How can I let Safari know that text in a particular input doesn’t need its protection?
It seems like you are able to disable this warning for an entire page by having an onbeforeunload handler on <body> (even an empty one will do). For example, the following will not produce the warning:
<body onbeforeunload="">
<form method="get"><input></form>
</body>
I'm not sure if this is the intended behaviour, or a bug.
I think I've got a solution to this problem, though it's unquestionably a hack (i.e. if Safari changes how this feature is implemented, it could stop working). Shown here with a touch of jQuery:
$('.unimportant').live('blur', function(){
var olddisplay = this.style.display;
this.style.display = 'none';
this.clientLeft; // layout
this.style.display = olddisplay;
});
Demo (try typing in the "unimportant" field, click somewhere else on the page, then close the window).
In short:
Hide the input
Trigger layout
Show the input
You can also change the value of the input, trigger layout, and change it back.
The one limitation here is that cleaning the input has to be done explicitly. In this case, it will be dirty until blur. This works well in my situation, and probably in many others, since the input will be protected from an accidental window close until something else on the page is manipulated (e.g. a link is clicked). You could choose to run this on keyup if you're willing to live with a flicker of the insertion point every time a key is pressed.
I'm open to cleaner solutions.
I found what I think is a pretty good solution to this problem. When I use AJAX to submit the form then I want the warning to suppress. This is accomplished with onbeforeunload.
window.onbeforeunload=function(e){}
But after I submit I might make additional changes and so I want the warning to show again. To do this I added a keyup handler to a form element.
$('txtarea').onkeyup=dirty;
What dirty does is checks is the input field has changed if it has then I set onbeforeunload to null.
function dirty(e){
if (e.srcElement.value != e.srcElement.defaultValue){
window.onbeforeunload=null;
}
}
I just found another solution to prevent Safari from displaying the "Are you sure you want to reload this page?" dialog when textareas have changed their content.
It turns out that setting the value through Javascript clears Safari's changed state:
$(document).on('blur', 'textarea', function() {
var value = $(this).val();
$(this).val('').val(value);
});
Clearing the value first is important, directly setting it to the content it already is does not work.
EDIT Apparently setting window.onbeforeunload to an empty function still works, however $(window).on('beforeunload', function() {}) does not.

Categories