When clearing HTML elements such as select boxes, tables, or lists, is it better/faster to remove the nodes (e.g., select.options.remove(i), table.deleteRow(i)) or just empty the innerHTML (e.g., select.innerHTML = "")? Or does it matter?
An example case would be reinitializing a table. A specific select field's value should load different values for a subsequent HTML table. When the select value changes, the table needs to be reinitialized.
In IE you cannot set the innerHTML of a select element. So for a cross-browser solution the only way is to add/remove child nodes.
I made a new test that isn't broken.
http://jsperf.com/innerhtml-vs-removechild/67
It's not perfect either since part of the sample setup is part of the test so this might skew the results.
This gives that innerHTML is faster but I don't know by how much.
Per this conversation here: What is the best way to empty an node in JavaScript
It appears that the while (elm.firstChild) {elm.removeChild(elm.firstChild);} approach got the the best results across browsers in this test.
(Would have put this as a comment instead of an answer, but the comments are coming in awful fast, so I didn't want it to get lost.)
Case 1:-
Run on 1000 Child DOM Element to Remove.
Which one is Faster?
Remove Child (5,676,264 ops/sec ±1.46%) --> Faster
innerHTML = "" (4,359,867 ops/sec ±1.46%) --> Slower
Case 2:-
Which one is able to remove event handler of child Nodes?
Remove Child --> Able to remove event handler
innerHTML = "" --> Not able to remove event handler
Result:-
Seems It's better to used Remove Child for removing any child nodes.
Ref:-
https://www.measurethat.net/Benchmarks/Show/6910/0/innerhtml-vs-removechild-vs-remove
https://rusingh.com/javascript-benchmark-removechild-vs-innerhtml/
Related
OK. I'm trying to get elements in javascript by CSS selector. To get one by custom element I know is like 'element[custom-name="Literal name"]'.
OK. This works. But now I need to get the second one. I mean I have some elements with the exact custom-name, and for everyone I need to apply a diferent rule (tehere are only 5).
How can I select the other ones? Is posibble select them by CSS?
PS: They are located in random positions, so maybe the first one is the 5 element one time and if I refresh the page it can be the 10 element inside the container.
PS2: No, It's not possible to change the HTML in my case :( . The only code I'm alowed to change is CSS and javascript.
Thanks for reading me! :D
Assuming you can't select specific ones by another, non-order-dependent factor, you can use the pseudo-selector :nth-child. In your case, the complete CSS selector would be element[custom-name="Literal name"]:nth-child(2) - substitute the 2 for any other number as you see fit. Generally it's not the best idea to select only by position in the document, as position may change more often than attributes - but in any case, there's a pure CSS solution!
Note that this only works if the elements you're working with are the only children of a common parent element - if you're looking for the second element that matches that query in general across the entire document, there is no way to do that with a CSS selector. Instead, you can make sure to add a unique class or other differentiating attribute to each element, or simply use querySelectorAll - in that case, you could get the second element using this little snippet: document.querySelectorAll('element[custom-name="Literal name"]')[1].
I am writing a userscript that grabs an element with something like:
var theElement = $('div.someClass:last');
To grab the last element in the class .someClass so I can parse it.
This is where my question comes in. There is another script on this page dynamically adding a new <div class="someClass"> every once in a while. I want to always have the last element on the page with a class of .someClass selected.
Will Javascript/jQuery always have the latest element or will I have to manually "refresh" it?
Sushanth's answer is correct, but according to this article you could use
var theElements = document.getElementsByClassName('someClass');
and then reliably use
var theElement = $(theElements[theElements.length - 1]); // wrapping in $() is optional
which is worth doing as $(selector) is quite an expensive operation to perform.
edit - only for ie9 and above though http://caniuse.com/getelementsbyclassname
Your selector is evaluated and the result is returned. If you want to do what you're asking, you'll have to re-evaluate that selector.
Nope .. It will not be automatically refreshed.
Every single time you modify something directly on the selector it is a good idea to cache it again.
The selector you wish is not a live Node list.
In such cases if there seem to any changes in the selector I prefer not to cache at all in the first case.
So that I can directly use the selector and not the cached one.
This is the same question as this:
Referring to a div inside a div with the same ID as another inside another
except for one thing.
The reason there are two elements with the same ID is because I'm adding rows to a table, and I'm doing that by making a hidden div with the contents of the row as a template. I make a new div, copy the innerhtml of the template to my new div, and then I just want to edit bits of it, but all the bits have the same ID as the template.
I could dynamically create the row element by element but it's a VERY complex row, and there's only a few things that need to be changed, so it's a lot easier to just copy from a template and change the few things I need to.
So how do I refer to the elements in my copy, rather than the template?
I don't want to mess up the template itself, or I'll never be able to get at the bits for a second use.
Or is there another simpler way to solve the problem?
It will probably just be easiest when manipulating the innerHtml to do a replace on the IDs for that row. Maybe something like...
var copiedRow = templateRow.innerHTML.replace(/id=/g,"$1copy")
This will make the copied divs be prefixed with "copy". You can develop this further for the case that you have multiple copies by keeping a counter and adding that count variable to the replace() call.
When you want to make a template and use it multiple times its best to make it of DOM, in a documentFragment for example.
That way it doesn't respond to document.getElementById() calls in the "live" DOM.
I made an example here: http://jsfiddle.net/PM5544/MXHRr/
id's should be unique on the page.
PM5544...
In reality, there's no use to change the ID to something unique, even though your document may not be valid.
Browsers' selector engines treat IDs pretty much the same as class names. Thus, you may use
document.querySelector('#myCopy #idToLookFor');
to get the copy.
IDs on a page are supposed to be unique, even when you clone them from a template.
If you dynamically create content on your page, then you must change the id of your newly cloned elements to something else. If you want to access all cloned elements, but not the template, you can add a class to them, so you can refer to all elements with that class:
var clonedElement = template.cloneNode(yes); // make a deep copy
clonedElement.setAttribute("id", "somethingElse"); // change the id
clonedElement.setAttribute("class",
clonedElement.getAttribute("class") + " cloned"
);
To access all cloned elements by classname, you can use the getElementsByClassName method (available in newer browsers) or look at this answer for a more in-depth solution: How to getElementByClass instead of GetElementById with Javascript?
Alternatively, if you have jQuery available, you can do this is far less lines of code:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned").appendTo("#someDiv");
The class lookup is even simpler:
$(".cloned").doSomethingWithTheseElements();
Try to avoid using IDs in the child elements of the cloned structure, as all ids of the cloned element should be changed before adding the clone to the page. Instead, you can refer to the parent element using the new id and traverse the rest of the structure using classnames. Class names do not need to be unique, so you can just leave them as they are.
If you really must use ID's (or unique "name" attributes in form fields), I can strongly suggest using a framework like jQuery or Prototype to handle the DOM traversal; otherwise, it is quite a burden to resolve all the cross-browser issues. Here is an example of some changes deeper in the structure, using jQuery:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned") // add a cloned class to the top element
.find("#foo").attr("id","bar").end() // find and modify a child element
.appendTo("#someDiv"); // finally, add the node to the page
Check out my ugly but functional cheese. I wrote a function that works like getelementbyid, but you give it a start node instead of the document. Works like a charm. It may be inefficient but I have great faith in the microprocessors running today's browsers' javascript engines.
function getelement(node, findid)
{
if (node)
if (node.id)
if (node.id == findid)
return node;
node = node.firstChild;
while(node)
{
var r = getelement(node, findid);
if (r != null)
return r;
node = node.nextSibling;
}
return null;
}
When you copy the row, don't you end up having a reference to it? At that point can't you change the ID?
Here's what I'm trying to do: I have a bookmarklet that is looking for elements in the current page (which can be any site) and dispatch a click event on the ones that match. I have that part working.
In some cases though, nothing matches automatically and I want to be able to show (by hovering it) what element should be activated and then save some info about it in localStorage. The next time I'm using the bookmarklet on that page, I want to retrieve that info to identify the element in the DOM and then dispatch a click event.
The question is: what information should I save to be able to identify it? (in most cases, since it will always be possible to create a case where it doesn't work)
In the best case, said-element will have an id value and I'm good to go. In some other cases, it won't and I'd like to see your suggestions as to what info and what method I should use to get it back.
So far my idea is to save some of the element's properties and traverse the DOM to find elements that match everything. Not all properties will work (e.g. clientWidth will depend on the size of the browser) and not all types of elements will have all properties (e.g. a div node won't have a src value), which means that on one hand, I can't blindly save all properties, but on the other, I need to either choose a limited list of properties that will work for any kinds of element (at the risk of losing some useful info) or have different cases for different elements (which doesn't sound super great).
Things I was thinking I could use:
id of course
className, tagName would help, though className is likely to not be a clear match in some cases
innerHTML should work in a lot of cases if the content is text
src should work in most cases if the content is an image
the hierarchy of ancestors (but that can get messy)
...?
So, my question is a bit "how would you go about this?", not necessarily code.
Thanks!
You could do what #brendan said. You can also make up a jQuery-style selector string for each element in the DOM by figuring out the element's "index" in terms of its place in its parent's list of child nodes, and then building that up by walking up the DOM to the body tag.
What you'd end up with is something that looks like
body > :nth-child(3) > :nth-child(0) > :nth-child(4)
Of course if the DOM changes that won't work so good. You could add class names etc, but as you said yourself things like this are inherently fragile if you don't have a good "id" to start with, one that's put there at page creation time by whatever logic knows what's supposed to be in the page in the first place.
an approach would be using name, tagName and className-combination. innerHTML could may be too big.
another approach would be to look for child elements of your choosen element which have an id.
check for id => check for childs with id => check for name, tagName and className-combination (if => tell user to choose a different item :-)
What about finding all elements without an ID and assigning them a unique id. Then you could always use id.
What about using the index (integer) of the element within the DOM? You could loop through every element on page load and set a custom attribute to the index...
var els = document.getElementsByTagName("*");
for(var i = 0, l = els.length; i < l; i++) {
els[i].customIndex = i;
}
I have a treeview with drag and drop functionality. Upon completion of drag and drop I need to find the count of the child nodes of the new parent node. So for that I'm using the following
lines
var childnodelength=elem.parentNode.parentNode.childNodes.length;
alert(childnodelength);
But I always get the same value of 4 irrespective of the no. of childs, with the above alert.
I've also tried in the following way.
alert(elem.getElementsByTagName("A")[0].childNodes.length);
Above line always gives me 1 irrespective of the no. of childs. I am not sure if I am referring correctly in both the ways.
And hence I'm unable to find the no. of child nodes.
Please could someone help me with this?
Thanks
Keep in mind that childNodes only looks one level down, so the value will be the same so long as you have the same number of immediate children. Perhaps what you wanted was to count all the children all the way down the tree?
If you want all the child elements, you can use getElementsByTagName("*"). Note that the use of the "*" argument doesn't work in IE5.5, which could also pose trouble for more recent versions of IE when running in quirks mode.
Another good thing to know about childNodes.length is that it may return a different value in IE than other browsers because of different ways of counting text nodes . . . If you want to exclude text nodes and only count elements you can loop through the childNodes and check the value of nodeType (1 = ELEMENT_NODE, 2 = ATTRIBUTE_NODE, 3 = TEXT_NODE, etc.) You can also use children instead of childNodes if you only want element nodes, but in IE this incorrectly counts comment nodes, and in Firefox it wasn't supported until version 3.5.
Perhaps the child node is a DIV or Table that wraps up the nodes you need to count?