Adding JQuery to content loaded with AJAX - ajax

I just learned that you can use live() instead of bind() to make sure an event handler still exists after content is reloaded with AJAX... but what can I do with other added JQuery, such as .sortable()? I have a UL that I need to reload after sorting or adding; but after the AJAX reload, the sorting doesn't quite work right...
It doesn't completely lose the sortability, but the dragging become much more difficult; it will jump around and seemingly switch 2 other sections that's not the one I'm trying to drag. This only happens after the partial reload; my guess is that when it reloads, I need to re-attach the sortable... how can I do this? Here's my code:
$("#sections").sortable({
start: function (event, ui) {
startIndex = ui.item.index();
},
update: function (event, ui) {
$.ajax({
type: "POST",
url: "/Form/sortSections",
data: {
formID: '#(Model.formID)',
displayOrder: ui.item.index() + 1,
sectionID: $(ui.item).attr("id"),
startDisplayOrder: startIndex + 1
},
success: function (result) {
$.get("/Form/_AllSections/#(Model.formID)", function (data) {
$("#sections").html(data);
});
},
error: function (req, status, error) {
alert(error);
}
});
}
});

Use the load event with live() so that it runs every time the element is added to the DOM.
http://api.jquery.com/load-event

Related

Ajax wait on success before next iteration in .each loop

I have an ajax call inside a .each loop wrapped in a setInterval function.
This handles updating of many divs on a dashboard with just a few lines of code on the html page.
I am worried about server lag vs client side speed. What will happen if the server has not responded with the data before the loop moves on to the next iteration?
So, my question is, can the loop be paused until the success is executed?
Ajax call:
setInterval(function() {
$(".ajax_update").each(function(){
$.ajax({
type: "POST",
dataType: "json",
url: "ajax/automated_update/confirmed_appointments.php",
data: "clinic_id=<? echo $clinic_id ?>&tomorrow=<? echo $tomorrow ?>&"+$(this).data('stored'), // serializes the form's elements.
success: function(data)
{
$(data[0]).html(data[1]);
}
});
});
}, 5000); //5 seconds*
</script>
I have looked into .ajaxComplete() but I dont see how to apply this as a solution.
I have also looked at turning the loop into something that calls itself like:
function doLoop() {
if (i >= options.length) {
return;
}
$.ajax({
success: function(data) {
i++;
doLoop();
}
});
}
But would that not interfere with .each? I dont understand how that would play nice with .each and looping based on my div class.
I just cant figure it out! Any help would be appreciated.
I was able to get .when working with the ajax call, but I dont understand how to make .when do what I need (stop the loop until the ajax call is done).
$(".ajax_update").each(function(){
$.ajax({
type: "POST",
dataType: "json",
url: "ajax/automated_update/confirmed_appointments.php",
data: "clinic_id=<? echo $clinic_id ?>&tomorrow=<? echo $tomorrow ?>&"+$(this).data('stored'), // serializes the form's elements.
success: function(data)
{
$(data[0]).html(data[1]);
}
});
$.when( $.ajax() ).done(function() {
alert("Finished it");
});
});
After thinking about your question a bit, perhaps a good solution would be to put an event in place that would trigger a new set of updates with a minimum time between your dashboard updates. This would ensure that all your updates process, that we do wait a minimum time between updates and then trigger the update cycle once again. Thus if you DO encounter any delayed ajax responses you do not try another until the previous one has all completed.
I have not fully tested this code but is should do what I describe:
//create a dashboard object to handle the update deferred
var dashboard = {
update: function (myquery) {
var dfr = $.Deferred();
$.ajax({
type: "POST",
dataType: "json",
url: "ajax/automated_update/confirmed_appointments.php",
data: "clinic_id=<? echo $clinic_id ?>&tomorrow=<? echo $tomorrow ?>&" + myquery,
success: dfr.resolve
});
return dfr.promise();
}
};
//create a simple deferred wait timer
$.wait = function (time) {
return $.Deferred(function (dfd) {
setTimeout(dfd.resolve, time);
});
};
// use map instead of your .each to better manage the deferreds
var mydeferred = $(".ajax_update").map(function (i, elem) {
return dashboard.update($(this).data('stored')).then(function (data, textStatus, jqXHR) {
$(data[0]).html(data[1]);
});
});
//where I hang my dashboardupdate event on and then trigger it
var mydiv = $('#mydiv');
var minimumDashboardUpdate = 5000;
$('#mydiv').on('dashboardupdate', function () {
$.when.apply($, mydeferred.get())
.then(function () {
$.when($.wait(minimumDashboardUpdate)).then(function () {
mydiv.trigger('dashboardupdate');
});
});
});
mydiv.trigger('dashboardupdate');

