Weird overlay display issue in jQuery [duplicate] - javascript

This question already has answers here:
What does "async: false" do in jQuery.ajax()?
(7 answers)
Closed 3 years ago.
I have an ajax function and thought it would be nice to include a little ajax-spinner to tell the enduser something is actually happening. This is my current jQuery function:
$('#contact-form').submit(function(e)
{
e.preventDefault();
let overlay = $('#overlay'),
loader = $('#loader-popup');
console.log(overlay);
console.log(loader);
console.log('===================');
//show overlay
overlay.removeClass('hidden');
loader.removeClass('hidden');
console.log(overlay);
console.log(loader);
let formData = new FormData($(this)[0]),
params = [];
$.ajax({
data: formData,
type: 'post',
url: '/pages/contact-us/action/send.php',
cache: false,
contentType: false,
processData: false,
success: function(res)
{
if (res == 1) {
params['type'] = 1;
params['msg'] = 'We will be with you as soon as we can!'
} else {
try {
res = $.parseJSON(res);
let data = [];
$.each(res, function(key, value) {data.push(value)});
params['type'] = 2;
params['msg'] = data.join('<br />')
} catch(e) {
console.log(e);
alert('Huh. that\'s weird, something went wrong! Please try again');
//cause syntax error to stop script working
die()
}
}
validator.displayAlert(params['type'], params['msg'])
},
error: function(res)
{
console.log(res);
alert('Don\'t worry.. it\'s not you, it\'s us.')
}
});
//hide overlay
overlay.addClass('hidden');
loader.addClass('hidden');
});
But weirdly the overlay doesn't show, nor does the loader. What makes this hard to kinda debug and fathom is the console.log output.
first console.log(overlay)
Object [ div#overlay.hidden ]
second console.log(loader)
Object [ div#loader-popup.hidden ]
third console.log(overlay)
Object [ div#overlay ]
fourth console.log(loader)
Object [ div#loader-popup ]
So I can see that my .removeClass() function is working, however, inspecting my page once the form is being submitted shows the elements with the hidden class. If I manually remove that hidden class in the inspector tab then everything shows, so I know it's not a CSS issue.
You can see this happen on a much simpler scale here
I've also tried with .toggle() with no avail.
How do I even begin to debug something that seems to work behind-the-scenes but, not on screen?

You should call hide the overlay in your callback, because it'll be executing asynchronously.
Something like
try {
res = $.parseJSON(res);
let data = [];
$.each(res, function(key, value) {
data.push(value)
});
params['type'] = 2;
params['msg'] = data.join('<br />')
} catch (e) {
console.log(e);
alert('Huh. that\'s weird, something went wrong! Please try again');
//cause syntax error to stop script working
die()
} finally {
//hide overlay
overlay.addClass('hidden');
loader.addClass('hidden');
}

The logic within the $.ajax() call is asynchronous. As such you remove the class then immediately add it back in as the AJAX request is in progress.
To fix this, change the addClass() calls to be made after the AJAX request completes. In your case the best place to do this would be in the complete callback as it will fire whether the AJAX request completed successfully or with an error:
$('#contact-form').submit(function(e) {
e.preventDefault();
let $overlays = $('#overlay, #loader-popup').removeClass('hidden');
let formData = new FormData(this),
params = [];
$.ajax({
// ajax settings...
complete: function() {
$overlays.addClass('hidden');
}
});
});

Related

How to Unit Test ajax jquery function inside document.ready

I have a script that makes $.ajax request for a json api. So what I want to do is to build unit test so I can test the result from the ajax request. For example if I get json object back. I know result should include "items" and "result" which is an array. The things is I dont know how to initialize the $.ajax function which is inside a
$("#button").click(function() { });
Here's the skeleton of my javascript index.js file. The file is not complete. as it is longer. I just included the relevant parts. But it works. Here's the app live online http://pctechtips.org/apps/books/
$(document).ready(function() {
var item, tile, author, publisher, bookLink, bookImg;
var outputList = document.getElementById("list-output");
var bookUrl = "https://www.googleapis.com/books/v1/volumes?q=";
var searchData;
$("#search").click(function() {
outputList.innerHTML = ""; //empty html output
searchData = $("#search-box").val();
//handling empty search input field
if(searchData === "" || searchData === null) {
displayError();
}
else {
// console.log(searchData);
// $.get("https://www.googleapis.com/books/v1/volumes?q="+searchData, getBookData()});
$.ajax({
url: bookUrl + searchData,
dataType: "json",
success: function(response) {
console.log(response)
if (response.totalItems === 0) {
alert("no result!.. try again")
}
else {
$("#title").animate({'margin-top': '5px'}, 1000); //search box animation
$(".book-list").css("visibility", "visible");
displayResults(response);
}
},
error: function () {
alert("Something went wrong.. <br>"+"Try again!");
}
});
}
$("#search-box").val(""); //clearn search box
});
});
In your test you need first to prepare a HTML fixture which will contain all the required elements like #search. After preparing it, you can load your script via $.getScript() - it will attach click event listener to #search. Finally, you have to spy on $.ajax and trigger the click manually via $('#search').trigger('click')

Objects generated by javascript vanish right after appearing

On a button click I'm calling a python script and I'm populating the result into an array. Once done, I create <a href> as many <a href> objects as the length of the array. Problem is, once the links are displayed, they vanish immediately.
Body:
//I obviously get the same effect by putting the onclick event in the submit button
window.onload = function() {
alert("loaded");
document.getElementById('submit_searchsubs').onclick = function () {
FindSubtitles();
};
};
function FindSubtitles() {
postData = {"movie": "Outlander", "season":"1", "episode":"2"};
$.ajax({
url: "/cgi-bin/find_subtitles.py",
type: "post",
datatype:"json",
async : false,
data: {postData},
success: function(response){
var subs = new Array();
var json = $.parseJSON(response);
for (var i=0;i<json.length;++i) {
subs[i] = new Array(json[i].IDSubtitle, json[i].SeriesEpisode, json[i].SubHash, json[i].SubDownloadsCnt, json[i].SeriesSeason, json[i].ZipDownloadLink, json[i].MovieName, json[i].id, json[i].SubFileName);
}
DisplaySubtitles(subs); //show the users the new words found in this subtitle
}
})
.fail(function(err) {
alert("error" + err);
});
}
function DisplaySubtitles(subs) {
var SubtitleDiv = document.getElementById("SubtitleDiv");
var a = document.createElement('a');
for (i = 0; i < subs.length; i++) {
var linkText = document.createTextNode(subs[i][8]);
a.appendChild(linkText);
SubtitleDiv.appendChild(a);
}
}
So what happens:
Page loads
alert("loaded") is displayed as it's fired in window.onload
FindSubtitles runs
DisplaySubtitles runs and <a> links appear
<a> links disappear and alert("loaded") is displayed again.
I don't know if I should use async: true, because in that case I get [Object object] error, which is strange because in case of another script I use that, otherwise it would freeze the UI.
Any ideas?
As Kieveli mentioned, if submit_searchsubs is a form button that submits, it may be refreshing the page after submit. Try using return false; to bypass the default browser onclick action.
document.getElementById('submit_searchsubs').onclick = function () {
FindSubtitles();
return false;
};

Unexpected call to method or property access. jquery.js?ver=1.7.1

I'm getting 'Unexpected call to method or property access' in IE7 and my script won't work in IE8 and I can't for the life of me figure out why.
I've been using developer tools in IE (woohooo!) but it isn't much help. The error I am getting is in Jquery:
SCRIPT65535: Unexpected call to method or property access.
jquery.js?ver=1.7.1, line 3 character 31871
It works perfectly fine in IE9, Safari, FF and chrome.
On the Html page, I click the following link which passes the vale of the data-tax attribute to the script. Do you think perhaps it has to do anything with html5? Any pointers will be much appreciated.
For example, if you click Brad Pitt, it should display movies Brad Pitt is in:
<li class="ajaxFilterItem brad-pitt af-actor-6 filter-selected" data-tax="actor=6"><span class="checkbox"></span>Brad Pitt (1)</li>
I pass the following value to
filterAjaxify("actor=6")
And this is the offending code:
(function($){
var isRunning = false;
// Return an array of selected navigation classes.
function loopSelected(_node) {
var _arr = [];
_node.each(function(){
var _class = $(this).attr('data-tax');
_arr.push(_class);
});
return _arr;
};
// Animate the progress bar based on Ajax step completion.
function increaseProgressBar(percent){
$('div#progbar').animate({
width: percent + '%'
},30);
};
// Join the array with an & so we can break it later.
function returnSelected(){
var selected = loopSelected($('li.filter-selected'));
return selected.join('&');
};
// When the navigation is clicked run the ajax function.
$('a.ajax-filter-label, a.paginationNav, a.pagelink').live('click', function(e) {
if(isRunning == false){
isRunning = true;
e.preventDefault();
var relation = $(this).attr('rel');
if($(this).parent('li').length > 0) {
$(this).parent('li').toggleClass('filter-selected');
thisPage = 1;
}
if(relation === 'next'){
thisPage++;
} else if(relation === 'prev') {
thisPage--;
} else if($(this).hasClass('pagelink')){
thisPage = relation;
}
filterAjaxify(returnSelected());
}
});
// Do all the ajax functions.
function filterAjaxify(selected){
$.ajax({
url: ajaxurl,
type: 'post',
data: {
"action":"affilterposts",
"filters": selected,
"posttypes": posttypes,
"qo": qo,
"paged": thisPage,
"_ajax_nonce": nonce
},
beforeSend: function(){
$('div#ajax-loader').fadeIn();
$('section#ajax-filtered-section').fadeTo('slow',0.4);
increaseProgressBar(33);
},
success: function(html){
increaseProgressBar(80);
$('section#ajax-filtered-section').html(html);
},
complete: function(){
$('section#ajax-filtered-section').fadeTo('slow',1);
increaseProgressBar(100);
$('div#ajax-loader').fadeOut();
isRunning = false;
},
error: function(){}
});
};
})(jQuery);
The <section> is new in HTML5, older IE doesn't know how to digest that, and has some DOM issues when you try and append things to such elements.
E.g. http://jsfiddle.net/EKU7R/

Chrome doesn't displays the dynamic css propery change by Jquery before Ajax sync call

I have a tricky problem with Google Chrome Browser.
I have the folowing HTML node:
<div class="result-req-chat pointer float-right" onclick="chat.addUser(this/*, other vars*/)" ><img src="/images/profile_icon_4.png" alt="" /></div>
On the click event it triggers the chat object's method
this.addUser = function(trigger_node, id, is_silent, session, show_block_message){
if(trigger_node){
this.bk_trigger_node.html = trigger_node.innerHTML;
this.bk_trigger_node.cn = trigger_node.className;
trigger_node.innerHTML = '';
jQuery(trigger_node).addClass("loader");
jQuery(trigger_node).removeClass("bpurple");
jQuery(trigger_node).removeClass("bgray");
jQuery(trigger_node).removeClass("button");
}
//alert('if this is executed then it displays the previous changes of the node');
if(trigger_node.innerHTML == ''){
this.addUserToChat(id, is_silent, session, show_block_message);
}
if(trigger_node){
trigger_node.innerHTML = this.bk_trigger_node.html;
trigger_node.className =this.bk_trigger_node.cn;
}
}
addUserToChat():
this.addUserToChat = function (id, is_silent, session, show_block_message){
var response = this.chat_tabs.addTab(id, null);
if(response.error){
callUrl("/me/chat/remove-session/id/"+id);
this.chat_tabs.removeTab(id);
if(show_block_message) alert(response.message);
}else{
this.createTabsBar();
if(!is_silent){
this.switchTab(id);
this.resetContainer(is_silent);
}
if(id == this.chat_tabs.active_tab){
this.active_chat_obj.refresh(session);
}
if(this.closed){
if(this.stop_check){
return;
}
this.resetContainer();
this.switchTab(id);
}
callUrl("/me/chat/add-session/id/"+id);
}
}
chat_tabs.addTab():
// creates and adds the a tab
this.addTab = function(id,name,user_data,session){
var exists = this.getTab(id);
if(!exists){
if(session){
var user_session_id = session.id;
var user_session_data = session.data;
}else{
var session = this.createSession(id);
if(session.error){
return session;
}
var user_session_id = session.id;
var user_session_data = session.data;
}
if(name){
var user_name = name;
}else{
var user_name = this.getName(id);
}
if(user_data){
var user_data = user_data;
}else{
var user_data = this.getData(id);
}
var ob = new Object({
user_id: id,
user_name: user_name,
user_data: user_data,
user_session_id: user_session_id,
user_session_data: user_session_data,
has_new:false,
chat_screen: new ChatScreen(session, id, user_name, user_data, this.main_user_id, this.main_user_photo)
});
this.chat_users.push(ob);
return ob;
}else{
return exists;
}
}
callUrl():
function getUrl(url){
return jQuery.ajax({ type: 'GET', url: url, async: false }).responseText;
}
The point is that the method addUserToChat() contains a syncronous Ajax call.
The problem with Chrome is that the trigger_node changes aren't displayed. If you watch with the built-in JS debuger then everithing goes ok ( even with displaying ) .Also if you uncomment the alert.
It runs on Mozilla ( latest version ).Also the Crome is the latest version.
I can observe that in the time that it waits for the ajax response, the page is unresponsive to events like hovers, tips etc.
Do you have any suggestions for this? How can I implement a workarround method?
Synchronous Ajax calls are bad practice! They stop the browser for the entire duration and fool the user into thinking something crashed. You really should change this.
To your question why you don't see the latest DOM changes:
When you change something in JavaScript the browser will not immediately change the DOM, because painting a ui element is far more expensive than painting a dozen. So modern browsers will try to change the DOM as lazy as possible.
There are, apart from performance other upsides, like
$('p').hide();
can hide all p elements at the same time although jQuery will select each and than change the css.
I cant't give you any hind of a workaround without understanding your code better, sorry. Thanks!
UPDATE:
After reading your code, I would think about adding some closures to the application. A basic concept of javascript is that functions are first class types. I personally think, that your program flow is less than ideal, and this is the area of improvement. the calls to call url should look something like this:
var callUrl = function(url, callback, interactionStoped) {
if(typeof interactionStoped != 'undefined' && interactionStoped == true) {
//add code to display some loading animation
}
jQuery.ajax({ type: 'GET', url: url, success: function(data) {
callback(data);
//remove loading animation here
} });
}
as a start. Then you refactor your getUrl calls.
Funny thing is in your code example you never use the response, so I don't know what your app is waiting for. Assuming it is something important you must handle the response always in your callback.
Try looking at your app as if it were a tree. A Parent Function or Object will call itself some child functions that handle different tasks, wich themselves will invoke other functions. Build methods that are small and do only one thing on a really small set of data / parameters.
I can't rewrite your complete code, but I hope this helps anyway.
When do you try to display/fill the trigger_node variable?
It seems a bit like you aren't executing this action in the callback-function of the AJAX-request. Note that if the request is still running while you try to check for trigger_node, it won't of course show your changes.

Replacing div content using jquery in a jsp

So far I've been making an AJAX call to replace the content of a div with another page, using the following code:
<script>
function fetchContainerContent(url, containerid) {
var req = false
if (window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP")
} catch (e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP")
} catch (e) {}
}
} else if (window.XMLHttpRequest) {
req = new XMLHttpRequest()
} else {
return false
}
req.onreadystatechange = function() {
requestContainerContent(req, containerid)
}
req.open('GET', url, true)
req.send(null)
}
function requestContainerContent(req, containerid) {
if (req.readyState == 4 && (req.status==200 || window.location.href.indexOf("http")==-1))
document.getElementById(containerid).innerHTML = req.responseText
}
</script>
I have tried transforming the above code to work with jQuery as below but it doesn't work. In other words, I am trying to mimic the end result of the above behaviour but it is nowhere near the same. In fact, nothing happens on screen, nothing changes. I should mention that I don't really need the Loading... but since the examples I've seen use it and since I'm not sure how to correctly syntax jQuery, I've left it in.
<script>
function fetchContainerContent(url, containerid) {
jQuery.ajaxSetup ({
cache: false
});
var ajax_load = "loading...' />";
jQuery("#load_basic").click(function() {
jQuery("#"+containerid).html(ajax_load).load(url);
});
}
</script>
Thanks in advance. I'm really new to jQuery so I may have done something really stupid.
After all the comments received (thanks guys!) I have left only the following:
function fetchContainerContent(url, containerid){
var ajax_load = "loading...";
$("#load_basic").click(function(){$("#"+containerid).html(ajax_load).load(url);});
}
but I'm still having problems as it does not update the page. No js error, nothing happens.
Try this:
jQuery("#load_basic").click(function() {
jQuery("#result").html(ajax_load).load(url);
return false;
});
Note the return false statement at the end of the click handler. This will prevent from propagating the click event in case load_basic is a button or an anchor element.
The only fundamental differences I see are:
You're using a hacky-looking loading string "loading...' />". This doesn't smell good.
You're hardcoding the containerid with "#result" instead of using "#" + containerid.
You're defining the click event in JS code rather than (apparently) inline in the element. How did it originally look like?
For the remnant the code looks fine.
Is the issue that it isn't calling your callback method? You have to had the callback to the .load method.
<script>
function fetchContainerContent(url, containerid) {
jQuery.ajaxSetup ({
cache: false
});
var ajax_load = "loading...' />";
jQuery("#load_basic").click(function() {
jQuery("#result").html(ajax_load).load(url, null, requestContainerContent);
return false;
});
}
function requestContainerContent(responseText, textStatus, XMLHttpRequest) {
// do replacement in here
}
</script>
You'll have to adjust the code a bit in your requestContainerContent to do what you need it to do with the arguments provided.
OK, I seem to have gotten it working, even if I'm not too sure about the quality of the code.
var ajax_load = "loading...";
$("#"+containerid).html(ajax_load).load(url);

Categories

Resources