Making async jquery calls - javascript

$(document).ready(function(){
$.getJSON("https://api.deckbrew.com/mtg/sets", function(sets) {
$(sets).each(function() {
$('<div id="' + this.name + '" class="set"/>')
.text(this.name)
.appendTo("#collection");
});
});
$.getJSON("https://api.deckbrew.com/mtg/cards", function(cards) {
$(cards).each(function(){
$('<div id="' + this.name + '" class="card"/>')
.text(this.name)
.appendTo("#" + this.editions[0].set);
});
});
});
I was wondering how I might (without using ajax and sticking to the "getJSON" method) make the two calls happen asynchronously. I can't make anything useful happen with the second jQuery object; I believe that's because of the synchronous nature of the calls. How can I make them work in order?

If you want these to happen in order, then you need to specifically serialize them and using the built-in promises that getJSON() returns is a simple way to do that:
$(document).ready(function () {
$.getJSON("https://api.deckbrew.com/mtg/sets").then(function (sets) {
$(sets).each(function () {
$('<div id="' + this.name + '" class="set"/>')
.text(this.name)
.appendTo("#collection");
});
}).then(function () {
$.getJSON("https://api.deckbrew.com/mtg/cards").then(function (cards) {
$(cards).each(function () {
$('<div id="' + this.name + '" class="card"/>')
.text(this.name)
.appendTo("#" + this.editions[0].set);
});
});
});
});
Or, a little faster (end to end time) would be to launch both requests at the same time and then process the results in order. Again using jQuery promises to manage this:
$(document).ready(function(){
$.when(
$.getJSON("https://api.deckbrew.com/mtg/sets"),
$.getJSON("https://api.deckbrew.com/mtg/cards")
).then(function(r1, r2) {
// process sets
var sets = r1[0];
$(sets).each(function() {
$('<div id="' + this.name + '" class="set"/>')
.text(this.name)
.appendTo("#collection");
});
// process cards
var cards = r2[0];
$(cards).each(function(){
$('<div id="' + this.name + '" class="card"/>')
.text(this.name)
.appendTo("#" + this.editions[0].set);
});
});
});
This last scheme uses $.when() to tell us when both ajax calls are done and it also sequences the results for us, regardless of which one actually finished first.

To run the getJSONS's in sequence, run the second in the callback of the first
like so
$(document).ready(function() {
$.getJSON("https://api.deckbrew.com/mtg/sets", function(sets) {
$(sets).each(function() {
$('<div id="' + this.name + '" class="set"/>')
.text(this.name)
.appendTo("#collection");
});
$.getJSON("https://api.deckbrew.com/mtg/cards", function(cards) {
$(cards).each(function() {
$('<div id="' + this.name + '" class="card"/>')
.text(this.name)
.appendTo("#" + this.editions[0].set);
});
});
});
});
personally, I would go with #jfriend00's promise method - I was going to add that to this answer, but he answered in the meantime, so, go with that more flexible method

EDIT
Since you said you were trying to use call both getJSON methods in order, then you can make the second call work after the first by using the DOMNodeInserted event
Well maybe a solution would be to use DOMNodeInserted event since you are appending to #collection
so:
$("#collection").on('DOMNodeInserted',function(){
$.getJSON...
});
According to DOCS
DOMNodeInserted
Fired when a node has been added as a child of another node. This
event is dispatched after the insertion has taken place. The target of
this event is the node being inserted.

Related

Elements created by getJSON don't react to the rest of the javascript loaded on the page

I am using getJSON to access Vimeo's Simple API, and any objects created on the page by the call, do not react to the rest of the javascript that is on the page. It is probably something simple that I am missing. Here is my getJSON code:
$.getJSON("http://vimeo.com/api/v2/album/1822727/videos.json", function(data){
$.each(data, function (index, value) {
var videoID = value.id;
var videoThm = value.thumbnail_large;
$('#galThms').prepend('<li id="thm' + videoID + '" style="background-image:url(' + videoThm + ');"></li>');
console.log(videoThm);
});
});
Here you go: http://jsfiddle.net/8t3Xq/1/
This demonstrates loading your <li> thumbs just as your question does, then I show how to easily change one of them. How to "change" them is endless, this is just a simple example of changing the content and background. So you must not have your selectors right.
This is just a snippet, see fiddle for everything...
$.getJSON("http://vimeo.com/api/v2/album/1822727/videos.json", function(data){
$.each(data, function (index, value) {
var videoID = value.id;
var videoThm = value.thumbnail_large;
$('#galThms').prepend('<li id="thm' + videoID + '" style="background-image:url(' + videoThm + ');"></li>');
console.log(videoThm);
});
});
window.changeIt=function()
{
$('li').first().html("I'm changed!");
$('li').first().css("background-image","");
}
Just make sure the <li>s are present first before your code that changes them is present. Would need to see more of you code to understand when/how that happens.
$.getJSON("http://vimeo.com/api/v2/album/1822727/videos.json", function(data){
$.each(data, function (index, value) {
var videoID = value.id;
var videoThm = value.thumbnail_large;
$('#galThms').append('<li id="thm' + videoID + '" style="background-image:url(' + videoThm + ');"></li>');
console.log(videoThm);
$( "#galThms li" ).click(function() {
$(this).hide();
});
});
});
try this
there is no way that my answer is so far removed from the problem statement. my guess is that either I somehow errantly posted this answer or the problem was edited. apologies
you could also use:
$(document).on('click','li .playVideo',function(){
//do something
});
i would probably change your #playVideo to a class, if you will have multiple li's

