Why does alert() execute before my $.each loop? - javascript

I'm using the jQuery validation plugin with a fairly large form (88 fields) that was converted to html5 from a fillable PDF by this nice online tool. It works great, but seems to be acting more asynchronously than I want it to.
In the code that runs in response to clicking the Submit button for the form, if any validation errors were detected, I want to display the messages and pop up an alert telling the user to correct the errors shown and try again. The code looks like this:
success: function() {
const OSHform=$("form").eq(0);
if (OSHform.valid()) {
top.document.location.href = "/Adsentry/completed";
}
else {
OSH.placeMessages();
alert("Fields did not validate. Please fix the highlighted fields and try again");
}
}
Where:
OSH.placeMessages = function() {
$('div[id^="form"].error').each(function(index) {
let me = $( this )
let labelid = me.attr("id");
let inputid = labelid.replace("-error", "");
let input_elem = $("#" + inputid);
let heightnum = 1 * input_elem.css("height").replace("px", "");
let topnum = 1 * input_elem.css("top").replace("px","");
if (input_elem.attr("name") === "JR DOB") {
topnum -= heightnum;
}
let leftnum = 1 * input_elem.css("left").replace("px", "");
let widthnum = 1 * input_elem.css("width").replace("px", "");
me.css("position", "absolute");
me.css("top", (topnum + heightnum - 6) + "px");
me.css("left", (leftnum + 10) + "px");
me.css("color", "red");
me.css("z-index", 1000);
me.css("display", "block");
});
}
But when I submit a form with validation errors, the alert pops up first, before any errors are displayed in the form. Then when I click OK to dismiss the alert, the invalid fields get highlighted and error messages get filled in, just as they should. How can I make the alert wait until after the messages are displayed? I didn't expect that the $.each() loop would go off to async land and let the alert() run ahead on the main thread...
Of course the real question is how do I get it to behave the way I expect? It doesn't seem like $.each returns a promise such that I could put the alert() in a function called when the promise is resolved...

You can use setTimeout to allow the browser to repaint first.
setTimeout(()=>alert("Fields did not validate. Please fix the highlighted fields and try again"));

Related

JQuery order of execution, synchronization issue

this seems like a pretty basic issue but I could figure out how to make sure the jquery gets executed before I check for HTML elements.
I have a loop to enter the search bar and read results and check if the search terms returns anythings.
This does what I wants except the if structures check for element even before the search button gets clicked. The website I run the extension on is https://www.avnet.com/wps/portal/us
var searchTerms = ["internal", "null?","random", "asdfsadfa", "relay"];
var i;
for (i = 2; i < searchTerms.length; i++) {
var curTerm = searchTerms[i];
$('#searchInput').val(curTerm);
$('.input-group-addon').click();
if($(".avn-noresultspage-main-section")[0]){
$(".avn-noresultspage-main-section").css( "border", "10px solid red" );
alert('This is a Null!');
}else{
alert('Not a Null!');
}
}
So with some googling I see that the chaining/ call back function is the way to go.
Callback function example
So I call the check elements function after the click in jquery. But now nothing happens. I just run through the for loop and last click was not even executed?
How should I properly chain my actions?
var searchTerms = ["internal", "null?","random", "asdfsadfa", "relay"];
var i;
for (i = 2; i < searchTerms.length; i++) {
var curTerm = searchTerms[i];
$('#searchInput').val(curTerm);
$('.input-group-addon').click(function() {
if($(".avn-noresultspage-main-section")[0]){
$(".avn-noresultspage-main-section").css( "border", "10px solid red" );
alert('This is a Null!');
}else{
alert('Not a Null!');
}
} );
}
Update: I did try to use the time out function but I seems to freeze the execution of click as well.
var searchTerms = ["internal", "null?","random", "asdfsadfa", "relay"];
var i;
for (i = 2; i < searchTerms.length; i++) {
var curTerm = searchTerms[i];
setTimeout(function() {
$('#searchInput').val(curTerm);
$('.input-group-addon').click();
}, (3 * 1000));
if($(".avn-noresultspage-main-section")[0]){
$(".avn-noresultspage-main-section").css( "border", "10px solid red" );
alert('This is a Null!');
}else{
alert('Not a Null!');
}
}
Is there a way to delay after the click?
Your modified code is definitely different in code logic. The syntax .click(function) is used to adding/modifying an event handler to "click" event. To make it work like the first version one, you need to add a chain trigger after it, like click(function(){...}).click();
And the reason why checking code's executing before the click event trigger is, the search event is probably a AJAX event and when you trigger click, Javascript will only "click" and execute the checking code without waiting for search event to return a result. You should use:
$(document).ajaxSuccess(function(){
//checking code here
});
But it apply for ALL the ajax events on this site, so the best solution imo is you should add a timeout waiting for checking code, to make sure we have the search result returned.

eventListener doesn't fire/doesn't bind properly

