Triggering a jQuery Re-Draw After Getting Data With Ajax - javascript

Good day, all,
Long-time listener, first-time poster...
I have a client who has been promised a seemingly very complex bit of functionality. They want to load the contents of 3 separate pages into one, after a visitor to their site successfully logs in to their account. They want this to happen without a page refresh. Ajax is the solution. I am not, however, experienced with Ajax.
I'm having trouble figuring out how to tell when a $.get command (using jQuery's Ajax commands) has finished loading its content. My approach is to, once the login has been successful, to go and fetch the 3 separate pages, load their XHTML content into variables, and redraw the pages. Below you'll see my pseudo-code. I use "XXItemXX" to stand-in for actual paths. Each resulting page that I'm trying to pull in has a div with class "content" surrounding the data I want to retrieve. The XHTML looks like this:
<html>
<head>
<title>Page Name</title>
</head>
<body>
<div id="header">...</div>
<div class="content">
.
.
.
</div>
<div id="footer">...</div>
</body>
</html>
The jQuery code I've built follows. I'm able to get the form submitted and even get the content back from the various .get commands. The problem is, I can't seem to daisy-chain things as I normally would. I am struggling to figure out how to only fire the jQuery commands to draw the page once all 3 have been successfully retrieved. I'm afraid my biggest stumbling point is how to articulate this when searching with Google to see how others have dealt with this problem. I'm not sure exactly how to describe what I'm trying to accomplish in 10 words or less or in a fashion that will actually return the information I need.
Can anyone help with this? I'm afraid I have too little time and too much to learn.
<script type="text/javascript">
$('XXLoginFormXX').submit(function () {
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function() {
$('<div class="loading">Loading...</div>').insertBefore('XXLoginFormXX').css('position','absolute');
},
success: function(data) {
// On successful login, draw page.
$('.loading').fadeOut('slow');
var dr_editProfileXHTML, dr_accountOrderListXHTML, dr_wishListsXHTML;
$.get('XXPathToEditProfilePageXX', function(data1){
var dr_editProfileXHTML = $('div.content', data1);
});
$.get('XXPathToAccountOrderListPageXX', function(data2){
var dr_accountOrderListXHTML = $('div.content',data2);
});
$.get('XXPathToWishListsPageXX', function(data3){
var dr_wishListsXHTML = $('div.content',data3);
});
$('div.content').fadeOut(function(){
$(this).html(dr_editProfileXHTML);
$('XXEditProfileXHTMLXX').before(dr_accountOrderListXHTML);
$('XXEditProfileXHTMLXX').before(dr_wishListsXHTML);
}).fadeIn();
}
});
return false;
});
</script>
Thank you very much for your time, help, and consideration.
Yours,
Sylvan012

If your problem is to wait that all 3 requests have returned, then:
store the results in variables scoped a bit higher so that each of the callbacks can access them
add a variable drawing in the same scope
in each of the callbacks, check if all 3 variables are non-null and drawing is false
if that's the case, then set drawing to true, and do the work

After working on this with people's generous help, I believe I've gotten it. All my thanks to Dave Briand who taught me about .when and .then.
Following is the pseudo-code I came up with. It seems to be working! Sure there's a lot of clean-up to do, but all three of the pages are now being pulled-in! Whoot!
What do you think of my solution?
<script type="text/javascript">
$('XXLoginFormXX').submit(function () {
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function() {
$('<div class="loading">Loading...</div>').insertBefore('XXLoginFormXX').css('position','absolute');
},
success: function(data) {
// On successful login, draw page.
var Page01XHTML;
var Page02XHTML;
var Page03XHTML;
$.when(
$.get('XXPathToEditProfilePageXX', function(data1){
var Page02XHTML = $('div.content', data1);
}),
$.get('XXPathToAccountOrderListPageXX', function(data2){
var Page03XHTML = $('div.content',data2);
}),
$.get('XXPathToWishListsPageXX', function(data3){
var Page01XHTML = $('div.content',data3);
})
).then(function(Page02XHTML,Page03XHTML,Page01XHTML){
$('.loading').fadeOut('slow');
$('div.content').fadeOut(function(){
$(this).attr('id','MyAccount').html(' ' + Page01XHTML + Page03XHTML + Page02XHTML + ' ').parents('body').find('.content').each(function(){
dr_thisID = $(this).attr('id');
if (dr_thisID != 'MyAccount') {
$(this).appendTo($('div#MyAccount'));
}
}).parents('div#MyAccount').children().each(function(){
dr_thisClass = $(this).attr('class');
if (dr_thisClass != 'content') {
$(this).remove();
}
});
}).fadeIn();
});
}
});
return false;
});
</script>

