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

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

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/

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 stops working after ajax request that adds fields to a form in Drupal 7

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?

JQuery mobile collapsible with Ajax

trying to display the JQMobile collapsible containing an unsorted list. The collapsible is not shown when the list is appended using an ajax call. The collapsible is correctly shown when the list is added statically. Any advice?
thanks
<script>
$(document).ready(function() {
var updateSectionsPage = function() {
// 1. Get the page and list we need to work with
var $page = $('#homeList');
// 2. Build the URL we need using the data stored on the main view page
var strUrl = 'http://xyz';
// 3. Get the sections and append them to the list
$.ajax({
url: strUrl,
dataType: 'json',
cache: false,
success: function(data) {
$sections = $page.find('#sections');
// 3.1 Delete the existing content, if any
$sections.empty();
// 3.2 Create a new collapsible
$sections.html('<div id="collapsible" data-role="collapsible" data-collapsed="true" data-theme="a" data-content-theme="a"></div>');
// 3.3 Create the title of collapsible
$sections.html('<h3>ColdPlay</h3>');
// 3.4 Create the list and store it into a JQuery object
$sections.html('<ul id="list" data-role="listview" data-inset="false"></ul>');
$list = $page.find('#list');
// 3.5 Build HTML that contains the desired information
for (var j in data.list[0].list){
var strHtml = '<li><img src="' + data.list[0].list[j].img + '" /><h4>' + data.list[0].list[j].title + '</h4></li>';
// Make it into a jQuery object...
var item = $(strHtml);
// ...so we can append it to our list.
$list.append(item);
}
// Call the listview widget.
$list.listview();
},
error: function() {
alert("An error occurred. please, try it again!");
}
});
}(); // 4. Call the updateSectionsPage() function
})
</script>
I think you just need to turn your $list.listview(); call into $list.listview('refresh');.
Also, you may benefit from changing up the way you append you new list items. Check this post out. You do not want to nest an append call within a loop if you can avoid it. You will also benefit from not wrapping your strHtml with the jQuery $ selector as it may not be necessary.
That optimization link is courtesy of another SO post here.
Once you create the list,use the following code snippet-
$list.listview('refresh');
$page.trigger('create');
in place of $list.listview();
Also it is not considered a best practice to use $(document).ready() in jquery mobile.See the note below
Important: Use pageInit(), not $(document).ready()
The first thing you learn in jQuery is to call code inside the
$(document).ready() function so everything will execute as soon as the
DOM is loaded. However, in jQuery Mobile, Ajax is used to load the
contents of each page into the DOM as you navigate, and the DOM ready
handler only executes for the first page. To execute code whenever a
new page is loaded and created, you can bind to the pageinit event.
This event is explained in detail at the bottom of this page.
From http://jquerymobile.com/demos/1.0/docs/api/events.html
thanks guys, the issue in my code was on the missing collapsible() widget call. Once the html page is dynamically created, we need to render it with jqmobile widget calls: listview() and collapsible(). Here the working code.
function fillSectionsPage() {
// 1. Get the page we need to work with
var $page = $('#sectionList');
// 2. Build the URL we need using the data stored on the main view page
var strUrl = 'http://xyz';
// 3. Get the sections and append them to the list
$.ajax({
url: strUrl,
dataType: 'json',
cache: false,
success: function(data) {
$sections = $page.find('#sections');
// 3.1 Delete the existing content, if any
$sections.empty();
// 3.2 Append a new collapsible and store it into a JQuery object
$sections.append('<div id="collapsible" data-role="collapsible" data-collapsed="true" data-theme="c" data-content-theme="c"></div>');
$collapsible = $page.find('#collapsible');
// 3.3 Append the title of collapsible
$collapsible.append('<h3>' + data.list[0].title + '</h3>');
// 3.4 Append the list header and store it into a JQuery object
$collapsible.append('<ul id="list" data-role="listview" data-inset="false"></ul>');
$list = $page.find('#list');
// 3.5 Build the list items
var htlmList = [];
for (var j in data.list[0].list){
htlmList[j] = '<li><img src="' + data.list[0].list[j].img + '" /><h4>' + data.list[0].list[j].title + '</h4></li>';
}
// 3.6 Append the list items to the list header
$list.append(htlmList.join(''));
// 3.7 Render the listview and the collapsible
$list.listview();
$collapsible.collapsible();
},
error: function() {
alert("An error occurred, please, try it again!");
}
});
}
Hope to check that tutorial, Collapsible content and Ajax loading with jQuery Mobile and this for How do I toggle the jQuery Mobile Accordion with a button click?

HTML data from multiple ajax requests to javascript array

I'm trying to pre-load some html content using AJAX and jQuery. The AJAX callback function adds the data to an associative array. I'm fine if I do each request individually:
var contentArray = new Object();
var urlA = "includes/contentA.php";
var urlB = "includes/contentB.php";
var urlC = "includes/contentC.php";
$.get(urlA, function(htmlA) {
contentArray["A"] = htmlA;
});
$.get(urlB, function(htmlB) {
contentArray["B"] = htmlB;
});
$.get(urlC, function(htmlC) {
contentArray["C"] = htmlC;
});
Since I am likely to have a few of these (more than three), I tried to do it a for loop:
var contentArray = new Object();
var pages = new Object();
pages["A"] = "includes/contentA.php";
pages["B"] = "includes/contentB.php";
pages["C"] = "includes/contentC.php";
for (var key in pages) {
var URL = pages[key];
$.get(URL, function(html) {
contentArray[key] = html;
});
}
However, this doesn't work. contentArray only has one property containing html data, rather than three. I'm knew to jQuery, particularly the AJAX stuff, so both explanations and solutions (similar or different-method-same-result) are welome.
By the way, I'm aware that one larger AJAX request is preferable to multiple small ones, but I'm trying to retain compatibility for users without JS enabled, and the current php includes are convenient. Any suggestions as how I might satisfy both these requirements are also very welcome.
Thanks.
The callback function for an AJAX request doesn't run until the request returns. In your case each callback function will use key as it exists in the current context, and since there's no key variable in it's local scope it will use the nearest it can find, the key in your for loop.
The problem is by the time the AJAX requests return, the for loop has been fully iterated over and key is equal to the last key in the array. Thus each of the callback functions will receive the same key, overwriting the previous value in your contentArray.
If you're using jQuery 1.5.1 or above a quick and dirty solution (one that doesn't involve changing the current structure of your PHP files) might be to try the following:
for (var key in pages) {
var URL = pages[key];
$.ajax({
url: URL,
xhrFields: {
'customData': key
},
success: function(html, statusText, jqXHR) {
contentArray[jqXHR.customData] = html;
}
});
}
I haven't tested that but according to the documentation page it should work. All you're doing is using the request object created by jQuery to pass your variable along to the callback function.
Hope that helps

Resources