How to stop AJAX calls if there's an error on a field with a keyup trigger

I have this jquery ajax call that is trigger on keyup. It has error handling which (with Firefox for e.g.) is triggered multiples times if the user enters keystrokes fast. Is there a quick way to stop multiple alert windows to be shown?
$('input[name$="_LOC"]').keyup(function(){
if ($(this).val().length >=4){
$.ajax({
type: 'POST',
url: 'red.asp?q='+$(this).val(),
beforeSend: function() {
[...]
},
success: function(data) {
[...]
},
error: function() {
alert("Oops!")
}
});
}
});
Restart a timer each time the onkeyup is triggered, this means the event only happens when the user has finished typing (or, at least, paused for a second or whatever).
Use timer = setTimeout(yourFunction, yourDelay);
To rest the timer user clearInterval(timer) and start the setTimeout again.
var typing = false;
var timer;
$('input[name$="_LOC"]').keyup(function(){
if(typing) {
clearInterval(timer);
}
timer = setTimeout(sendAjax, 500, [this]);
typing=true;
});
function sendAjax(element)
{
if ($(element).val().length >=4){
$.ajax({
type: 'POST',
url: 'red.asp?q='+$(element).val(),
beforeSend: function() {
[...]
},
success: function(data) {
[...]
},
error: function() {
alert("Oops!")
}
});
typing = false;
}
Here's JSFiddle example: http://jsfiddle.net/X8US5/, you'll need your browsers console.log viewer ready to see stuff (otherwise edit the console.logs to be alerts though they interrupt JS so times will be off)
Edit:
IE9 compatible (hack) version http://jsfiddle.net/5ndM5/1/
Tried to find a jQuery alternative but none it seems.
THe overriding the function alternative is good if you don't want the global var, but if you only plan to use this code on one form then the global is acceptable (JS code is usually rife with them by accident anyway)

AJAX avoid repeated code

I'm using Symfony2.1 with Doctrine2.1
I'd like to use AJAX for many features on my site , editing a title , rate an article , create an entity on the fly , etc.
My question is simple :
Do I need to create a JQuery function for each functionnality , like this :
$('#specific-functionality').bind('click', function(e){
var element = $(this);
e.preventDefault();
// the call
$.ajax({
url: element.attr('href'),
cache: false,
dataType: 'json',
success: function(data){
// some custom stuff : remove a loader , show some value, change some css
}
});
});
It sounds very heavy to me, so I was wondering if there's any framework on JS side, or a specific method I can use to avoid this. I was thinking about regrouping items by type of response (html_content , boolean, integer) but maybe something already exists to handle it nicely !
From what I understand, you are asking for lighter version of JQuery ajax method. There are direct get/post methods instead of using ajax.
$.get(element.attr('href'), {'id': '123'},
function(data) {
alert(data);
}
);
To configure error function
$.get(element.attr('href'), {'id': '123'}, function(data) {alert(data);})
.error(function (XMLHttpRequest, textStatus, errorThrown) {
var msg = jQuery.parseJSON(XMLHttpRequest.responseText);
alert(msg.Message);
});
Also, you can pass callback function to do any synchronous operations like
function LoadData(cb)
{
$.get(element.attr('href'), { 'test': test }, cb);
}
And call
LoadData(function(data) {
alert(data);
otherstatements;
});
For progress bar, you use JQuery ajaxStart and ajaxStop functions instead of manually hiding and showing it. Note, it gets fired for every JQuery AJAX operation on the page.
$('#progress')
.ajaxStart(function () {
//disable the submit button
$(this).show();
})
.ajaxStop(function () {
//enable the button
$(this).hide();
});
Instead of $('#specific-functionality').bind('click', function(e){, try this:
$(".ajax").click(function(){
var url = $(this).attr("href") ;
var target = $(this).attr("data-target") ;
if (target=="undefined"){
alert("You forgot the target");
return false ;
}
$.ajax(....
And in html
<a class="ajax" href="..." data-target="#some_id">click here </a>
I think it is the simplest solution. If you want some link to work via ajax, just give it class "ajax" and put data-target to where it should output results. All custom stuff could be placed in these data-something properties.

Disable ajaxStart() and ajaxStop() for a specific request

I am using .ajaxStart() and .ajaxStop() to show a modal while an ajax request is being made. (between start and stop)
Now I'd like to add a longpoll function that keeps waiting for notifications, similar to the one on the left upper corner of this site.
My problem now lies in disabling this modal only for the longpolling request..
Registering "loading screen" on and off handlers:
$(document).ajaxStart(handleAjaxStart);
$(document).ajaxStop(handleAjaxStop);
My longpoll function:
$.ajax({
timeout: 35000,
url: longPollUrl,
success: function(data){
if(data.queCount) $('#numQueCount').html(data.queCount);
if(data.queAccept) $('#numQueAccept').html(data.queAccept);
},
dataType: 'json',
complete: longpoll
});
I tried:
$().off('ajaxStart');
$().off('ajaxStop');
..and reattaching the handlers after starting the polling, but no joy.
I also tried introducing a global variable into handleAjaxStart() that would return at the first line of the function, but that seems to completely kill the loading screen.
Any ideas how this can be achieved?
I figured it out..
There is an attribute in the options object .ajax() takes called global.
If set to false, it will not trigger the ajaxStart event for the call.
$.ajax({
timeout: 35000,
url: longPollUrl,
success: function(data){
if(data.queCount) $('#numQueCount').html(data.queCount);
if(data.queAccept) $('#numQueAccept').html(data.queAccept);
},
global: false, // this makes sure ajaxStart is not triggered
dataType: 'json',
complete: longpoll
});
After reading all possible solutions, I want to combine answers.
Solution 1: Bind/Unbind
//binding
$(document).bind("ajaxStart.mine", function() {
$('#ajaxProgress').show();
});
$(document).bind("ajaxStop.mine", function() {
$('#ajaxProgress').hide();
});
//Unbinding
$(document).unbind(".mine");
It is a depreciated solution. Before jQuery 1.9, global events of ajax like ajaxStart, ajaxStop, ajaxError etc. can be binded to any element. After jQuery 1.9:
As of jQuery 1.9, all the handlers for the jQuery global Ajax events,
including those added with the .ajaxStart() method, must be attached
to document.
Therefore we cannot bind/unbind these events to custom namespaces.
Solution 2: Set the property global to false
$.ajax({
url: "google.com",
type: "GET",
dataType: "json",
global: false, //This is the key property.
success: function (data) {
console.log(data);
},
error: function (data) {
console.log(data);
}
});
This solution works to disable ajaxStart()/ajaxStop() event(s). However, it also makes disable ajaxComplete(), ajaxError(), ajaxSend(), ajaxSuccess(). If you don't use these global events, it seems ok, but when it is needed, you have to come back and change your solution for all pages where you set global: false.
Solution 3: Use global variable
var showLoadingEnabled = true;
$(document).ready(function () {
$('#loading')
.hide() // at first, just hide it
.ajaxStart(function () {
if (showLoadingEnabled) {
$(this).show();
}
})
.ajaxStop(function () {
if (showLoadingEnabled) {
$(this).hide();
}
});
});
function justAnotherFunction() {
window.showLoadingEnabled = false;
$.ajax({
url: 'www.google.com',
type: 'GET',
complete: function (data) {
window.showLoadingEnabled = true;
console.log(data);
}
});
}
Global variables should not be used in javascript files. However, this is the simplest solution, I can find.
I prefered the third solution for my project.

load doesn't trigger ajaxSetup complete handler on complete

I have
// Ajax setup
$.ajaxSetup({
beforeSend: function() {
$('#general-ajax-load ').fadeIn();
},
complete: function() {
$('#general-ajax-load ').fadeOut();
}
});
on page load to set loading animation for all my ajax calls. It works perfect, except for load() calls. For loads only beforeSend is triggered, and complete never gets called, Which results with showing animation which never dissapears.
Any idea?
According to http://bugs.jquery.com/ticket/4086#comment:4, the "correct" way would be:
$(document).ajaxSend(function(event, jqXHR, settings) {
$('#general-ajax-load ').fadeIn();
});
$(document).ajaxComplete(function(event, jqXHR, settings) {
$('#general-ajax-load ').fadeOut();
});
I just did some testing and that indeed seems to work in all cases (including $.load).
Adding success fixed the problem, thanks (I can swear I tried it before)
$.ajaxSetup({
beforeSend: function() {
$('#general-ajax-load ').fadeIn();
},
complete: function() {
$('#general-ajax-load ').fadeOut();
}
success: function() {
$('#general-ajax-load ').fadeOut();
}
});
:)
The $.load manual says:
...It is roughly equivalent to
$.get(url, data, success) except that
it is a method rather than global function and it has an implicit
callback function.
It would seem that $.load's implicit callback function is overriding the complete callback in your $.ajaxSetup. The $.ajaxSetup documentation says:
All subsequent Ajax calls using any
function will use the new settings,
unless overridden by the individual
calls, until the next invocation of
$.ajaxSetup().
I guess the solution would be to replace your $.load calls with $.get (or the more verbose $.ajax). You could also try using success instead.

Resources