Related

JQuery - Looping a .load() inside a 'for' statement

I'm not sure if this will actually be possible, since load() is an asynchronous method, but I need some way to basically Load several little bits of pages, one at a time, get some data included in them via JavaScript, and then send that over via Ajax so I can put it on a database I made.
Basically I get this from my page, where all the links I'll be having to iterate through are located:
var digiList = $('.2u');
var link;
for(var i=0;i<digiList.length;i++){
link = "http://www.digimon-heroes.com" + $(digiList).eq(i).find('map').children().attr('href');
So far so good.
Now, I'm going to have to load each link (only a specific div of the full page, not the whole thing) into a div I have somewhere around my page, so that I can get some data via JQuery:
var contentURI= link + ' div.row:nth-child(2)';
$('#single').load('grabber.php?url='+ contentURI,function(){
///////////// And I do a bunch of JQuery stuff here, and save stuff into an object
///////////// Aaaand then I call up an ajax request.
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {digimon: JSON.stringify(digimon)},
dataType: 'json',
success: function(msg){
console.log(msg);
}
////////This calls up a script that handles everything and makes an insert into my database.
}); //END ajax
}); //END load callback Function
} //END 'for' Statement.
alert('Inserted!');
Naturally, as would be expected, the loading takes too long, and the rest of the for statement just keeps going through, not really caring about letting the load finish up it's business, since the load is asynchronous. The alert('Inserted!'); is called before I even get the chance to load the very first page. This, in turn, means that I only get to load the stuff into my div before I can even treat it's information and send it over to my script.
So my question is: Is there some creative way to do this in such a manner that I could iterate through multiple links, load them, do my business with them, and be done with it? And if not, is there a synchronous alternative to load, that could produce roughly the same effect? I know that it would probably block up my page completely, but I'd be fine with it, since the page does not require any input from me.
Hopefully I explained everything with the necessary detail, and hopefully you guys can help me out with this. Thanks!
You probably want a recursive function, that waits for one iteration, before going to the next iteration etc.
(function recursive(i) {
var digiList = $('.2u');
var link = digiList.eq(i).find('map').children().attr('href') + ' div.row:nth-child(2)';
$.ajax({
url: 'grabber.php',
data: {
url: link
}
}).done(function(data) {
// do stuff with "data"
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {
digimon: digimon
},
dataType: 'json'
}).done(function(msg) {
console.log(msg);
if (i < digiList.length) {
recursive(++i); // do the next one ... when this is one is done
}
});
});
})(0);
Just in case you want them to run together you can use closure to preserve each number in the loop
for (var i = 0; i < digiList.length; i++) {
(function(num) { < // num here as the argument is actually i
var link = "http://www.digimon-heroes.com" + $(digiList).eq(num).find('map').children().attr('href');
var contentURI= link + ' div.row:nth-child(2)';
$('#single').load('grabber.php?url=' + contentURI, function() {
///////////// And I do a bunch of JQuery stuff here, and save stuff into an object
///////////// Aaaand then I call up an ajax request.
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {
digimon: JSON.stringify(digimon)
},
dataType: 'json',
success: function(msg) {
console.log(msg);
}
////////This calls up a script that handles everything and makes an insert into my database.
}); //END ajax
}); //END load callback Function
})(i);// <-- pass in the number from the loop
}
You can always use synchronous ajax, but there's no good reason for it.
If you know the number of documents you need to download (you can count them or just hardcode if it's constant), you could run some callback function on success and if everything is done, then proceed with logic that need all documents.
To make it even better you could just trigger an event (on document or any other object) when everything is downloaded (e.x. "downloads_done") and listen on this even to make what you need to make.
But all above is for case you need to do something when all is done. However I'm not sure if I understood your question correctly (just read this again).
If you want to download something -> do something with data -> download another thing -> do something again...
Then you can also use javascript waterfall (library or build your own) to make it simple and easy to use. On waterfall you define what should happen when async function is done, one by one.

Ajax call pulling in random html

I'm having an issue that I can't seem to solve. First thing is first. I'm running this on Ubuntu 14.04 with apache and php5. I am using the netbeans IDE which i originally assumed to be the issue but then i ran it directly from the web root /var/www/html. php is working i already tested it. i can run html pages and php pages from the root. I have developed a few applications already using this computer. Some which use very advanced ajax to calls to google apis. I have no idea why all of a sudden I can't make the simplest ajax call. Anyways the problem is that when I make my ajax call to a php file nothing happens. if i inspect the page in browser, it shows that it pulled in a bunch of random iframes. I have never used an iframe before. I am really hoping that I just need a different set of eyes on this and that it isn't something else.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Index HTML</title>
<meta charset="UTF-8">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$.ajax({
url: "getHTML.php",
data: { "test": "testing"},
type: "GET",
dataType: "xml",
statusCode: {
0: function () {
alert("Thank You");
},
200: function (html) {
$("#page").append(html);
}
}
});
});
</script>
</head>
<body>
<div id="page">The Div</div>
</body>
</html>
getHTML.php
<?php
echo "Hello World";
?>
After running this in mozilla firefox, no iframes are being pulled in. I can only assume it is some bug with chrome. So now to answer the question: why aren't I getting any response from the ajax call?
So I am now getting a response from my ajax calls. Way worse though. I changes my $.ajax to a $.post() and then to a $.get() and now it is return a duplicate of index.html over and over and over in an infinite loop. From what i understand the default url that $.get/post take is the current url. But I added getHTML.php as the url argument. so I am at a loss. I can't get any work done if I can't solve this issue. Can anyone help?
$(document).ready(function(){
$.get("getHTML.php", function(data)
$("body").append(data);
});
});
Its best to use JSON for these requests:
$return_data = array();
if (condition){
$return_data['status'] = 'success';
} else {
$return_data['status'] = 'info';
}
echo json_encode($return_data);
exit();
Remember to specify return data type:
function send() {
var data = $('#signup_form').serialize();
$.ajax({
type: "POST",
url: "signup_process.php",
data: data,
dataType: 'json',
success: function (data) {
alert(data.status);
if (data.status == 'success') {
// everything went alright, submit
$('#signup_form').submit();
} else if (data.status == 'info')
{
console.log(data.status);
$("label#email_error").show();
return false;
}
}
});
return false;
};
Ok so the solution to this issue has nothing to do with any programming. For the iframes, this way a strange google chrome issue, luckily it doesn't affect the visuals as nothing displays. As far as the ajax problem, I was doing it right (tested on a different computer) but the Apache2 server was not responding to ajax requests. It is gonna take some time searching through log files to find a proper solution.

