I've an API that I'm connecting to. I've an index.html page with two columns in it. The left column contains thumbnails of images (see Part A) and the right column will contain details of information about the images once it is clicked on. There are currently two AJAX calls (both calling different URLs) within the same JS script file.
Part A
$.ajax({
url: 'http://gateway.marvel.com:80/v1/public/characters?limit=5&offset=300&apikey=' + publickey + "&ts=" + ts + "&hash=" + hash,
method: 'get',
success: function (res) {
var characters = res.data.results;
var index = 0;
loadCharacters();
function loadCharacters() {
if (index > characters.length) {
index = 0;
return;
}
for (index = 0; index < characters.length; index++) {
var hero = characters[index];
var heroID = hero.id;
var heroName = hero.name;
var image = hero.thumbnail;
var image_url = image.path + '.' + image.extension;
var image_target = $("#comics")
$('<img>').attr({
src: image_url,
width: 80,
height: 80,
}).appendTo($('<a>').attr({
href: '#?' + heroID,
//}).click(dummyCall(hero.id)).appendTo(image_target)); //NOTE: to figure how to pass this hero.id to second ajax call..
}).appendTo(image_target));
image_target.append("<br/>" + heroName + "<br/>");
}
} // end first ajax
}
});
Part B
$.ajax({
url: "https://gateway.marvel.com:443/v1/public/characters/1009269/comics?limit=5&apikey=" + publickey + "&hash=" + hash + "&ts=" + ts,
method: 'get',
success: function (res) {
var comicDetails = res.data.results;
var index = 0;
loadComic();
function loadComic() {
if (index > comicDetails.length) {
index = 0;
return;
}
for (index = 0; index < comicDetails.length; index++) {
var comic = comicDetails[index];
var comicTitle = comic.title;
$("#comics").append("<br/>" + comicTitle + "<br/>");
}
}
}
});
Part B is still incomplete, but for the purpose of this question, it should suffice. I need to know how I can trigger Part B once the user clicks on the anchored image hyperlinks in the left sidebar. I don't want Part B to load immediately because Part B is dependent on the hero.id (from Part A) being clicked and passed to it.
You'll want to have a click handler for the images. You can do this in a couple ways.
Either directly
$imageElements.on('click', functionThatCallsAjaxB);
Or indirectly with a delegate
$("#comics").on('click', 'img', functionThatCallsAjaxB);
The difference being that the first puts a handler on each image and the image has to exist when you execute that line. The second is a single handler attached to the parent that reacts when one of this children has that event.
I also see in your ajax A that your wondering how to pass information to the second ajax call. One way you can do this is with data elements.
$element.data('heroId', 'myvalue'); <-- setter
$element.data('heroId') <-- getter
So you can set them on the images when you create them and reference them later.
An assumption on my part, lets assume that hero id is a unique value for each image. If so, in you loop,
$('<img>').attr({
src: image_url,
width: 80,
height: 80,
id: hero.id,
...
Because you are assigning a click handler, you don't need the '< a>'.
Elaborating on Taplar's answer a little more, $("#comics").on('click', 'img', functionThatCallsAjaxB); after the looping is done (you only have to do it once.
If you do it this one...
$("#comics").on('click', 'img', function(evtObj){ functionThatCallsAjaxB(evtObj);});
with function
functionThatCallsAjaxB(obj) {
// the image that was clicked will be obj.target.
// with the above assumption,
var heroID = obj.target.id;
// the rest of your part b ajax goes here.
}
Related
I am developing a chrome extension and I need to read information from a page, insert the data into the database table and then move on to the next page and do the same thing.
The problem is that the function which inserts the data (using ajax) inserts 6 rows out of 45 before moving to the next page, which means that we are moving to the next page without inserting the rest of the data.
What I want it to do is to respect code order and insert all the rows into the database and then move on to the next page.
the code is as follows :
for (elt of an) {
var t = elt.getElementsByClassName('annonce_titre');
if ((elt.id !== '')) {
//console.log(().getElementsByTagName('a')[0].href);
let titleH2 = t[0].getElementsByTagName('h2');
let titleLink = t[0].getElementsByTagName('a');
var url = titleLink[0].href;
var title2 = titleH2[0].innerHTML;
var last_item = 0;
var w = elt.getElementsByClassName('titre_wilaya')[0].innerHTML;
console.log(w);
var wilaya = w.substring(w.length - 2, w.length);
console.log("leg0 leng " + (w.length - 2) + " ** " + w.length)
console.log("wilaya " + wilaya)
if (isNumber(wilaya)) {
var city = w.substring(0, w.length - 4);
} else {
var city = w;
wilaya = 16;
}
console.log("w c " + wilaya + " ** " + city)
var num = (elt.id).substring(4, 20)
// ADD DELAY OF 5 SECONDS BETWEEN PAGE LOADS
var logger = setInterval(logNextTitle, 10);
var inserts = [
[title2, wilaya, city, url, num]
];
test = test.concat(inserts);
console.log('test spead ');
$.ajax({
data: {
link: url,
h2: title2,
field: "auto",
last: last_item,
numero: num,
wilaya: wilaya,
city: city,
items: test
},
type: "post",
url: "http://localhost/insert.php",
success: function(data) {
console.log("Data Save: " + data);
}
});
}
}
//window.open(first_link ,'_SELF');
console.log("first_link " + first_link)
What this code does is loop through all the elements of an array , insert the data into the DB using ajax and then moving to the next page .
https://imgur.com/Rw4xrMq
This console shows that echoing "first_link" in the code is after the code for insertion , but the later is executed after the echo. There is a miss-order in javascript
Basics of asynchronous calls is they start and the code moves on. Problem with your code is you assume they are all done when they are still queued up waiting to be made. So you need to wait for them to all be done before you do it. Promises make it easier to do that. So look at using jQuery's when.
var ajaxCalls = []
for (elt of an) {
...
ajaxCalls.push($.ajax({...}))
}
$.when.apply($, ajaxCalls).then( function () { console.log('done'); } );
// $.when(...ajaxCalls).then( function () { console.log('done'); } );
I'm trying to perform the following actions at a high level:
Make an API call to generate an array of cardIDs for a Trello board
Iterate and make an API call for each cardID, pull some action (history) data for each card and store that in the action_tableData table
Once complete, append action_tableData to the table object
The way I understand things is that I need to perform a getJSON call, let that finish and then perform another getJSON on top of that. I've looked around and understand the $.when() function might help, but I can't seem to even remotely make that work.
I think there could be an issue when the getJSON returns an undefined object, but I'm not sure how to skip those. Any help would be most appreciated!
cardIDapiCall = "https://api.trello.com/1/boards/" + boardID + "/cards?fields=id&key=" + appKey + "&token=" + tokenKey;
$.getJSON(cardIDapiCall, function(cardID) {
var cardID_tableData = [];
// Iterate over the JSON object
for (var i = 0, len = cardID.length; i < len; i++) {
cardID_tableData.push({
"card_id": cardID[i].id,
});
}
//get action data
for (var j = 1, len = cardID_tableData.length; j < len; j++) {
//this alert works
alert("card id: " + cardID_tableData[j].card_id);
var actionapiCall = "https://api.trello.com/1/cards/" + cardID_tableData[j].card_id + "/actions?key=" + appKey + "&token=" + tokenKey;
//why isn't this getting called???
$.getJSON(actionapiCall, function() {
//this alert doesn't work
alert("action call successful");
});
var action_tableData = [];
action_tableData.push({
"action_id": action[j].id,
"action_type": action[j].type,
})
table.appendRows(action_tableData);
doneCallback();
}
});
In the below example we read from a JSON data and store it in a variable.
Then we loop through it to print out the value of each of its iteration.
I have "cartList.innerHTML" which will list out "Edit" 4 times as that is the number of the objects in the array. Like below
Edit
Edit
Edit
Edit
Once you click on the first Edit a modal should open and display the name of the first object, on clicking the second edit the name of the second and so on.
But for some reason the value remains to be the name of the last object for each Edit. How do I get it to print the correct name for each edit.
// Fetch JSON data
function loadJSON(file, callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', file, true); // Refers to JSON file
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
}
xobj.send(null);
}
function load() {
loadJSON("assets/cart.json", function(response) {
var actual_JSON = JSON.parse(response);
console.log(actual_JSON);
var cartList = document.getElementById("cart-products");
var itemObj = actual_JSON.productsInCart;
var itemLength = actual_JSON.productsInCart.length;
// Loop through JSON data
for (var i = 0; i < itemLength; i++) {
(function(i) {
/* Output Link Element with Edit text & on click
display a modal containing current value of iteration */
cartList.innerHTML += '<div>'+
'Edit'+
'</div>';
var editCartModal = document.getElementById("edit-cart");
editCartModal.innerHTML = itemObj[i].p_name;// Name of the current object
})(i);
// for loop ends here
}
})
}
load(); // Loads function on page load
This happens because every function inside the for loop begins to execute after the loop ends. It's the same behavior as for callbacks. When you call each (function(i) {})(i) they all go to the end of function call stack, and only after last iteration of the loop they start executing (with the last i value).
I'm curious why do you need an internal inline function at all? Why don't just put this code directly to the loop body?
It's because though you are running the following code in an IIFE:
var editCartModal = document.getElementById("edit-cart");
editCartModal.innerHTML = itemObj[i].p_name;// Name of the current object
You're just modifying the same modal (getElementById()) four times.
A simple answer to fix this would be adding a data attribute:
for (var i = 0; i < itemLength; i++) {
(function(i) {
/* Output Link Element with Edit text & on click
display a modal containing current value of iteration */
cartList.innerHTML += '<div>'+
'<a href="javascript: void(0);"' +
'class="btn btn-outline button-edit" data-toggle="modal"' +
'data-target=".bs-example-modal-lg" data-custom-value="' + i +
'">Edit</a>'+
'</div>';
})(i);
// for loop ends here
}
/* ES5 way*/
/*
var list = document.getElementsByClassName('button-edit');
list.forEach(function(){
...
})
*/
[...document.getElementsByClassName('button-edit')].map(function(element){
element.addEventListener('click', function(e){
var chosenIdx = this.dataset.customValue;
var editCartModal = document.getElementById("edit-cart");
editCartModal.innerHTML = itemObj[chosenIdx].p_name;// Name of the current object
}, false);
});
I'm looping a set of div's conaining an id for YouTube videos to get and show their titles, descriptions, etc. The divs I loop through look like this:
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
For some reason the loop adds all of the info to the last of these divs instead of applying the info to the current div inside the loop. Any idea why this is?
And also, as a sub question, why can't I reach i inside the ajax call? I have to set tutorial = tutorials[i] and append the children to tutorial instead of tutorials[i] but I can't use just tutorials[i] within the ajax part. I can't understand what the difference is.
(function () {
var tutorials = document.getElementsByClassName('tutorialVideoDataContainer');
for (var i = 0; i < tutorials.length; i++) {
var tutorial = tutorials[i];
console.log(i);
console.log(tutorial);
console.log(tutorials[i]);
var id = tutorial.getAttribute('data-clipid');
$.ajax({
url: "http://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=json",
dataType: "jsonp",
success: function (data) {
var title = data.entry.title.$t;
var description = data.entry.media$group.media$description.$t;
var duration = data.entry.media$group.media$content[0].duration;
var thumbnail = data.entry.media$group.media$thumbnail[3].url;
if (title.length > 0) {
var titleElement = document.createElement("h3");
titleElement.innerText = title;
tutorial.appendChild(titleElement);
}
if (description.length > 0) {
var descriptionElement = document.createElement("p");
descriptionElement.innerText = description;
tutorial.appendChild(descriptionElement);
}
var minutes = Math.floor(duration / 60);
var seconds = duration - minutes * 60;
var durationElement = document.createElement("p");
durationElement.innerText = minutes + ":" + seconds;
tutorial.appendChild(durationElement);
var clickLink = document.createElement("a");
clickLink.className = "showOverlayVideo";
var thumbnailElement = document.createElement("img");
thumbnailElement.src = thumbnail;
thumbnailElement.alt = title;
clickLink.appendChild(thumbnailElement);
tutorial.appendChild(clickLink);
}
});
}
})();
If you loop over array and crate closure where you access the index of the array you access reference to variable not copy of it so when all asynchrous code is finished you end up with reference to last element. To fix it you need to create another closure with new reference:
for (var i = 0; i < tutorials.length; i++) {
(function(tutorial) {
// you can use tutorial inside
})(tutorials[i]);
}
Because of the way the javascript event loop works, the loop finishes running before your ajax requests come back. So by the time they do the variable tutorial is set to the last one in the loop.
Does the data you get back from the youtube api include the clip ID, you could then use that to look up the correct dom element in the callback rather than inside the loop.
My guess is that since $.ajax() is not a blocking function, the loop is finishing before ajax is done doing it's thing. This would cause the execution path to look something like this:
Loop starts, i = 0
Ajax call is made
i = 1
i = 2
...
i = tutorials.length
Ajax call finishes
Hope that helps / makes sense.
I am currently, trying to National Library of Australia's API to find pictures on a specific search term. Trove API I have the following functions which should send a query from an input form to the api and receive images back, however I am not receiving the majority of the images. In a particular example, if is search for 'sydney' I am only receiving 3 images back when there is in fact way more. For instance, this is the json, that is returned. I know that you will not be familiar with this api, but in my code below, is there anything that you can see, that would be causing it not to return all the images? I have changed a few things around to try and find the problem as well as put a few console.log statements but it is still not being kind to me.
var availableImages = {
"nla": {
"numImages":0,
"url_pattern":"nla.gov.au",
"images":[]
},
};
var url_patterns = ["nla.gov.au"];
$(document).ready(function(){
$("form#searchTrove").submit();
$("form#searchTrove").submit(function() {
resetImageData();
//get input values
var searchTerm = $("#searchTerm").val().trim();
searchTerm = searchTerm.replace(/ /g,"%20");
var sortBy = $("#sortBy").val();
//create searh query
var url = "http://api.trove.nla.gov.au/result?key="
+ apiKey + "&l-availability=y%2Ff&encoding=json&zone=picture"
+ "&sortby=relevance&n=100&q=" + searchTerm + "&callback=?";
//print JSON object
console.log(url);
//get the JSON information we need to display the images
$.getJSON(url, function(data) {
$('#output').empty();
$.each(data.response.zone[0].records.work, processImages);
//console.log(data);
printImages();
});
});
});
function processImages(index, troveItem){
console.log("av"+ availableImages);
for(var i in availableImages){
//console.log(availableImages[i].url_pattern)
if(troveItem.identifier[0].value.indexOf(availableImages[i].url_pattern) >= 0){
console.log("Trove URL "+troveItem.identifier[0].value+" Pattern: "+availableImages[i]["url_pattern"]);
availableImages[i].numImages++;
availableImages.totalimages++;
availableImages[i]["images"].push(troveItem.identifier[0].value);
}
}
}
function printImages(){
$("#output").append("<h3>Image Search Results</h3>");
for(var i in availableImages){
if(availableImages[i]["url_pattern"]=="nla.gov.au" && availableImages[i]["numImages"]>0){
printNLAImages();
console.log(availableImages);
}
}
}
function printNLAImages(){
$("#output").append("<h3>National Library of Australia</h3><p>"
+availableImages["nla"]["numImages"]+" images found from <a href='http://"
+availableImages["nla"]["url_pattern"]+"'>"
+availableImages["nla"]["url_pattern"]+"</a></p>");
for (var i in availableImages["nla"]["images"]){
$("#output").append("<img src='"+availableImages["nla"]["images"][i]+"-v'>");
}
console.log(availableImages);
}
function resetImageData(){
availableImages.totalimages = 0;
for (var i in availableImages){
availableImages[i].numImages = 0;
availableImages[i]["images"] = [];
}
console.log(availableImages); //displaying hee
}