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.
Related
I have jEditable working fine with a textarea to edit the content inside an object. I'm submitting onBlur using jEditable's onblur option directly to a function (instead of an url) and all works as expected UNLESS you change windows using for e.g. alt+tab. When I use alt+tab it looks like the content is submitted through an actual http request ignoring my callback function.
My very basic sample implementation looks like this:
$(".editable").editable(function(content,settings){
content = content.replace(/\n/gi,"<br />");
return content;
},{
data: function(value, settings) {
return value.replace(/<br[\s\/]?>/gi, '\n');
},
type: "textarea",
onblur: "submit"
});
You can test it here: http://jsfiddle.net/xmajox/wePp5/
I've tried all other window operations: resize, move, etc and even moving between tabs on the browser works great (submits the data and exits the edit mode).
Any ideas of how this might be tamed?
UPDATE:
After a few more tests and some colaboration from other people, it seems that this depends on the os (window manager?). It happens consistently on ubuntu 12.10 with unity but doesn't happen on mac or windows (haven't tested other linux boxes).
Also, it is now proved that my callback method does run when I use alt-tab but the form gets POST'd anyway afterwards. Adding a live preventDefault didn't seem to help.
Found the fix for this, but coulnd't quite understand why it happens on such a specific situation.
First of, I must be the (un)luckiest guy in the world because I've tried this on 10ths of other combinations and it didn't have this problem, only place I could get it to happen was:
Ubuntu 12.10 (with Unity) and Chrome (v22 at the time of this post)
Disclaimer: This doesn't mean it does not happen on other places, I just couldn't reproduce it on the ones I tested (or asked friends to test).
Problem:
When focus is lost, jEditable - as of version 1.7.1 - sets a timeout before actually executing any action (200ms by default) to prevent duplicate actions according to the comments in the source. If you (using the above browser/os combination) alt+tab out of the browser window before the timeout is fired (aka. before the data is submitted and your method is ran) it will force a POST request with the data, completely ignoring the preventDefaults or return falses. It won't have this problem if you press alt... keep it pressed (more than those 200ms, i.e. enough time for the timeout to be fired) and then press alt to move away.
Solution:
I edited jEditable's source to reduce the timeout (or even remove it). So I around line 280, where it says:
(...)
input.blur(function(e) {
// prevent double submit if submit was clicked
t = setTimeout(function() {
form.submit();
}, 200);
});
(...)
I changed it to:
(...)
input.blur(function(e) {
form.submit();
});
(...)
Notes:
I removed this because on my situation (submitting textareas ONLY on blur and without any other controls that allow the user to submit the data) I coulnd't reproduce the double submit issue that Mika Tuupola was mentioning on the comments. If you have text input fields (that are submitted pressing enter) with the submit onBlur active, you might fall into that situation. On those cases I suggest you just reduce the timeout or simply avoid to use onBlur submits.
Is there any way we can intercept the html form's onsubmit event?
In my web application, there are several screens containing forms etc. The issue we are facing is when the user presses any button multiple times, the server gets overloaded with same requests.
Some of the forms have event handlers already attached to them(like onSubmit, button.onClick etc).
One way can be to "inject" my button disable code by going through all the screens.
But what I am looking for is a generic solution which can be applied to all the screens by just including the script where the function is written.
I know I can setup callback using jQuery (capturing onSubmit for form), but in the issue in this case is if any screen has a onSubmit registered already, it may not get called.
Any help in this regard appreciated!
I think this piece of code is a good place to start. It should be placed in separate file and included where you want to use it (if you appear to have global list of scripts - its a good place for it)
var suppressed_items = [];
function allowOnlyOne(item,e){
if (jQuery.inArray(item, suppressed_items)==-1){
//hi little item, I haven't saw you before, please go on... but I remember you
suppressed_items.push(item);
return true;
}
else{
//Hey, you have been submitted already, stay where you are!
return false; //or e.preventDefault(), it's a matter of faith :)
}
}
jQuery(document).ready(function(){
//don't worry, it won't replace your `ready` handlers, but just append new handler
jQuery("from").submit(function(e){
return allowOnlyOne(jQuery(this),e);
});
});
You can use the allowOnlyOne function with any item you wish. So, for example to allow single click on all hyperlinks, inside that ready handler add:
jQuery("a").click(e){
return allowOnlyOne(jQuery(this),e);
}
I hope you get the basic idea: catch the event, get the ID of the element that trigger it, fed it to AllowOnlyOne along with event.
Of course you can wrap it all around into self-executing closure to achieve incapsulation and so on...
If you already have jQuery I suggest you use it... All you need to do is make sure is that your form's onsubmit do not have a "return false" or else it can block jQuery's on submit.
Here's what you need to do:
Remove any return false from your form's onsubmit (if any). Don't worry we'll take care of this later in jQuery.
Add a class to your forms... something like "disableOnSubmit". Example:
<form action="something" onsubmit="yourExistingCode" class="disableOnClick">
</form>
OR
<form action="something" onsubmit="yourExistingCode" class="someOtherClass disableOnClick">
</form>
Implement a code similar to:
<script type="text/javascript">
$(document).ready(function(){
$('form.disableOnClick').submit(function(e){
// preventDefault() does the same as "return false;". It
// will not submit the form. If you're not using return false
// and want the form to be submitted remove the line below
e.preventDefault();
// Now diable any submit button
$('input[type=submit], button[type=submit]').attr('disabled, 'disabled');
});
});
</script>
I would like to replace the Javascript confirm() function to allow custom buttons instead of Yes/Cancel. I tried searching but all the solutions are event driven such as jquery dialog(where the code does not wait for a response but it is event driven). Does anyone know of a non-event driven solution. It must work in Safari as well as IE (so no vbscript).
Here is sample code in many parts of my system. This is old code and was not designed with event driven windows in mind. I am trying to avoid a rewrite.
**
// Wait for users response
if (result>2000) {
if (confirm("Are you sure this is right?")){
... do stuff
}
}
... continue with other stuff
... lots of other code.
if (confirm("Did you double check your numbers?")){
... do more stuff
} else {
... do something
}
**
Like the others have said, this isn't possible. confirm is a blocking function - no more script is executed until the user has dismissed the dialog - and you can't simulate that with other methods of Javascript.
A better solution would be to structure your code for asynchronous execution. This is almost always a better idea -- firstly, it lets you decide how your dialogs should look, what buttons there are, etc; and secondly, it doesn't block the user. They might have the important information they need to double-check open in another tab, or elsewhere on the page. With confirm they'd have to answer your question before being able to get to either of these places.
Here's a snippet of what the code might look like. There's a lot of blank bits here, but it might put you on the right track:
if (result>2000) {
displayConfirm("Are you sure this is right?", {
"Yes": function () {
// ... do stuff
},
"No": function () {
// do.. nothing? up to you.
}
}
}
You'll see here that there are two functions defined, but none actually get executed. The displayConfirm function would have to construct a dialog box (in whichever way) and then create buttons, using those functions as the click handlers (or at least, calling them from the click handler).
What you're trying to do is impossible. You'll have to use an event driven custom dialog solution, or stick with the browsers default confirmation dialog.
You will not be able to do this w/o changing your calls. No custom code can stop execution like the confirm box can. Any solution will require editing code to an event model.
As others have mentioned you can't do it directly, but I managed to do it in a round about way. Assuming like me you have a HTML button which submits a form, and want that button to have a jQuery modal dialog...
add an onclick event to the HTML button
make the onlick event open a jQueryUI dialog, and have the onclick event return false to cancel the button's default action
On your jQueryUI dialog, get the Yes/Ok button to remove the onclick event from the button in step 1, and then call then trigger the button to fire the click event
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.
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.