Issue with binding event handler to dynamically generated divs

I have a problem with displaying the id of the clicked div on the screen in an alert window. I am pretty confident this is because of the order of the controls and event handlers being added to the page, however after trying different ways I am unable to get this to work. Unfortunately I can't post reproducible code due to the div's being created from an ajax get request.
$(document).ready(function () {
$.getJSON('ClientPortal/GetSkills', function (data) {
var test = 'poo';
$.each(data, function (data) {
$('#flipContainer').append("<div class=flip id='" + this.Value + "' value='" + this.Value + "'>" + this.Text + "<//div>");
})
})
})
$(document).ready(function () {
$(".flip").on('click', function () {
alert($(this).attr("id"));
})
})
Try this :
$(document).ready(function () {
$("#flipContainer").on("click", ".flip", function () {
alert($(this).attr("id"));
})
})
You effectively have to base you click-binding on an element existing when the DOM ready event fires... But with this syntax, you delegate the binding on an existing element but it applies to another element contain in the first one...
See the jQuery documentation for on (for line "selector") : http://api.jquery.com/on/#on-events-selector-data-handlereventObject .

How to write jquery selectors for dynamically generated html elements?

I'm currently writing an object oriented module which assigns callback to dynamically generated elements.
function Instant(containerID) {
this.var1 = 0;
this.var2 = 0;
this.containerID = containerID;
// and more variables...
};
And here containerID is the id of a DIV which is dynamically generated. I populate this DIV via Ajax Request which reads a file like the following:
<!-- content.html -->
<div class="general_container">
<div class="top_container">
<!-- plenty of divs, spans etc -->
</div>
<div class="tweet_section">
<!-- plenty of divs, spans etc -->
</div>
</div>
Now the important part is, I assign all callbacks like the following:
Instant.prototype.addCallbacks = function() {
$(this.containerID + " bar").click(function() {
$(this.containerID + " bar").foo();
});
$(this.containerID + " bar").click(function() {
$(this.containerID + " bar").foo();
});
$(this.containerID+ " bar").click(function(e) {
$(this.containerID + "bar, " + this.containerID+ " bar").foo();
});
});
As you see, I always have to put this.containerID before each selector to assign events. (Therefore, I make sure I'm selecting only one element) Now, my code is full of clutter as I have plenty of this.containerIDs. I don't know if there is a smarter method to make my code easy. Any help will be appreciated.
Here is a sample JSFiddle.
Note that this is not my real module, I just made it up to make it clear!
Then you shouldn't be using IDs. You should be using classes instead.
It would take long to edit your code, but here's a hint: Add a handler to the parent. Use event delegation, like .on(). Then have it listen for all children, now or future.
Create a separate java script file and put your add callbacks function in there and just pass the containerID. That way, you can re-use it later. However, looks like you cannot get rid of containterID since you will be needing that to do your add, subtract, save etc..
in your current file shown as above,
Instant.prototype.addCallbacks = createAddCallbacks(this.ContainerID);
create addCallbacks.js
function createAddCallbacks(containerId)
{
Instant.prototype.addCallbacks = function() {
$(containerId + " bar").click(function() {
$(containerId + " bar").foo();
});
$(containerId + " bar").click(function() {
$(containerId + " bar").foo();
});
$(containerId+ " bar").click(function(e) {
$(containerId + "bar, " + containerIdD+ " bar").foo();
});
});
}
Like #JosephTheDreamer said, use Event Delegation. (Jquery.fn.on)
Using event delegation you set one handler to multiple targets. It means just one handler in memory and dynamic event handlers set.
I made a demonstration modifying your code, take a look...
Instant.prototype.addCallbacks = function () {
var selfContainer = null, // DOMElement container
me = this; // Object reference
$('body').on("click", ".selection_container .btn-add", function () { //Using event delegation
selfContainer = $(this).parents(".general_container"); //set DOMElement
selfContainer.find("input[name=currentValue]").val(++me.instantValue);
});
$('body').on("click", ".selection_container .btn-subtract", function () {
selfContainer.find("input[name=currentValue]").val(--me.instantValue);
});
$('body').on("click", ".selection_container .btn-reset", function () {
me.instantValue = 0;
selfContainer.find('input[name=currentValue]').val(0);
});
$('body').on("click", ".selection_container .btn-save", function () {
me.savedValue = me.instantValue;
});
$('body').on("click", ".selection_container .btn-load", function () {
me.instantValue = me.savedValue;
selfContainer.find('input[name=currentValue]').val(me.savedValue);
});
};
Hope it helps...
So, I think I find a better method according to this post
I wanted to limit the scope of my selector.
Firstly, I'll create a jQuery instance variable
function Instant(containerID) {
this.var1 = 0;
this.var2 = 0;
this.container= $('#'+containerID);
// and more variables...
};
and adding a new prototype like this
Instant.prototype.$ = function(selector){
return this.container.find(selector);
};
I'll only use this.$(selector) function which is better.

