Why is my javascript function completely ignored? (Very simple example) - javascript

I have two buttons. Save and undo. They execute a javascript, and in the end they call resetButtons() which is meant to disable the buttons until any other change in the users input occurs. This code has worked for over a year now, but while implementing some new features, this one has broken.
Here is a direct snippet of code:
function undo(){
var r = confirm("Are you sure you want to cancel your inputs?");
if (r == true) {
setLoadedValues(); //calls in the cached previous values
updateAllBoxValues(); //updates view
console.log("before resetting buttons");
resetButtons();
console.log("after resetting buttons");
}
}
function resetButtons(){
console.log("resetting buttons");
$(".save").addClass("disabled");
$('.save').attr('disabled', 'disabled');
$(".undo").addClass('disabled');
$('.undo').attr('disabled', 'disabled');
console.log("done resetting buttons");
}
This gives the following output:
before resetting buttons
after resetting buttons
I have suspected some wrong formatting with brackets, but this is a direct snippet of code, and if undo() works, so should resetButtons(), right? Also, I should have been given a undefined function, or an unexpecetd end of input or something similar if that was the case. The call to resetButtons is also ignored when I click the save button. I am lost. Help?

You have a duplicate function definition. Rename one of them.

Your code seems fine, you must have some other error somewhere else (like a function named like those two).
By the way, I suggest using jQuery's "prop" method to change the disabled attribute:
$('foo').prop('disabled', true);
$('foo').prop('disabled', false);

Try triggering the resetButton() function in console, if there's an issue it should return it. Testing it in a random console gives all the logs it should, but I can't vouch for the selectors. The error is probably elsewhere in the code.

Related

Trigger function once

The Preface:
I have a form with a button, after clicking that - jquery function executes. After this closing, and this function still executes one more time.
The problem is - how to execute this function once. Note: it shouldn't be also triggered on refresh action(F5).
Thanks for the help.
P.S Sorry, i can't attach code examples.
P.S.S I tried to make more clear to understand...
As mentioned in the comments you need store state somewhere. Here's an overly simple example where we use hasExecuted (an arbitrary property I picked) on localStorage
if (window.localStorage && !window.localStorage.hasExecuted) {
window.localStorage.hasExecuted = "true";
runSomethingThatShouldOnlyRunOnce();
}

Using if-if or if-else if-else within a simple function

I wrote this simple function once, to display notifications on hash change :
function watchHash() {
if(location.hash == '#thanks') {
displayNotification('Thanks for your feedback, I\'ll try to get back to you as soon as possible.'); // Notify on form submit success
}
if(location.hash == '#error') {
displayNotification('Oops, something went wrong ! Please try again.'); // Notify on form submit error
}
}
window.onhashchange = watchHash;
I came back to it today and I thought, is it correct if I write it this way instead?
function watchHash() {
if(location.hash == '#thanks') {
displayNotification('Thanks for your feedback, I\'ll try to get back to you as soon as possible.'); // Notify on form submit success
}
else if(location.hash == '#error') {
displayNotification('Oops, something went wrong ! Please try again.'); // Notify on form submit error
}
else {
return;
}
}
window.onhashchange = watchHash;
If so, is it relevant?
I'm a bit confused here, I'd like to stick to best practices.
Thanks for your help.
The second scenario is much better. Why?
Because in the first scenario if first condition is met or not met - it doesn't matter, the second case is checked too, the third, the fourth and so on.
In second case If the first case fails, then the second one is tested, if it fails, the third is tested, so your software does not spend useless time checking scenario which will not happen.
In your case, either method works just as well. However, there are some times when using the else clause is truly the best way to handle over-lapping logic.
Untested p-code, just for an example
if (isRaining) && (iHaveUmbrella) {
iGetWet = false;
} else if (isRaining) {
iGetWet = true;
} else {
iGetWet = false;
}
In this case, the else if means that if the first condition is true, the second condition is never checked.
it might not be relevant for this usecase, but the 1st way is slower because the interpreter has to check conditions more often.. consider a game loop which is called 60 times per second and you don't structure your if else blocks you will get a huge performance hit.
Either way works properly. Differences between two are readability and pattern of coding.
Regarding readability, I personally prefer the first way to work on a single input. Second one works in better performance, but you might lost in multiple boundaries if you're not proficient developer.
Regarding pattern of coding, we can see that the first one won't return anything. Second one will return null ONLY IF hash tag is neither your selections. You can't use this function to check the condition because it won't return when hash tag is either '#thanks' or '#error'.
Your second version is "correct" insomuch as its functionality is identical to the first version, but the added code is entirely redundant.

jquery beginner - function to initiate by time

