How does JQuery empty() method work? - javascript

I know that the empty method removes all children in the DOM element.
In this example however, why does removing the empty method result in duplicate entries:
and putting it in results in a normal page:
var renderNotesList = function()
{
var dummyNotesCount = 10, note, i;
var view = $(notesListSelector);
view.empty();
var ul = $("<ul id =\"notes-list\" data-role=\"listview\"></ul>").appendTo(view);
for (i=0; i<dummyNotesCount; i++)
{
$("<li>"+ "" + "<div>Note title " + i + "</div>" + "<div class=\"list-item-narrative\">Note Narrative " + i + "</div>" + "" + "</li>").appendTo(ul);
}
ul.listview();
};

I don't know why empty() doesn't work but I found this
... so until this is sorted everyone should just use:
el.children().remove(); instead of el.empty();
( jQuery.empty() does not destroy UI widgets, whereas jQuery.remove() does (using UI 1.8.4) )

Without seeing how your JavaScript is being used in your page, I suspect that you must be calling the renderNotesList() function twice and thus generating to unordered lists.
When you use the .empty() method, you are removing the first ul list, so you only see one instance. Without the call to .empty(), you retain both.
However, I can't say where or how this is happening in you web page without seeing more, but at least you now have some idea of what to look for.
Demo Fiddle
I built a demo using your JavaScript, but I was sort of guessing as to how you are using it.
Fiddle: http://jsfiddle.net/audetwebdesign/UVymE/
Footnote
It occurred to me that the function ul.listview() may actually be appending a second copy of the ul to the DOM. You need to check the code or post it for further review.

Related

jQuery for loops not running function

I'm trying to make a loop in jQuery that finds all 'img' elements and places a caption below them, according to the value of the element's 'caption' attribute. Whenever I run the loop below, I am left with no captions under any of the images.
for (var i = 0; i < $('.myimage').length; i++) {
$('.myimage')[i].after('<h6>' + $('.myimage').attr('caption') + '</h6>');
};
However, when I run this code
$('.myimage').after('<h6>TEST</h6>');
the word 'TEST' appears below all of the images. Therefore I know my html is correct, I have no typos, and the selector is working, I just cannot get the for loop to work... What have I done wrong?
$('.myimage')[i] returns a DOM element (not a jQuery object) so there is no after method. If you want to loop, simply use .each
$(".myimage").each(function() {
//this refers to each image
$(this).after('<h6>' + $(this).attr('caption') + '</h6>');
});
You can loop through the .myimage elements like this, using .after()'s callback function
$('.myimage').after(function(){
return '<h6>' + $(this).attr('caption') + '</h6>';
});
One minor note, don't make up your own attributes. use the custom data attribute instead, like data-caption="something".
jsFiddle example

'div_element' is null or not an object in IE7

