Optimization appending <li> into <ul> with jQuery - javascript

I populate a list with search results appending li elements. I update DOM for each result.
for (var i = 0; i < topics.length; i++) {
$("#searchResults").append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
I want to make a group of li elements first and update DOM-tree just once.
I try something like this:
var list = $([]);
for (var i = 0; i < topics.length; i++) {
list.append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
$("#searchResults").append(list);
But div $("#searchResults") is empty.
Where is the problem?

Something like this should be much faster:
var ul = $("<ul />");
for (var i = 0, l=topics.length; i < l; i++) {
$("<li />", { text: result.Name, "class": "example" }).appendTo(ul);
};
$("#searchResults").append(ul.contents());
By using a document fragment ($("<ul />")) and appending to it, then appending at the end, we're not messing with the entire DOM each append. Also we're not repeatedly selecting #searchResults each loop...or checking .length would could also be expensive.
Note: this method still uses the DOM to create elements (as opposed to a string), eliminating issues of result.Name having HTML that could screw things up, etc.

Creating DOM elements on the fly will usually be slower than just using innerHTML (or a wrapper around that). Also, concatenating string with + will usually be slower than using Array.join('').
In the end, I suspect something like this would be the fastest:
var list = [];
for (var i = 0; i < topics.length; i++)
list.push("<li class=example>",topics[i].Name,"</li>");
$("#searchResults").html(list.join(''));

Try just using a string. Add all your li's to a string and then put them into the innerHTML of the searchResults div.
var list = '';
for (var i = 0; i < topics.length; i++) {
list +="<li class=example>" + result.Name + "</li>";
}
$("#searchResults").innerHTML = list;
If you are looking for efficacy this is probably better because you are not using the DOM engines a lot. (although unless you are adding hundreds of li's it is probably insignificant anyway.

All previous solutions still suffer from recalculating, painting and layout for every single element we add.
Instead of appending the elements directly to the document when they are created, append them to the DocumentFragment instead, and finish by adding that to the DOM.
var el;
var i = 0;
var fragment = document.createDocumentFragment();
while (i < 500) {
el = document.createElement('li');
el.innerText = '1ist ' + i;
fragment.appendChild(el);
i++;
}
div.appendChild(fragment);

Related

Store DOM elements in array : strange behaviour?

need some help over here.
I create and than, store DOM elements in a array.
Later, when i want to use them, they are like unusable, except for the last one.
Here is a realy simple code to illustrate :
http://jsfiddle.net/rd2nzk4L/
var list = new Array();
// first loop create and store elements
for (i = 1; i <= 5; i++) {
var html = '<div id="test-' + i + '" style="float: left; border: 2px solid black; width: 50px; height: 50px; margin: 5px;">' + i + '</div>';
document.getElementById('test').innerHTML += html;
var element = document.getElementById('test-' + i);
//console.log(element);
list.push(element);
}
// second loop use stored elements
for (index in list) {
//console.log(list[ index ]);
list[ index ].style.backgroundColor = 'yellow';
}
You can see only the last element became yellow.
I'll really appreciate if someone have an idea.
Thanks!
Setting the innerHTML of the container will recreate its entire DOM tree.
Therefore, all of the old elements from the previous iteration are no longer attached to the DOM.
You should only set innerHTML once.
because you aren't creating the elements, so they can't be found with the selector you are using. Try using document.createElement, then div.appendChild(element)
Each time you set the innerHTML of the container, it destroys all of the child elements and recreates them. This is a problem because now your array contains references to elements that no longer exist, apart from the final iteration.
The solution is to work with DOM elements, not raw HTML.
Fiddle
var list = [];
var container = document.getElementById("test");
// first loop create and store elements
for (var i = 1; i <= 5; i++) {
var element = document.createElement('div');
element.id = 'test-' + i;
element.className = 'mydiv';
element.appendChild(document.createTextNode(i));
container.appendChild(element);
list.push(element);
}
// second loop use stored elements
for (var x=0; x<list.length; x++) {
list[ x ].style.backgroundColor = 'yellow';
}
Side notes:
for..in can be used on arrays, but you need to use hasOwnProperty, or just use a basic for loop like above.
var list = \[\] is preferred to var list = new Array() – more info.
Remember to declare count variables like i using var to make them locally scoped, otherwise they will be scoped globally.

How to get the result of the clone() function from javascript?

this is my code :
html = "";
for (i = 0; i < id_array.length; i++){
html = html.concat($(id_array[i]).clone(true));
}
console.log(html);
The id_array contains 3 ids of the <tr> tag . Instead of the html code from the ids , the result of the html variable is object object object ... Why ? How do I get the html code from this id ?
This is my html code , it is not written by me , it is generated by JQgrid plugin. so i took a picture:
It looks like your want to call outerHTML. In order to do it, you need the native DOM element, you can get it using [0] or get(0) :
var html = "";
for (i = 0; i < id_array.length; i++){
html += $(id_array[i])[0].outerHTML;
}
console.log(html);
clone returns jQuery objects. You don't want to concat them with an empty string. Instead, use an array to store them:
trs = [];
for (i = 0; i < id_array.length; i++){
trs.push($(id_array[i]).clone(true));
}
console.log(trs);
You don't want to use HTML strings when dealing with the DOM.
It seems you may want the outer HTML of the TR elements. Some browsers support it, but not all (and surprisingly not jQuery). In this case you can do something like:
var id_array = ['tr0','tr1','tr2'];
var html = "";
var tbody = $('<tbody>');
for (i = 0; i < id_array.length; i++) {
tbody.append($('#' + id_array[i]).clone(true));
html += tbody.html();
tbody.html('');
}

Remove characters from an element while using appendChild

I'm trying to remove or replace characters in an element while using appendChild as follow:
var options = from.getElementsByTagName("option");
var to = document.getElementById("target");
to.appendChild(options[i].replace("(A)",""));
I tried various different syntax but no luck. Can someone help? Either JQuery or javascript works for me.
Thanks
I assume you're already in a for loop. If so, use the .text property of the option element, and create a new text node.
to.appendChild(document.createTextNode(options[i].text.replace("(A)","")));
Or better, in the loop append to a string, and create a single node at the end.
var txt = "":
for (var i = 0; i < options.length; ++i)
txt += options[i].text.replace("(A)"), "");
}
to.appendChild(document.createTextNode(txt));
If you actually wanted to append a copy of the element itself, then use .cloneNode(true) instead.
for (var i = 0; i < options.length; ++i) {
var clone = to.appendChild(options[i].cloneNode(true));
clone.text = clone.text.replace("(A)", "");
}

Dynamically loading multiple <li>'s with a javascript for loop - nothing loading yet

I'm trying to load X amount of <li>'s into a <ul> via a for loop in a jquery function, and while I think I've got the syntax about right I'm not getting anything loading. (no problem with loading a single <li>, but none for multiples with the method I've tried)
Initially I attempted to pass a variable into the loop to determine the amount of increments: var peekListAmount = 5;
That didn't work so I went for a bog-standard loop incrementer. That doesn't work either so, after searching here and getting close, I have put together a fiddle to see if someone can point out what I'm doing wrong: http://jsfiddle.net/janowicz/hEjxP/8/
Ultimately I want to use Knockout.js to dynamically input a number to pass to the loop amount variable, but 1st things 1st.
Many thanks in advance.
When you do:
var peekListItem = $('<li>...</li>');
you're creating a single instance of an <li> node, encapsulated in a jQuery object.
Appending an already-present node to the DOM just removes it from its current place in the DOM tree, and moves it to the new place.
You need to create the node inside the loop, not outside, otherwise you're just re-appending the same node each time, not a copy of that node.
In fact, given you're not manipulating that node, you can just put the required HTML directly inside the .append() call without wrapping it in $(...) at all:
$(function() {
var peekList = $('<ul class="peekaboo-list">').appendTo('div.peekaboo-wrap');
function addLiAnchorNodes(nodeAmount) {
var html = '<li>' +
'<p class="peekaboo-text"></p></li>';
for (var i = 0; i < nodeAmount; ++i) {
peekList.append(html);
}
}
addLiAnchorNodes(5);
});
See http://jsfiddle.net/alnitak/8xvbY/
Here is you updated code
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
var peekListItem = '<li><p class="peekaboo-text"></p></li>';
//var peekListAmount = 5;
var tmp = '';
var addLiAnchorNodes = function (nodeAmount){
//var nodeAmount = peekListAmount;
for (var i = 0; i < 10; i++){
tmp += peekListItem;
}
peekList.append(tmp);
$('div.peekaboo-wrap').append(peekList); // This bit works fine
}
addLiAnchorNodes();
});
This should work. Instead of appending the list item in each loop, append the list only once at the end.
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
peekList.appendTo('div.peekaboo-wrap');
var addLiAnchorNodes = function (nodeAmount){
var list = "";
for (var i = 0; i < 10; i++){
list += '<li>Sample<p class="peekaboo-text"></p></li>';
}
peekList.append(list);
}
addLiAnchorNodes();
});
Here is the updated fiddle
Try this:
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
$(peekList).appendTo('div.peekaboo-wrap'); // This bit works fine
var addLiAnchorNodes = function (nodeAmount){
//var nodeAmount = peekListAmount;
for (var i = 0; i < 10; i++){
var peekListItem = $('<li><p class="peekaboo-text"></p></li>');
peekListItem.appendTo(peekList);
}
}
addLiAnchorNodes();
});