Load .txt file using JQuery or Ajax

How can I fix the script below so that it will work EVERY TIME! Sometimes it works and sometimes it doesn't. Pro JQuery explains what causes this, but it doesn't talk about how to fix it. I am almost positive it has to do with the ajax ready state but I have no clue how to write it. The web shows about 99 different ways to write ajax and JQuery, its a bit overwhelming.
My goal is to create an HTML shell that can be filled with text from server based text files. For example: Let's say there is a text file on the server named AG and its contents is PF: PF-01, PF-02, PF-03, etc.. I want to pull this information and populate the HTML DOM before it is seen by the user. A was ##!#$*& golden with PHP, then found out my host has fopen() shut off. So here I am.
Thanks for you help.
JS - plantSeed.js
var pageExecute = {
fileContents:"Null",
pagePrefix:"Null",
slides:"Null",
init:function () {
$.ajax({
url: "./seeds/Ag.txt",
success: function (data){
pageExecute.fileContents = data;
}
});
}
};
HTML - HEAD
<script type="text/javascript">
pageExecute.init();
</script>
HTML - BODY
<script type="text/javascript"> alert(pageExecute.fileContents); </script>
Try this:
var pageExecute = {
fileContents:"Null",
pagePrefix:"Null",
slides:"Null",
init: function () {
$.ajax({
url: "./seeds/Ag.txt",
async: false,
success: function (data){
pageExecute.fileContents = data;
}
});
}
};
Try this:
HTML:
<div id="target"></div>
JavaScript:
$(function(){
$( "#target" ).load( "pathToYourFile" );
});
In my example, the div will be filled with the file contents. Take a look at jQuery .load() function.
The "pathToYourFile" cand be any resource that contains the data you want to be loaded. Take a look at the load method documentation for more information about how to use it.
Edit: Other examples to get the value to be manipulated
Using $.get() function:
$(function(){
$.get( "pathToYourFile", function( data ) {
var resourceContent = data; // can be a global variable too...
// process the content...
});
});
Using $.ajax() function:
$(function(){
$.ajax({
url: "pathToYourFile",
async: false, // asynchronous request? (synchronous requests are discouraged...)
cache: false, // with this, you can force the browser to not make cache of the retrieved data
dataType: "text", // jQuery will infer this, but you can set explicitly
success: function( data, textStatus, jqXHR ) {
var resourceContent = data; // can be a global variable too...
// process the content...
}
});
});
It is important to note that:
$(function(){
// code...
});
Is the same as:
$(document).ready(function(){
// code
});
And normally you need to use this syntax, since you would want that the DOM is ready to execute your JavaScript code.
Here's your issue:
You've got a script tag in the body, which is asking for the AJAX data.
Even if you were asking it to write the data to your shell, and not just spout it...
...that's your #1 issue.
Here's why:
AJAX is asynchronous.
Okay, we know that already, but what does that mean?
Well, it means that it's going to go to the server and ask for the file.
The server is going to go looking, and send it back. Then your computer is going to download the contents. When the contents are 100% downloaded, they'll be available to use.
...thing is...
Your program isn't waiting for that to happen.
It's telling the server to take its time, and in the meantime it's going to keep doing what it's doing, and it's not going to think about the contents again, until it gets a call from the server.
Well, browsers are really freakin' fast when it comes to rendering HTML.
Servers are really freakin' fast at serving static (plain-text/img/css/js) files, too.
So now you're in a race.
Which will happen first?
Will the server call back with the text, or will the browser hit the script tag that asks for the file contents?
Whichever one wins on that refresh is the one that will happen.
So how do you get around that?
Callbacks.
Callbacks are a different way of thinking.
In JavaScript, you perform a callback by giving the AJAX call a function to use, when the download is complete.
It'd be like calling somebody from a work-line, and saying: dial THIS extension to reach me, when you have an answer for me.
In jQuery, you'll use a parameter called "success" in the AJAX call.
Make success : function (data) { doSomething(data); } a part of that object that you're passing into the AJAX call.
When the file downloads, as soon as it downloads, jQuery will pass the results into the success function you gave it, which will do whatever it's made to do, or call whatever functions it was made to call.
Give it a try. It sure beats racing to see which downloads first.
I recommend not to use url: "./seeds/Ag.txt",, to target a file directly. Instead, use a server side script llike PHP to open the file and return the data, either in plane format or in JSON format.
You may find a tutorial to open files here: http://www.tizag.com/phpT/fileread.php