Here I have get one error in JavaScript div_element is null or not an object.
I have given my code below:
function showLoading(id) {
div_element = $("div#" + id)[0];
div_element.innerHTML = loading_anim; // Error in this line
}
When I am debugging my script but it's working fine in other browsers including IE 8, but it's not working in IE 7. I don't understand what exact issue occur in this script.
First of all, you dont need to put a tag name infront of the jQuery, unless you have other elements with exact same id on other elements, in other pages.
Next, your statement div_element.innerHTML = loading_anim; is correct. So, the only explanation is that, there is no element with that ID, in the DOM.
Finally, since you are usign jQuery already, no need to mix up native JS and jQuery to create a dirty looking code.
function showLoading(id) {
div_element = $("#" + id);
console.log(div_element); //check the console to see if it return any element or not
div_element.html(loading_anim);
}
I think you don't select anything with your jquery selector (line 2)
try to display
id
"div#" + id
$("div#" + id)
$("div#" + id)[0]
You can use firebug javascript console or a simple alert like this:
alert($("div#" + id)[0]);
And see if you must id or class on your div ( use # or . selector)
I suppose, that nic wants to display some loader GIF animation, I confirm, that nic must use jQuery .html() method for DOM objects, and tusar solution works fine on IE6+ browsers. Also (it is obvious but anyway) nic must assign a value to loading_anim variable in script, lets say: var loading_anim = $('#loader').html(); before assigning its value to div_element.
Use .html() for jQuery objects. innerHTML work for dom objects, they wont work for jQuery objects.
function showLoading(id) {
div_element = $("div#" + id);
$(div_element).html(loading_anim); // Provided `loading_anim` is valid html element
}

Why do you have to re-select a JQuery element from a collection?

I have a number of check-boxes on my page, each has a name attribute for its useful value.
I want to get a list of values for the checked items only. I can get a collection of elements like so...
var checkedItems = $(".checkbox:checked");
Now I want to create the list (string) so I create a loop...
var list = "";
for (var i = 0; i < checkedItems.length; i++) {
list += $(checkedItems[i]).attr("name") + "\n";
}
This works and gives me my list. But the question is, why do I have to apply the JQuery object thingy i.e. $(...) to use the attr property?
if I had the following instead it would not work...
list += checkedItems[i].attr("name") + "\n";
But surely the array should be a collection of JQuery elements already? Why re-cast?
Here is a JSFiddle of the working example if anybody wants to try it
EDIT: Why does this work...
var item = $("#item");
var name = item.attr("name");
checkedItems[i] is a raw DOM element, not a jQuery object.
To get a jQuery object for a given element in the set, call checkedItems.eq(i).
You can do this more nicely by writing
var list = $(".checkbox:checked")
.map(function() { return this.name; })
.get()
.join('\n');
The code in your question is wrong, because
$(checkedItems[i].attr("name"))
should really be
$(checkedItems[i]).attr("name")
Besides that, you should really do it like this
var list = "";
checkedItems.each(function(){ list += this.name + '\n'; });
Demo at http://jsfiddle.net/dGeNR/2/
The reason it would not work, is that when you access the elements in the jQuery object with [] you get back a direct reference to the DOM element, and not another jQuery object.
I presume that what you actually do is this:
list += $(checkedItems[i]).attr("name") + "\n";
checkedItems is a jQuery selection. It contains references to the elements that it contains. These elements can be manipulated by jQuery methods, or accessed directly with checkedItems[n], where n is a 0-based index.
Those properties are not jQuery selections: they are the native DOM elements. If you want to use a jQuery method like attr, you need to create a new jQuery object, wrapping that native object.
In this case, you can avoid that by using the eq method, which gets a jQuery selection from an original selection by using a numeric key:
list += checkedItems.eq(i).attr('name') + "\n";
Even better in your case would be to use a combination of map and get and Array.prototype.join:
var list = checkedItems.map(function() {
return this.name;
}).get().join('\n');
Well the list-building code in your question doesn't work; it should be:
for (var i = 0; i < checkedItems.length; i++)
list += $(checkedItems[i]).attr("name") + "\n"; // or just checkedItems[i].name
The ".attr()" function is just defined to return the attribute value for the first element in the list of matched elements. It could have been designed to return an array (like the ".pluck()" function in the Prototype library), but it just doesn't work that way.
The ".css()" jQuery method is similar in that regard, as is ".val()".
Another way to do what you want is:
var list = checkedItems.map(function() {
return this.name;
}).get().join('\n'); // thanks SLaks :-)
To be able to call jQuery functions, you need to wrap the content in $() or jQuery().
I have fixed the example at http://jsfiddle.net/dGeNR/1/
You need to use list += $(checkedItems[i]).attr("name") + "\n";

how to get children elements of an html element

I have a var saved in my JS custom object (code below is from inside the class):
var toolbar;
this.create_toolbar = function() {
self.toolbar = document.createElement('div');
$(self.toolbar)
.html('<ul>' +
'<li id="insert_bold"></li>' +
'<li id="insert_em"></li>' +
'<li id="insert_hyperlink"></li>' +
'<li id="insert_code"></li>' +
'<li id="insert_image"></li>' +
'</ul>')
.insertBefore(self.editTextarea); // just a textarea on the page
}
The toolbar gets created and placed successfully, and self.toolbar now holds the object for the new div. However, when trying to bind the <li>'s to clicks I can't seem to get anything to happen:
$(self.toolbar).children("li#insert_bold").click(function() {
alert("just checking");
});
To be clear, after the above, nothing is happening when I click <li id="insert_bold">
Is there something that I am missing? I am fairly new to jQuery....am I allow to put a variable that holds an [object object] in the $()? If not, how should I do this?
You need to use .find() here, like this:
$(self.toolbar).find("li#insert_bold").click(function() {
alert("just checking");
});
.children() only looks at immediate children, <li> is beneath the <ul> though, so it's not an immediate child.
Be aware though if you intend on having multiple instances of this on a page (I'm guessing this is likely the case), you should use classes instead (IDs need to be unique), then use a class selector, for example: $(self.toolbar).find("li.insert_bold").
While Nick has solved your question, I don't think this is a good idea, especially if you have a large number of HTML elements binding.
When I have a rather small number, I do gather the items in an object inside the main constructor function
this.el = {
$button : $('.button','#div'),
$submit : $('.submit','#div')
};
Then when I need an element, I just call it with (this), assuming you are using prototype functions.
this.el.$button.click(function () {
});
If you many elements to deal with, 20 or more, I'll recommend that you opt for a JavaScript FrameWork. Backbone Js is a good one.

How does one add arrays of elements to the DOM in jQuery?

Coming to jQuery from a functional background, I am fond (perhaps unreasonably so) of elegant chains of functions. I often find myself dealing with arrays of elements, such as those that may result from $.map, and my ability to manipulate these arrays in the DOM seems quite limited. Here's some sample code that runs through the results of a Google search, rendering the result titles:
var newResultsDiv = $('<div id="results" />');
$.each(searcher.results, function() {
newResultsDiv.append('<p>' + this.title);
});
$("#searchresults").append(newResultsDiv);
I find this excessively verbose. Ideally, I would do something along these lines instead:
$.map(searcher.results, function(elem) {
return $('<p>' + elem.title);
}).wrapAll('<div id="results" />').appendTo('#searchresults');
I've tried this out, along with several variants using different forms of append and wrap. They all appear to be incapable of handling the plain-old-Javascript array that jQuery.map spits out. They're designed to work with jQuery's own set collection. At least, that's my guess, as messing around with these functions in the Firebug console seems to confirm the problem.
I am hoping that someone here has the wisdom to show me an elegant way to do what I'm trying to do. Is there one?
Using the $.map method you presented, you could return the actual DOM element instead of a jQuery object. This is done by grabbing the [0] index item in the jQuery object. Then wrap the entire $.map with $().
This works because jQuery will accept an array of DOM elements.
Your <p> creation was a little off. I changed it to pass an object literal to set the text. Otherwise, you would need to concatenate the ending tag as well.
Finally, you would need to traverse up to the wrapper #results you created using .parent().
$($.map(searcher.results, function(elem) {
return $('<p>',{text:elem.title})[0];
})).wrapAll('<div id="results" />').parent().appendTo('#searchresults');
EDIT: IF you don't mind the look of it, you could do this as well:
$('<div id="results" />').append(
$.map(searcher.results, function(elem) {
return $('<p>',{text:elem.title})[0];
})).appendTo('#searchresults');
Hm...not sure if this will work, but give it a try:
$("<div id='results'>" + $.map(searcher.results, function(elem) {
return '<p>' + elem.title + '</p>';
}).join("") + "</div>").appendTo('#searchresults');
OK, just tested it here:
http://jsfiddle.net/xxxST/1/
And it seems to work. I should mention, however, that while this is perhaps not very "verbose" in terms of number of lines, it's a bit opaque with regard to its clarity. It's a jQuery function result, joined together to a string, inside a jQuery wrapper with an appendTo function running on it. I think it would be much more readable if you were to do something like this:
var resultString = $.map(searcher.results, function(elem) {
return '<p>' + elem.title + '</p>';
}).join("");
$('#searchresults').append("<div id='results'>" + resultString + "</div>");
But that's just my opinion.

Categories