I am a rookie in JS, have a problem understanding JQUERY semantics.
I have a function written for checking the content of a cell.
Problem: the function just starts when the cell loses focus, if I click Submit, the error shows first, then it will run the function.
I want the function to run even when I am inside the cell. How to do it?
Initiated by this:
$(".user_id").blur(function(){ validateUserId($('.user_id')); });
The function:
function validateUserId(reference) {
if ( 5 == $(reference).val().length ) {
$.get('index.php?user=' + $(reference).val(), function(data) {
if ( "error" == data ) {
$(reference).parent().parent().addClass('error');
alert('error');
} else {
$(reference).parent().parent().removeClass('error');
$(reference).addClass('valid');
$(reference).parent().parent().addClass('success');
}
});
} else {
alert('short');
$(reference).parent().parent().addClass('error');
}
}
$(".user_id").on('keyup', function(){
validateUserId($(this));
});
i would use the keyup event.
So everytime somebody types a key in your input cell, the function will be executed.
$(".user_id").on("keyup", function(){ validateUserId($(this)); });
I changed the $(".user_id"), you take the value from ,to $(this). Since you want the value of the field you did the keyup event on. (And not an other field, if there would be 2 fields with the same .user_id class.)
Try to bind function on focus event
$("input[submit]").click(function(e){
e.preventDefault();
var valid = validateUserId($('.user_id')); // maybe the function could return a boolean value too instead of just adding classes to the HTML part
if (valid) {
$("#your_form_id").submit(); // only if the input is valid, submit it
}
});
sidenote on your problem: if you click the submit button it will first trigger the submit action which may give you an error and then execute the blur() block of code
Here is a working demo http://jsfiddle.net/pomeh/GwswM/. I've rewritten a little the code to:
cache DOM selections into variables,
use jQuery methods chaining,
improve if conditions with tripe equal signs,
separated AJAX calls and application logic,
cache identical AJAX calls,
use jQuery deferred to hide the asynchronous aspect of the verification (linked to AJAX)
Hope that'll help :)

Can I control the order in which javascript / jQuery events fire?

