I'm doing an Ajax query that pulls back a JSON array filled with ~50 urls to images. I then use a forEach on the array to produce a <div> nested with other <div>, with one of those <div> holding an <img> element in where I set the src equal to the current url in the forEach iteration.
The catch is that some of the urls are dead, and result in broken <img> elements ( you know, with the little images of a ripped file?). So what I do is, during the iterations, when I create my <img> element, I set the onerror attribute equal to "$(this).closest('.gramDiv').remove()", which attempts to delete a certain <img> parent element and all of that parent's childrens. This works, but I feel like it's kinda hacky, and not best practice. Is there more standardized way of doing this?
$(document).ready(function(){
console.log("Ready");
function adder(data,status){
data.forEach((url,index)=>{
let attrObj = {
src: url,
alt: 'photo ' + index,
onerror: "$(this).closest('.targetDiv').remove()"
};
let img = $('<img>').attr(attrObj);
let targetDiv = $('<div></div>').addClass('target');
let picDiv = $('<div></div>').addClass('picDiv').append(img);
let component = targetDiv.append(picDiv);
$('#bodyDiv').append(component);
})
}
$.ajax({
url:'https:/blahblahblah.json',
dataType: 'json',
success: adder,
timeout: 3000,
error: function(){ alert('error retrieving')}
});
})
You can try to use this code
$("<img/>")
.on('load', function() { console.log("image loaded correctly"); })
.on('error', function() { console.log("error loading image"); })
.attr("src", $(originalImage).attr("src"))
;
From here
Thanks to #SabirAmeen and this link for this answer.
Basically, within the forEach block, you want to run another ajax call on the URL of the current iteration, and retrieve it's status, which indicates whether it's working or broken. In my case, a working status is 200, and anything else I consider broken.
I've simplified my code from above, but it still illustrates the point:
$(document).ready(function(){
console.log("Ready");
//event.preventDefault();
console.log("pressed");
function adder(data,status){
data = data.reduce(function(a,b){
if(!a.includes(b)) a.push(b);
return a;
},[]);
data.forEach((url,index)=>{
UrlExists(url, function(status){
if(status === 200){
// file was found
console.log('found:',status);
let img = $('<img>').attr('src',url);
$('body').append(img);
}
else {
// do nothing
console.log('not found:',status);
}
});
})
}
function UrlExists(url, cb){
jQuery.ajax({
url: url,
dataType: 'text',
type: 'GET',
complete: function(xhr){
if(typeof cb === 'function')
cb.apply(this, [xhr.status]);
}
});
}
$.ajax({
url:'https://yaddayadda.json',
dataType: 'json',
// success: adder,
success: adder,
//complete: remover,
timeout: 3000,
error: function(){ alert('error retrieving data fam')}
});
})
Related
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');
}
});
});
I have a following ajax operation that is intended to (1) show spinner gif before sending ajax request, and after after the request is complete, (2) hide the gif and 3 display appropriate alert messages.
Finally (4) reload the page.
Here's the code:
$.ajax({
url: rUrl,
data: {
id: rID,
requisitionStatus: rStatus,
comment: rComment
},
type: "POST",
cache: false,
beforeSend: function() {
$("#requisitionStatusDialog").dialog('close');
$('#ajax_loader_my').show();
},
success: function(data, resp) {
var json = data;
var obj = JSON && JSON.parse(json) || $.parseJSON(json);
if (obj.status == "success") {
alert('Success! ' + obj.message);
location.reload();
} else if (obj.status == "error") {
alert('Error!' + obj.message);
}
},
error: function(data, resp) {
$("#updateDialog").dialog('close');
console.log(resp);
},
complete: function() {
$('#ajax_loader_my').hide();
}
});
But in this case, alert pops up first while the spinner gif still shows up, and reloads the page after clicking OK.
I even tried hiding the gif in success callback itself instead of using complete:
success: function(data, resp) {
var json = data;
var obj = JSON && JSON.parse(json) || $.parseJSON(json);
if (obj.status == "success") {
$('#ajax_loader_my').hide();
alert('Success! ' + obj.message);
location.reload();
} else if (obj.status == "error") {
alert('Error!' + obj.message);
}
},
Both gives the same result.
The reason your alert pops up before the spinner is hidden is the success code runs before complete which hides the spinner. The reason it reloads is because after the alert you send location.reload();
Check that $('#ajax_loader_my').hide(); is actually hiding the spinner. The element that is or contains the spinner in your html must be have its id set to ajax_loader_my.
If you open Chrome or Firefox Dev tools you should be able to send $('#ajax_loader_my').hide() and see what happens.
Rewrite the code this way, this will put your alert and location related code in event queue which will run when it will be free.
if(obj.status=="success") {
$('#ajax_loader_my').hide();
setTimeout(function(){
alert('Success! '+obj.message);
location.reload();
},0);
}
Hi you should try to use promises here is the documentation Jquery promises, I made this on the fly it can have some errors but is just an example:
$( function() {
function AjaxCall(rID,rStatus,rComment){
return $.ajax({
url: 'request.php',
data: {
id: rID,
requisitionStatus: rStatus,
comment: rComment
},
type: "POST",
cache: false,
beforeSend: function() {
$("#requisitionStatusDialog").dialog('close');
$('#ajax_loader_my').show();
}
})
}
$( "#requisitionStatusDialog" ).dialog();
$("#yourbuttonInputId").on('click',function(event) {
AjaxCall().done(function(data,response){
var obj = JSON.parse(data);
if (obj.status == "success") {
alert('whe are on done!');
}
}).fail(function(data,response){
$("#updateDialog").dialog(' close');
}).always(function(data){
if(confirm('You have finished the request. Click OK if you wish to continue ,click Cancel to reload the page.'))
{
$('#ajax_loader_my').hide();
$("#requisitionStatusDialog").dialog('open');
}else{
location.reload();
}
});
});
} );
EDIT: Check this jsfiddle it will guide you to elaborate your code
Hope it Helps
I would rather suggest to use an empty div or span with an Id.
Than display success in the html of that div.
For Example:
$('#ajax_loader_my').hide();
setTimeout(function () {
$('#successDiv').html('Success! ' + obj.message);
location.reload();
}, 2000);
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')
I'm building a page that uses the Slideshare API to pull a limited set of presentations from a specific Slideshare user and output a series of iframes with those presentations embedded.
For the most part the page is working as intended, however one of the presentations that the API is calling was recently deleted, and therefore when the page loads that iframe the browser returns a 410. Unfortunately there is no object in the XML returned by Slideshare that can be used to determine if that particular presentation has been deleted.
Therefore I would like to check for the 410 error prior to adding that particular iframe to the page. What's making this difficult for me is that the error doesn't appear when making the API call, but rather when the embedded iframe loads, so I don't know where or how to make the appropriate check.
Here's the relevant code:
$.ajax({
url: slideShareURL,
dataType: 'XML',
success: function(data) {
var allSlides = x2js.xml2json(data);
allSlides = allSlides.User.Slideshow;
for (i = 0; i < allSlides.length; i++) {
document.getElementById('slides').innerHTML += allSlides[i].Embed;
}
}
});
Notes:
slideShareURL is defined earlier in the code using the API
I'm using xml2json.js to convert the XML received from the API to a json object
allSlides is an array of presentations each containing a set of objects such as slide title, id, download URL, etc
Embed is the object that contains the full iframe embed code
EDIT: Answer provided by Christophe below was almost perfect, but I had to add a closure to make the second ajax call work within the for loop.
Hmmm,what about this solution :
$(function() {
$.ajax({
url: "www.domain.com/iframe",
dataType: "jsonp",
timeout: 5000,
success: function () {
$("#iframe").attr("src", "www.domain.com/iframe");
},
error: function (parsedjson) {
if(parsedjson.status == "200") {
$("#iframe").attr("src", "www.domain.com/iframe");
} else {
// Handle error
}
}
});
});
The answer provided by Christophe put me on the right track, but it wasn't possible to run the second ajax call properly within the for loop. After some more research I learned about closures, and came up with the following:
$.ajax({
url: slideShareURL,
dataType: 'XML',
success: function(data) {
var allSlides = x2js.xml2json(data);
allSlides = allSlides.User.Slideshow;
for (i = 0; i < allSlides.length; i++) {
(function(i) {
var slideURL = allSlides[i].SlideshowEmbedUrl;
$.ajax({
url: slideURL,
success: function () {
document.getElementById('slides').innerHTML += allSlides[i].Embed;
},
error: function () {
console.log('Failed to load');
}
})
})(i);
}
}
});
here's my code:
new Ajax.Updater('container', url, {
method: "get",
onLoading: function () {
document.getElementById('container').innerHTML = "Loading...";
},
on200: function(response) {
if(response.responseText.match(/WhatImLookingFor/)) {
window.location = "leNewURL";
}
},
onComplete: function(response) {
//do other cool stuff
}
});
What I'm trying to do is intercept the response before container gets updated and if the text in the response matches WhatImLookingFor, I want to redirect the user to leNewURL. With the code above this is happening, but not before the update, so it looks quirky. Is there anyway that I can intercept right before the update happens or do I have to resort to other hacks like hiding container and show it only if there's no match?
If you want to customize the behavior of your Ajax call like that I would recommend using the base Ajax.Request() http://api.prototypejs.org/ajax/Ajax/Request/
new Ajax.Request(url, {
method: "get",
onLoading: function () {
$('container').update("Loading...");
},
onComplete: function(response) {
if(response.responseText.match(/WhatImLookingFor/)) {
window.location = "leNewURL";
}
else {
//do other cool stuff
$('container').update(response.responseText);
}
}
});
I swapped out the document.getElementById() with the $() utility method as its less to type and includes all of PrototypeJS's Element methods