jQuery can't call plugins after appending Ajax data?

EDIT2: Nevermind, got it working. jQuery was included again in one of the scripts called with ajax. Thanks anyway.
EDIT: This is a rephrased question. Took my a while to find where the problem was.
Calls to plugin functions (including UI effect) aren't working after I load some content with ajax.
The original question had .effect("pulsate",{},1000) call, which explains sarcastyxs answer. Now I'm trying the same thing with the countdown plugin. The same problem appears.
Here is my code. Ignore that it doesn't do much sense, it is stripped down from a larger file. By itself the code is fine. For instance, if I call fadeOut() it works, but if I use .countdown or .effect the mentioned problem appears.
var getOrderDetails= function(id){
var that = this;
$.ajax({
type: "GET",
url: "orderDetails.php",
data: {id: id},
success: function(data){
//$("#orderDetails").html(data);
$("#orderDetails").unbind().click(function(){
var status = $("#orderDetails .orderStatus").text()
acceptOrder(id, status);
});
}
});
}
var acceptOrder = function(id, status){
var that = this;
$.ajax({
type: "GET",
url: "orderStatus.php",
data: {action: 'set', id: id, status: status},
success: function(data){
var nowTime = new Date();
var countdownTime = nowTime.setMinutes(nowTime.getMinutes() + 2);
$("#waitingOrders").countdown({until: countdownTime, compact:true, format: 'MS'});
}
});
}
$(document).ready(function(){
$(".order").unbind().click(function() {
var id = $(this).find(".orderId").text();
getOrderDetails(id);
});
});
When I comment the $("#orderDetails").html(data) line (like in code), the countdown timer appears inside the orderDetails div.
When I uncomment the line, I get an countdown is not a function error. Looked at the dom in firebug, and really in first case I can see that the selector has the .countdown function, and in the second scenario it does not.
What am I missing here?
Someone please help, I have been stuck on this for a few days.
Tried to wrap the ajax success functions with another function and pass it this as context, but it doesn't help. Same thing
You seem to be missing some core functions from jquery-ui. You can download a custom jquery-ui library from the jQuery site.
You basically want to select Effects core and whatever other effect you want to use on the site. The effects core is not part of the default jQuery ui library for some reason.