anyway to delete all <a href=> tags with javascript after the page loads on an iPad?

I know that I can run a line of javascript code after the page loads on an iPad with UIWebView (which is what I am using), but I do not know what I could type to go through and remove all of the tags. I also would like to be able to do this to only certain parts of the page e.g. only remove tags within a certain tag.
You can get all elements by tag name using document.getElementsByTagName(). All links have the tag name a. You can visually remove them by setting their display style to none.
var elements = document.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
elements[i].style.display = 'none';
}
To remove elements of a certain tag within a certain tag, just invoke getElementsByTagName() on the element in question. Suppose that you want to hide all links inside a <li> only:
var listitems = document.getElementsByTagName('li');
for (var i = 0; i < listitems.length; i++) {
var anchors = listitems[i].getElementsByTagName('a');
for (var j = 0; j < anchors.length; j++) {
anchors[j].style.display = 'none';
}
}
The element.parentNode.removeChild(element) is also a good one, but it doesn't work nicely inside a standard for loop. You need to loop backwards:
var elements = document.getElementsByTagName('a');
for (var i = elements.length; i-- > 0;) {
var element = elements[i];
element.parentNode.removeChild(element);
}
Update as per the clarified functional requirement: you thus want to replace the link element with a text node representing the link's original content? You can use Node.replaceChild() for this. Here's a kickoff example:
var elements = document.getElementsByTagName('a');
for (var i = elements.length; i-- > 0;) {
var element = elements[i];
var text = document.createTextNode(element.firstChild.nodeValue);
element.parentNode.replaceChild(text, element);
}
Thought I'd post a remove() function to complement BalusC:
function remove(el){
if(el.parentNode)el.parentNode.removeChild(el);
}
Note: If the element doesn't have a parent, it means it is not in the DOM tree.
It also means it will be removed in the GC's (garbage collector) next run (as long as there are no references to it).
If you're going to be doing a lot of dom manipulation, it might be worth it to include jQuery to grab your elements. It'd be a little easier to remove items. Eg.
$(function(){
$('.surrounding_class a').remove();
});
If you want to remove links but preserve their display contents (text, images, etc.) you can insert their childNodes before the links, then remove the link elements:
var removeLinks = function(context) {
var undefined;
if(context === undefined) {
context = document;
}
if(!context) {
return false;
}
var links = context.getElementsByTagName('a'), i, link, children, j, parent;
for(i = 0; i < links.length; i++) {
link = links[i];
parent = link.parentNode;
if(!link.href) {
continue;
}
children = link.childNodes;
for(j = 0; j < children.length; j++) {
parent.insertBefore(children[j], link);
}
parent.removeChild(link);
}
return context;
};
// Use:
removeLinks(document.getElementById('container'));

Categories