Getting reference to element that invoked inline function with JQuery

I am inserting elements into the DOM populated with some data I retrieved from a web service. I attached an inline click event to call a function when invoked. The problem is I am not getting a reference to the element that invoked that function.
Code that appends the new elements:
$.getJSON("/search?" + $(this).serialize(), function (data) {
if (data != null) {
$.each(data, function (index, video) {
resultItem.append("<li ><a onclick='loadNewVideo(e)' href='play?video=" + video.video_id + "'>" + "<img width='185' src='" + video.defaultImg + "'/>" + "<span class='video_left_title'>" + video.song.song_name + "<h6 class='artist_name'>" + video.artist.artist_name + "</h6></span></a>");
});
}
});
Function:
function loadNewVideo (e) {
e.preventDefault();
alert($(this).attr("href"));
}
Instead of using inline event handlers, you could delegate the clicks on all a to resultItem:
// Call this only once, when resultItem is already in the DOM
// (for example, on a document.ready callback)
resultItem.on('click', 'a', loadNewVideo);
// Proceed with your current code (slightly modified):
function loadNewVideo (e) {
e.preventDefault();
alert($(this).attr("href"));
}
$.getJSON ("/search?" + $(this).serialize(),function (data) {
if (data != null) {
$.each (data,function (index,video) {
resultItem.append("<li ><a href='play?video=" + video.video_id +"'>"
+ "<img width='185' src='"+video.defaultImg +"'/>"
+ "<span class='video_left_title'>"+ video.song.song_name
+ "<h6 class='artist_name'>"+video.artist.artist_name
+ "</h6></span></a>");
});
}
});
Inline onclick handlers don't go through jQuery, which is why you don't have access.
You can either leave those there and change the handler:
function loadNewVideo(e) {
e.preventDefault();
alert($(e.target).attr("href"));
}
Or, and more preferably, don't use the inline handlers. Just give the a elements a class of video (or whatever) and install handlers with jQuery:
...
resultItem.append("<li><a class='video' href=...'")
...
// and elsewhere
$(resultItem).on('click', 'a.video', loadNewVideo);
jQuery's event object allowed me to grab what the documentation calls the 'current DOM element within the event bubbling phase'.
var originatingElement = event.currentTarget;

jQuery $.when with deferreds not working

What I'm trying to do is make some action happen when two simultaneous image loads via ajax are done. To do this I created a custom Deferred to be resolved when the image loads are done.
<div id="i"></div>
$(document).ready(function() {
$('#i').hide();
var imgLoad = loadImgs();
$.when(imgLoad).then(function() {
$('#i').show('slow');
});
});
function loadImgs() {
var dfd = $.Deferred();
var matrix = $.getJSON("https://graph.facebook.com/thematrixmovie");
var pulp = $.getJSON("https://graph.facebook.com/pulpfiction");
$.when(matrix, pulp).then(function(m, p) {
mImg = '<img src=' + m[0].picture + '>';
pImg = '<img src=' + p[0].picture + '>';
$('#i').show().append(mImg + pImg);
dfd.resolve;
});
return dfd.promise();
}
You can try this on JSFiddle.
I've used Eric Hynds post including a working example as a reference but still haven't gotten it to work. Any ideas?
You're using $.when correctly, but you try to call the resolve method like this:
dfd.resolve;
Unlike some other languages, JavaScript doesn't allow you to omit the parentheses in a method call. You just need to add them and your code works correctly!
dfd.resolve();

Categories

Resources