JavaScript Code Organization Suggestions/Code Review

I am working on a large site that has a lot of custom (page specific js). There is one main.js and page-specific.js files. Is there a better approach that can use the following pattern?
How can I re-factor multiple methods that use ajax?
I currently assign all onclick events inline such as onclick="MYSITE.message.send... - it there a better way? Creating multiple $("#button").click(function() {}); seems like more work...
var MYSITE = MYSITE ? MYSITE: {};
var MYSITE {
bookmark: {
add: function(contentId, userId) {
var data = {
contentId: contentId,
userId: userId
};
$.ajax({
url: "/rest/bookmarks/",
type: "post",
data: data,
complete: function(response) {
if (response.error) {
alert(response.error);
} else {
alert("success");
}
}
});
}
},
message: {
/* <a onclick="MYSITE.message.send('1234', '1234');" href="javascript:void(0);">BOOKMARK</a> */
send: function(contentId, userId) {
var data = {
contentId: contentId,
userId: userId
};
$.ajax({
url: "/rest/bookmarks/",
type: "post",
data: data,
complete: function(response) {
if (response.error) {
alert(response.error);
} else {
alert("success");
}
}
});
}
}
}
You can use jquery delegate method
if you have div element with a tags
<div id="bookmarks">
BOOKMARK
</div>
$("div#bookmarks").delegate("a", "click", function(){
MYSITE.message.send('1234', '1234');
});
and you can get 'contentId' and 'userId' dynamically.
Firstly, assigning onclick="" directly is bad practice. Using jQuery's click() method is not that much more work, but even if it was, it is cleaner and much more recommended. It keeps your HTML markup separate from your JS functionality and makes it easier to change things later on. That's the very first thing you should refactor.
What I do with my JS is create one main library JS file with all of my classes/core functionality. I then use inline <script> tags on specific pages to call the method that hooks up the links to the functions.
In the global app.js file, I'll have a function:
function initLinksOnPageX() {
$('#button').click(function() { MYSITE.message.send('1234', '1234'); });
/* ... setup any other events ... */
}
In the page body:
<script type="text/javascript">
$(initLinksOnPageX);
</script>
Again, this keeps your markup clear of any JS code, so your JS related updates happen in one file (or fewer). With this setup you shouldn't strictly need page-specific JS files, though organizationally speaking it might make sense-- I don't know exactly how big your JS is.
My thoughts:
This depends on many factors, but if the page specific code is "large" then it is probably best to keep it separate. If, however, it is just a few lines, and we are not talking about thousands of pages, then lumping it all into the main.js may not be a big issue.
Not very familiar with ajax, so I will pass on commenting on how to "refactor".
If you can loop your assignment of the onclick events, then NOT doing them inline is going to save you a lot of work.
I'd consider wrapping my custom functionality up as a jquery plugin.

Categories

Resources