jQuery stops working after ajax request that adds fields to a form in Drupal 7 - ajax

I don't think this is a Drupal-specific question, but more of a general jquery/ajax issue:
Basically, I'm trying to use javascript to add up form fields and display the result in a "subtotal" field within the same form. Everything is working fine until i click the option to add another field (via ajax), which then changes my "subtotal" field to zero, and won't work again until I remove the field.
Here is the function that adds up the fields:
function calculateInvoiceFields(){
var total = 0;
var rate = 0;
var quantity = 0;
var i = 0;
var $ = jQuery;
$("#field-aminvoice-data-values tr").each(function(){
// quantity field number
quantity = $("#edit-field-aminvoice-data-und-"+i+"-field-aminvoice-quantity-und-0-value").val();
// rate field as number
rate = $("#edit-field-aminvoice-data-und-"+i+"-field-aminvoice-rate-und-0-value").val();
if(!isNaN(quantity) && !isNaN(rate)){
total += quantity*rate;
}
i++;
});
return total;
}
And here are the functions that get fired for .ready and .live:
jQuery(document).ready(function(){
var $ = jQuery;
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
jQuery(function(){
var $ = jQuery;
$(".form-text").live('change', function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
});
Any ideas would be a big help. Thanks in advance!

I recommend using 'on' for any binding statement. and 'off' for unbinding.
The reason it doesn't work after an AJAX call, is because you need to be watching for that element to be added to the DOM, and an event attached to it after it gets loaded. If you load a new element in, and there is nothing watching for it, it won't add the event watch to that new DOM element.
As below:
function calculateInvoiceFields(){
/*..*/
return total;
}
$(document).ready(function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
$("body").on('change', ".form-text", function(){
$(".field-type-commerce-price input").val(calculateInvoiceFields());
});
});

usually it stops working when an error has been thrown. did you check out your javascript console (firefox firebug, or built in for chrome) for any indication of an error?

Related

Add or trigger event after inner content to page

I have links on a table to edit or delete elements, that elements can be filtered. I filtered and get the result using ajax and get functions. After that I added (display) the result on the table using inner.html, the issue here is that after filtering the links on the elements not work, cause a have the dojo function like this
dojo.ready(function(){
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
I need to trigger the event after filtering, any idea?
(I'm assuming that you're using Dojo <= 1.5 here.)
The quick answer is that you need to extract the code in your dojo.ready into a separate function, and call that function at the end of your Ajax call's load() callback. For example, make a function like this:
var attachDeleteEvents = function()
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
};
Then you call this function both in dojo.ready and when your Ajax call completes:
dojo.ready(function() { attachDeleteEvents(); });
....
var filter = function(someFilter) {
dojo.xhrGet({
url: "some/url.html?filter=someFilter",
handleAs: "text",
load: function(newRows) {
getTableBody().innerHTML = newRows;
attachDeleteEvents();
}
});
};
That was the quick answer. Another thing that you may want to look into is event delegation. What happens in the code above is that every row gets an onclick event handler. You could just as well have a single event handler on the table itself. That would mean there would be no need to reattach event handlers to the new rows when you filter the table.
In recent versions of Dojo, you could get some help from dojo/on - something along the lines of:
require(["dojo/on"], function(on) {
on(document.getElementById("theTableBody"), "a:click", function(evt) {...});
This would be a single event handler on the whole table body, but your event listener would only be called for clicks on the <a> element.
Because (I'm assuming) you're using 1.5 or below, you'll have to do it a bit differently. We'll still only get one event listener for the whole table body, but we have to make sure we only act on clicks on the <a> (or a child element) ourselves.
dojo.connect(tableBody, "click", function(evt) {
var a = null, name = null;
// Bubble up the DOM to find the actual link element (which
// has the data attribute), because the evt.target may be a
// child element (e.g. the span). We also guard against
// bubbling beyond the table body itself.
for(a = evt.target;
a != tableBody && a.nodeName !== "A";
a = a.parentNode);
name = dojo.attr(a, "data-yourapp-name");
if(name && confirm("Really delete " + name + "?")) {
alert("Will delete " + name);
}
});
Example: http://fiddle.jshell.net/qCZhs/1/

Ajax / jQuery - append new items but check don't already exist in DOM

I have a container div which includes lots of element divs all of which have a unique ID. I then make an ajax call to get more elements and append these to the DOM.
This works using the function below but I need to check that what I append doesn't already exist in the DOM. I've been looking into using each() and possibly remove() or detach() in order to do this, but I am not sure of jQuery syntax and really need some assistance.
function loadMoreItems(url) {
$.get(url, null, function(data) {
var container = $(data).find('#container');
var newItemsHTML = "";
/*-- not sure what to do in between
container.find('.element').remove();
container.each('.element').detach();
--*/
newItemsHTML = $(container).html();
var $newItems = $(newItemsHTML);
$container.isotope('insert', $newItems, true);
}, 'html');
}
<div class="element" id="id_172977"></div>
Assuming all of appended divs has class element you can do
$(".element").each(function() {
container.find("#" + this.id).remove();
});
Demo

"Load More Posts" with Ajax in wordpress

I am trying to create ajax pagination on Blog Page..
What I need to do is to display 5 posts initially and then load 5 more when "load more posts" link is clicked.
Below is the javascript I am using:
<script>
jQuery(document).ready(function() {
// ajax pagination
jQuery('.nextPage a').live('click', function() {
// if not using wp_pagination, change this to correct ID
var link = jQuery(this).attr('href');
// #main is the ID of the outer div wrapping your posts
jQuery('.blogPostsWrapper').html('<div><h2>Loading...</h2></div>');
// #entries is the ID of the inner div wrapping your posts
jQuery('.blogPostsWrapper').load(link+' .post')
});
}); // end ready function
</script>
The problem is that when I click the link the old posts get replaced by the new ones, I need to show old posts as well as the new posts...
Here is the Updated jQuery Code which enables the ajax pagination.
jQuery(document).ready(function(){
jQuery('.nextPage a').live('click', function(e){
e.preventDefault();
var link = jQuery(this).attr('href');
jQuery('.blogPostsWrapper').html('Loading...');
jQuery('.blogPostsWrapper').load(link+' .post');
});
});
The only problem now is the old posts get removed, i need to keep both old and new posts..
Here is the final code I used and now everything works perfectly...
// Ajax Pagination
jQuery(document).ready(function($){
$('.nextPage a').live('click', function(e) {
e.preventDefault();
$('.blogPostsWrapper').append("<div class=\"loader\"> </div>");
var link = jQuery(this).attr('href');
var $content = '.blogPostsWrapper';
var $nav_wrap = '.blogPaging';
var $anchor = '.blogPaging .nextPage a';
var $next_href = $($anchor).attr('href'); // Get URL for the next set of posts
$.get(link+'', function(data){
var $timestamp = new Date().getTime();
var $new_content = $($content, data).wrapInner('').html(); // Grab just the content
$('.blogPostsWrapper .loader').remove();
$next_href = $($anchor, data).attr('href'); // Get the new href
$($nav_wrap).before($new_content); // Append the new content
$('#rtz-' + $timestamp).hide().fadeIn('slow'); // Animate load
$('.netxPage a').attr('href', $next_href); // Change the next URL
$('.blogPostsWrapper .blogPaging:last').remove(); // Remove the original navigation
});
});
}); // end ready function
Could you maybe try the following code? This is how I got this working on my own site.
replace:
jQuery('.blogPostsWrapper').load(link+' .post')
with:
$.get(link+' .post', function(data){
$('.blogPostsWrapper').append(data);
});
You should use jQuery append() to add the new posts without using the old ones.
jQuery load() Will replace the data found in your element . Quoted from jQuery API:
.load() sets the HTML contents of the matched element to the returned
data. This means that most uses of the method can be quite simple:

jQuery hashchange how to do?

I have made a jQuery thing; with will load content without refreshing the page. The code for that is:
$(document).ready(function(){
// initial
$('#content').load('content/index.php');
// handle menu clicks
$('#navBar ul li ').click(function(){
var page = $(this).children('a').attr('href');
$('#content').load('content/'+ page +'.php');
return false;
});
});
Now I want to have a sort of history thing in that, the code for that is:
(function(){
// Bind an event to window.onhashchange that, when the hash changes, gets the
// hash and adds the class "selected" to any matching nav link.
$(window).hashchange( function(){
var hash = location.hash;
// Set the page title based on the hash.
document.title = 'The hash is ' + ( hash.replace( /^#/, '' ) || 'blank' ) + '.';
// Iterate over all nav links, setting the "selected" class as-appropriate.
$('#nav a').each(function(){
var that = $(this);
that[ that.attr( 'href' ) === hash ? 'addClass' : 'removeClass' ]( 'selected' );
});
})
// Since the event is only triggered when the hash changes, we need to trigger
// the event now, to handle the hash the page may have loaded with.
$(window).hashchange();
});
Found on: http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
My Question is: how can i make the second code working with the first?
Since you haven't gotten an answer yet I will write it. You need the plugin jQuery hashchange for the code to run.
https://github.com/cowboy/jquery-hashchange
To implement a cache you could do something like
$('#content').load('content/index.php');
//create a cache object
var cache = {};
// handle menu clicks
$('#navBar ul li ').click(function(){
var page = $(this).children('a').attr('href');
//check if the page was already requested
if(cache[page] === undefined){
//if not fetch the page from the server
$.get('content/'+ page +'.php', function(data){
$('#content').html(data);
//save data in cache
cache[page] = data;
}else{
//use data from cache
$('#content').html(cache[page]);
}
return false;
});
Use History JS. It works for HTML5 pushState and also falls back to HTML 4 hashtags. Also works for keeping the state model when the page is refreshed.

jquery mobile ajax sends both GET and POST requests

Here is the problem:
By default jQuery Mobile is using GET requests for all links in the application, so I got this small script to remove it from each link.
$('a').each(function () {
$(this).attr("data-ajax", "false");
});
But I have a pager in which I actually want to use AJAX. The pager link uses HttpPost request for a controller action. So I commented the above jQuery code so that I can actually use AJAX.
The problem is that when I click on the link there are two requests sent out, one is HttpGet - which is the jQuery Mobile AJAX default (which I don't want), and the second one is the HttpPost that I actually want to work. When I have the above jQuery code working, AJAX is turned off completely and it just goes to the URL and reloads the window.
I am using asp.net MVC 3. Thank you
Instead of disabling AJAX-linking, you can hijack clicks on the links and decide whether or not to use $.post():
$(document).delegate('a', 'click', function (event) {
//prevent the default click behavior from occuring
event.preventDefault();
//cache this link and it's href attribute
var $this = $(this),
href = $this.attr('href');
//check to see if this link has the `ajax-post` class
if ($this.hasClass('ajax-post')) {
//split the href attribute by the question mark to get just the query string, then iterate over all the key => value pairs and add them to an object to be added to the `$.post` request
var data = {};
if (href.indexOf('?') > -1) {
var tmp = href.split('?')[1].split('&'),
itmp = [];
for (var i = 0, len = tmp.length; i < len; i++) {
itmp = tmp[i].split('=');
data.[itmp[0]] = itmp[1];
}
}
//send POST request and show loading message
$.mobile.showPageLoadingMsg();
$.post(href, data, function (serverResponse) {
//append the server response to the `body` element (assuming your server-side script is outputting the proper HTML to append to the `body` element)
$('body').append(serverResponse);
//now change to the newly added page and remove the loading message
$.mobile.changePage($('#page-id'));
$.mobile.hidePageLoadingMsg();
});
} else {
$.mobile.changePage(href);
}
});
The above code expects you to add the ajax-post class to any link you want to use the $.post() method.
On a general note, event.preventDefault() is useful to stop any other handling of an event so you can do what you want with the event. If you use event.preventDefault() you must declare event as an argument for the function it's in.
Also .each() isn't necessary in your code:
$('a').attr("data-ajax", "false");
will work just fine.
You can also turn off AJAX-linking globally by binding to the mobileinit event like this:
$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false;
});
Source: http://jquerymobile.com/demos/1.0/docs/api/globalconfig.html

Resources