Background
I've got asp.net webform with a grid, and when users update textboxes in that grid, the onchange event kicks off a WebMethod call and updates the rest of the changed row. Nothing is saved at that time -- we're just updating the UI.
To commit the changes, you click the save button.
This actually works reliably in almost every scenario. However, there is one very persistant one that it feels like I should be able to solve, but it's time to call in the specialists.
The Problem Scenario
I'm using jQuery to capture the enter key, and unfortunately that event fires first, causing the page to submit before the callback completes. The row is not updated correctly. Stale and bewildering data is saved.
Update
I don't think you can make the enter behavior depend on the callback, because you could save without changing a row. In that case, if you didn't change a row, it would never save.
Now if there was some way to inspect javascript's internal list of things to do, or maybe create my own and then manage it somehow, that would work. But that's some heavy lifting for something that should be easy. So unless an expert tells me otheriwse, I have to assume that's wrong.
Attempts
Right now I'm using the built-in jQuery events and I've got this elaborate setTimeout persisting the fact that a save was attempted, pausing long enough for the WebMethod to at least get called, and relying on the callback to do the submit. But it turns out javascript ansychrony doesn't work the way I hoped, and the onchange event doesn't even fire until that chunk of code completes. That was surprising.
I was thinking I could use my own little object to queue up these events in the right order and find a clever way to trigger that, etc.
This all seems like the wrong direction. Surely this is insane overkill, this is a common problem and I'm overlooking a simple solution because I don't work in javascript 24/7.
Right?
Code
Here's what I've got right this minute. This obviously doesn't work -- I was trying to take advantage of the async nature of jquery, but all of this apparently has to conclude before the row's onchange event event fires:
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) {
handleEnter();
return false; //apparently I should be using e.preventDefault() here.
}
});
function handleEnter() {
setTimeout(function () {
if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
alert('recalculating...');
return true; //recur
}
//$('input[id$="ButtonSave"]').click();
alert('no longer recalculating. click!');
return false;
}, 1000);
}
And then a typical row looks like this. Note that I'm not using jquery to bind this:
<input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />
I could post the code for recalculateOrderItemRow, but it's really long and right now the problem is that it doens't fire until the after keypress event concludes.
Update Dos
According to Nick Fitzgerald (and man is that a cool article) the use of setTimeout should cause this to become async. Digging further into interactions between setTimeout and jQuery, as well as interactions between normal javascript events and jQuery events.
Preventing ENTER shouldn't be causing you so much trouble! Make sure you have something like this on your code:
$(document).on('keydown', 'input', function(e) {
if(e.keyCode == 13) {
e.preventDefault();
}
});
UPDATE
It looks like you do want to save on ENTER, but only after the UI is updated on change. That is possible. You could use a flag a Matthew Blancarte suggested above, trigger save from the change callback, and get rid of the setTimeout.
But I wouldn't recommend that. You are better off relying solely on the save button for saving. If you don't, your users will have to wait for two async operations to complete before saving is finished. So you'd have to block the UI, or keep track of all async operations, aborting some as needed. I think it's not worthy, ENTER becomes less intuitive for the users if saving takes too long.
The hideous mass of workarounds below, which effectively took me all day today and half of yesterday to write, seems to solve every permutation.
The amusing thing is that enter itself doesn't trigger onchange, if you call e.preventDefault(). Why would it? The change doesn't actually happen until the default behavior of clicking the save button occurs.
Very little else about this is amusing.
//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false;
var saveOnID = '';
//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;
//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
originalVal = this.value;
});
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) { //enter pressed.
e.preventDefault();
handleEnter();
}
});
//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns.
//Otherwise, we save as normal.
function handleEnter() {
var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")
//did we press enter with a field that triggers a callback selected?
if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) {
//Set details so that the callback can know that we're saving.
isSaving = true;
saveOnID = focusedElement.attr('id');
//Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
focusedElement.trigger("change");
focusedElement.focus();
} else {
forceSave();
}
}
function isCallbackElement(element) {
return (element.length == 1);
}
function isElementDirty(element) {
if (element.length != 1)
return false;
return (element.val() != originalVal);
}
function forceSave() {
isSaving = false;
saveOnID = '';
$('input[id$="ButtonSave"]').click();
}
This gets called in the change event for the textboxes:
function recalculateOrderItemRow(textbox) {
//I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.
//Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
orderItemDose,
ProductItemSize,
orderItemUnits,
orderItemUnitPrice,
onRecalculateOrderItemRowComplete);
}
And then, at the end of the WebMethod callback code we pull the updated form values out, put the caret where it needs to be using jquery.caret, and check to see if we need to force a save:
function onRecalculateOrderItemRowComplete(result) {
var sender, row;
sender = $('input[id="' + result.Sender + '"]');
row = $(sender).closest('tr');
row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));
calculateGrandTotalPrice();
$(document.activeElement).select();
if (isSaving && saveOnID == result.Sender) {
forceSave();
}
}
result.Sender is the ID of the calling control, which I stuffed into the WebMethod call and then returned. saveOnID may not be perfect, and it might actually be even better to maintain a counter of active/uncallback-ed WebMethod calls to be totally sure that everything wraps up before save. Whew.
Can you post your javascript? Sounds like you're on the right track. I would change my OnChange events to increment a variable before making the AJAX call. I'll call the variable inProcess and initialize it to zero. When the AJAX call comes back, I would update the inProcess to the current value minus one. On the Enter key event, I would check to that inProcess equals zero. If not, you could either warn the user or set a timeout to try again in a bit.
You could unbind the Enter key capture while you are in the onChange event, then rebind it at the end of the callback function. If you post some code, I could give a more specific answer.
It sounds like you shouldn't be calling the WebMethod asynchronously. Call it synchronously, and on success, save your data.

Run function only if event default is/isn't prevented

Is there a way to run a function only if event.preventDefault() is called on an event (by another unknown function). This is for a jQuery plugin, so I don't have any knowledge of what other parts of the page might be doing. I've tried this:
Event.test = Event.preventDefault;
Event.preventDefault = function () {
alert('Success');
this.test();
}
but it doesn't work... just behaves as normal, with no errors.
Conversely, I want the opposite too... to call a function only if event.preventDefault() isn't called. In effect, to add a function to the default action for an event. Any ideas? Is all this at all possible?
Edit: Based on the comment, I've got a solution to the first problem: http://jsfiddle.net/nathan/VAePB/9/. It works in Chrome (alerts function preventDefault() { [native code] }, but IE alerts undefined. So IE won't let me define Event.prototype.test, but it will let me redefine Event.prototype.preventDefault. Weird. I'm sure I can come up with a solution to the the second problem based on this one if I can just get it to work in IE.
I'm not sure I've understand. Can't you just use event.isDefaultPrevented() like this
For the first problem, try something like this:
oldPreventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function() {
//do stuff
oldPreventDefault.call(this);
}
I don't know if that will work, but it might be worth a shot.
For the second problem, I would try something similar to live event handling. Put a listener on a parent element (i.e. body or a top-level div). If you can get your hook into preventDefault as noted before, you can use that to set a flag. If the event bubbles up to that element and your flag isn't set, do your extended behavior. Though this won't work with all events, since not all events bubble. Another way to tackle this problem might be to delay execution until the current stack has finished using setTimeout(0,...) and then checking the flag.

Categories

Resources