I've been messing around with YouTube API to make a button that copies the video ID on click using jQuery. I have no idea why, I don't get any Javascript errors and after hours (actually been working on this problem for 2 days), I still haven't figured out why does the eventListener doesn't fire.
The eventListener is bound at the end of the function, which is fired when you hit a search button. Each time you click on the search button or on the next page/ previous page buttons, they fire the makeRequest function. Somehow, though, the function triggered by the eventListener never fires (the function code isn't in this parcel of code itself, but it's actually just console.log's). Can anyone help? Would be really appreciated <3
function makeRequest(e) {
$('#search-container')[0].innerHTML = '';
ytSearchResults = [];
var q = $('#query').val();
var request = gapi.client.youtube.search.list({
q: q,
part: 'snippet',
maxResults: 15,
pageToken: ytPage,
type: 'video',
safeSearch: 'none',
videoSyndicated: 'true'
});
request.execute(function(response) {
var str = response.result;
ytNextPage = str.nextPageToken;
ytPrevPage = str.prevPageToken;
for(i=0;i<str.items.length;i++){
$('#search-container')[0].innerHTML += '<br><image src='+str.items[i].snippet.thumbnails.default.url+'></image><br><button id="'+str.items[i].id.videoId+'" class="NCScopiable">'+str.items[i].snippet.title+'</button>';
ytSearchResults.push(str.items[i].id.videoId);
}
$('#search-container')[0].innerHTML += '<br><div id="button-changePage"><span id="button-prevPage" style="color:blue;cursor:pointer;margin:5px"><u>Previous Page</u></span><span id="button-nextPage" style="color:blue;cursor:pointer;margin:5px"><u>Next Page</u></span>';
if(e&&ytCurPage>0){
$('#button-prevPage')[0].style.display = 'block';
$('#button-nextPage')[0].style.display = 'block';
} else {
ytCurPage = 0;
$('#button-prevPage')[0].style.display = 'none';
}
$('#button-prevPage').on('click',function(){ytCurPage-=1;ytPage=ytPrevPage;makeRequest(true);});
$('#button-nextPage').on('click',function(){ytCurPage+=1;ytPage=ytNextPage;makeRequest(true);});
console.log('Current ytCurPage value: '+ytCurPage);
});
$('.NCScopiable').on('click',cTWI);
}
The problem is that the line
$('.NCScopiable').on('click',cTWI);
Is being executed before the code inside the callback passed to request.execute has fired, so those elements don't exist yet. Thus $('.NCScopiable') is returning nothing.
Move that line into the last line of your callback code and you should be good to go, for example:
request.execute(function(response) {
var str = response.result;
ytNextPage = str.nextPageToken;
ytPrevPage = str.prevPageToken;
// ...code removed for brevity...
// this was the code that is actually creating the elements with
// the NCScopiable class
console.log('Current ytCurPage value: '+ytCurPage);
$('.NCScopiable').on('click',cTWI);
});

Function is called too many times and retains old variable values

I currently have a table that is updated every time a user presses a button (handled using Backbone events).
Here is the event that is fired every time the button is pressed:
add: function(ev) {
if($(ev.target).data("card-id")) {
this.card_array.push($(ev.target).data("card-id"));
current_deck.push($(ev.target).data("card-id"));
updateCurrentDeckTable();
} else {
alert("Couldn't add the card. Please wait a few seconds before trying again.");
}
console.log(this.card_array);
console.log(current_deck);
}
updateCurrentDeckTable is defined as so:
function updateCurrentDeckTable() {
console.log(current_deck);
var content = "";
$.each(current_deck, function(i, cardId) {
console.log("Index: "+i+" || Card ID: "+cardId);
var M_Card = Parse.Object.extend("Card");
var M_CardQuery = new Parse.Query(M_Card);
M_CardQuery.get(cardId, {
success: function(m_card) {
console.log("Got "+m_card.get("cardTitle"));
content += "<tr>";
content += "<td>"+m_card.get("objectiveNumber")+"</td>";
content += "<td>"+m_card.get("cardTitle")+"</td>";
if (m_card.get("cardAffiliation") == null) {
content += "<td>-</td>";
} else {
content += "<td>"+m_card.get("cardAffiliation")+"</td>";
}
content += "<td><a>Details</a></td>";
content += "</tr>";
console.log("Content: "+content);
$('#deck-table tbody').append(content);
},
error: function() {
alert("Error.");
}
});
});
}
Now, the first time the user presses the button, everything works just fine. However, on the second press, more than one row is added to the table. Further, the content variable seems to keep it's old values. Shouldn't it reset to "" every time the function is called?
Does anyone have any idea what's happening? If you need more information, please ask.
Why oh why are you passing data using a global current_deck variable?
Each time you enter the updateCurrentDeckTable branch of your add method:
if($(ev.target).data("card-id")) {
this.card_array.push($(ev.target).data("card-id"));
current_deck.push($(ev.target).data("card-id"));
updateCurrentDeckTable();
}
You're adding another entry to the global current_deck and then updateCurrentDeckTable iterates over that array:
$.each(current_deck, function(i, cardId) { ...
I don't see anything that clears out current_deck so it will just keep growing. That would explain the odd behavior you're describing.
The solution would be to stop using a global for current_deck, just pass it around as an argument and clean it up as needed.

JQuery Mobile Slider Not ReEnabling

So I think I am doing everything correct with my jquery mobile slider, but the control is not being re-enabled. I've made a pretty decent jsFiddle with it, in hopes someone will spot the error quickly.
On the fiddle you will see the jQuery moblie control. If you click and move the slider position the event will fire that the control value changed. If you end up changing the value more than 5 times within 20 seconds the control will lock up. You can think of this as being a cooldown period. Well after the control cools down it should be re-enabled for more mashing.
Problem is, the control never comes back from being disabled!
http://jsfiddle.net/Narq6/
Sample Javascript:
var sent = 0;
var disabled = false;
$('#slider-fill').on( 'slidestop', function()
{
send();
writeConsole(sent);
})
function send()
{
setTimeout(decrease, 4000);
sent +=1;
if(sent > 5)
{
$('#slider-fill').prop('disabled', 'disabled');
disabled = true;
}
}
function decrease()
{
if(sent > 0)
sent -= 1;
writeConsole('decrease');
writeConsole(sent);
if(sent === 0)
{
//CODE TO DISABLE HERE!!!
//LOOK HERE THIS IS WHERE I REMOVE THE DISABLE!!!
writeConsole('no longer disabled!');
$('#slider-fill').prop('disabled', '');
///YOU LOOKED TOO FAR GO BACK A LITTLE BIT :D
}
}
function writeConsole(message)
{
var miniconsole = $('#miniConsole');
var contents = miniconsole.html();
miniconsole.html(contents + message + '<br/>' );
miniconsole.scrollTop(10000);
}
You were using incorrect enable/disable syntax.
This one is a coorect syntax:
$('#slider-fill').slider('disable');
and
$('#slider-fill').slider('enable');
Here's am working example made from your jsFiddle: http://jsfiddle.net/Gajotres/djDDr/

Display javascript validation messages on the screen not alert messages?

I'm using struts1.3.8. I'm using struts ValidatorPlugIn for generating client side and server side validation messages.
Now client side javascript is generated by validator plugin. If there is any validation errors it is displaying in alert messages. But i want to display them besides the text field.
I'm still now working with alert messages only.. But now requirement changed. I tried but no use...
How to do it?
This is the code generated by plugin
`enter code here` function jcv_handleErrors(messages, focusField) {
if (focusField && focusField != null) {
var doFocus = true;
if (focusField.disabled || focusField.type == 'hidden') {
doFocus = false;
}
if (doFocus &&
focusField.style &&
focusField.style.visibility &&
focusField.style.visibility == 'hidden') {
doFocus = false;
}
if (doFocus) {
focusField.focus();
}
}
alert(messages.join('\n'));
}
Without specific information, all I can really suggest is a variation of the following:
window.alert = function(message){
console.log(message);
}
JS Fiddle demo.
Which simply ensures that any messages passed to alert() get passed, instead, to the console.log().
You could, instead, target the messages to a particular element:
window.alert = function(message) {
var output = document.getElementById('output'),
newTextContainer = document.createElement('p'),
text = document.createTextNode(message);
newTextContainer.appendChild(text);
output.appendChild(newTextContainer);
}
JS Fiddle demo.
Using either of these will break any usage of your alert() function in your page, though. So I'd suggest, instead, creating a new function with the latter example (immediately above) and calling that function, rather than over-writing alert().
With regards to creating a custom function to handle your alerts, as well as specify a particular element to which the new 'alerts' should be appended:
function newAlert(message, elem) {
// message is a string containing the message to display.
// elem is the id of the element into which the message should be displayed,
// defaults to an id of 'output' if no element is specified.
var output = elem ? document.getElementById(elem) : document.getElementById('output'),
newTextContainer = document.createElement('p'),
text = document.createTextNode(message);
newTextContainer.appendChild(text);
output.appendChild(newTextContainer);
}
JS Fiddle demo.
Edited in response to question from OP, below:
Next again submit the form I want to overwrite the previous error message. Not twice display the same message.
There are a couple of ways of doing this, assuming you only want to show the last of the error messages, rather than appending those error messages; in the first example I'm using a while loop to remove the firstChild of the output element and, when empty, appending the new error message:
function newAlert(message, elem) {
var output = elem ? document.getElementById(elem) : document.getElementById('output'),
newTextContainer = document.createElement('p'),
text = document.createTextNode(message);
while (output.firstChild){
output.removeChild(output.firstChild);
}
newTextContainer.appendChild(text);
output.appendChild(newTextContainer);
}
JS Fiddle demo.
An alternative is to get a reference to the first paragraph element in the output element (if one exists, otherwise create one) and then simply overwrite the text in that element:
function newAlert(message, elem) {
var output = elem ? document.getElementById(elem) : document.getElementById('output'),
textContainer = output.getElementsByTagName('p')[0] || output.appendChild(document.createElement('p'));
if (textContainer.firstChild){
textContainer
.firstChild
.nodeValue == message;
}
else {
textContainer
.appendChild(document
.createTextNode(message));
}
}

Categories

Resources