So much confusion, so few answers. I'm trying to loop through the DOM, looking for a specific node by id, however, this code has several problems for which I have no explanation. First, the length of the childNodes list comes up as '5'. Two "ul"'s, two "id"'s, if those count...and one for luck?
Second, it dies at if(y[i].hasAttribute('id')===true). Firebug says this is not a function. I have no reason to not believe it, but am not sure why it isn't.
Thank you for any help.
<div id="list">
<ul id="first"></ul>
<ul id="second"></ul>
</div>
<script>
var comments=document.getElementById('list')
var y=comments.childNodes;
var count=y.length
for(i=0;i<count;i++)
{
document.write(y.length);
if(y[i].hasAttribute('id')===true)
{ document.write('here!');}
}
</script>
the childNodes attribute contains all nodes in the DOM, which specifically means, it includes text nodes. you have 3 of them - the newline/linefeed characters inside your div.
you can test for element children using the nodeType attribute ( see eg. here; 1represents ELEMENT_NODE, 3 stands for TEXT_NODE).
If you use a tool like Firebug and inspect the DOM itself, you would see all the children of an element and the difference between .children and .childNodes .
It's by hunting around in the DOM that I discovered why there are so many things that at first appear to be duplicates of each other, but are definitely not. The Mozilla developers site developer.mozilla.org is also a wealth of information.
Related
I am just beginning to learn client-side JavaScript and using an online tutorial, so please bear with me.
This question is based on my understanding of the following:
To access the properties of the document's body, the syntax is "document.body", which returns all the elements in the body.
Similarly when you access the head, you use "document.head". Makes sense and most importantly, it works.
However, when I attempt to access elements WITHIN the body or head following the same logic, I get a return value of "undefined". For example, document.body.h1, returns "undefined", in spite of there being an h1 element inside the body element.
Further, when I enter document.head.title -- "undefined".
Strangely, however, when I enter "document.title", it returns the string value associated with the title tag.
I thought in order to access the title, you would have to access it through the head, since it is an element nested inside the head. But ok, that's fine. Using the same logic, I should then be able to enter document.h1 and get its value. Nope, instead, I get undefined.
Would someone be kind enough to explain to me why this behavior is so inconsistent. Thanks in advance.
You've really asked two questions:
Why document.title rather than document.head.title?
and
Why doesn't document.body.h1 return an element if there's an h1 in the body?
document.title
document.title is historical. Various parts of the browser environment were developed somewhat ad hoc by multiple different people/organizations in the 1990s. :-) That said, it's the title of the document, so this isn't an unreasonable place to put it, even if you use the title tag in head.
document.body.h1
One answer is: Because no one decided to design it that way. There were some early things like document.all (a list of all elements in the document) and even tag-specific ones (I forget exactly what they were, but they weren't a million miles off your document.body.h1 — I think document.tags.h1 or something, where again it was a list.)
But another answer is: Because the DOM is a tree. body can have multiple h1 elements, both as direct children and as children of children (or deeper); collectively, descendants. Creating automatic lists with all of these proved not to be scalable to large documents.
Instead, you can query the DOM (either the entire document, or just the contents of a specific element) via a variety of methods:
getElementById - (Just on document) Get an element using its id attribute value.
querySelector - Find the first element matching a CSS selector (can use it on document or on an element). Returns null if there were no matches.
querySelectorAll - Get a list of all elements matching a CSS selector (can use it on document or on an element). You can rely on getting back a list; its length may be 0, of course.
getElementsByTagName - Get a list of all elements with a given tag name (such as "h1").
getElementsByClassName - (No support in IE8 and earlier) Get a list of all elements with a given class.
There are many more. See MDN's web documentation and/or the WHAT-WG DOM Standard for more.
Some of the automatic lists persist (they got so much use that they had to be maintained/kept), such as document.forms, document.links, the rows property on HTMLTableElement and HTMLTableSectionElement instances, the cells property on HTMLTableRowElement instances, and various others.
document.head.title is a thing... but not what you might think.
title is an attribute that is applicable to all html elements; that is, it is a global attribute. It's meaning is 'advisory information'; one use is to display a tooltip:
<span title="hover over me and you'll see this">information</span>
So, all elements have a title attribute - including head. The title element - which is completely different - should be a child of the head though. So you might be tempted to set its value via document.head.title = "my title" , but document.head.title is not the head's title element, it's a property of the head element.
What you're actually doing is setting the title property on the head element:
<head title="my title">.... </head>
... which isn't what you want at all.
The correct way to set the title is document.title, which is a shortcut way of doing
document.querySelector("title").innerText = "my title"
I was wondering about two different syntax of selecting element in JavaScript.
suppose if I want to select all divs from current document then:
var divs = document.getElementsByTagName("div");
console.log("There are "+divs.length+" Divs in Document !");
Will work fine. But there is also another way of doing so, like:
var divs = document.querySelectorAll("div");
console.log("There are "+divs.length+" Divs in Document !");
When both of them works in the same way. What's the difference between them ?
Which one is faster?
Why?
How both works?
Thanks in advance. I've seen the questions like this but they didn't satisfied the need.
Most answeres are wrong. Nicolae Olariu is the only, who answered correcly
Which one is faster? Why?
are not the questions. The real question "How it works?"
The main difference is in this example:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Yandex</title>
</head>
<body>
Яндекс,
Yandex
</body>
<script>
var elems1 = document.getElementsByTagName('a'), // return 2 lements, elems1.length = 2
elems2 = document.querySelectorAll("a"); // return 2 elements, elems2.length = 2
document.body.appendChild(document.createElement("a"));
console.log(elems1.length, elems2.length); // now elems1.length = 3!
// while elems2.length = 2
</script>
</html>
Because querySelectorAll returns a static (not live) list of elements.
Selections
getElementsByTagName only selects elements based on their tag name. querySelectorAll can use any selector which gives it much more flexibility and power.
Return value
gEBTN returns a live node list.
qSA returns a static node list.
Live node lists can be useful (you can query once, store the value, and have it update as the DOM changes) but are responsible for a lot of confusion such as the example in this question.
Usually a static list is easier to deal with.
Support
See caniuse for gEBTN and qSA.
gEBTN has more support, but qSA has support in all browsers that are relevant for most use cases today.
Performance
You probably shouldn't care. These functions are unlikely to be a bottleneck in your code.
I've seen conflicting reports about which is faster. It likely varies between browsers anyway.
From MDN:
element = document.querySelector(selectors);
Returns the first element within the document (using depth-first pre-order traversal of the document's nodes) that matches the specified group of selectors.
elements = element.getElementsByTagName(tagName)
Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments.
querySelector also supports other CSS selectors such as "#id" to get an element by id, and "input[type=text]" to get all input elements with a type=text attribute. See here for more details.
They would probably be about equally fast for simple queries like the one you asked about, but for advanced CSS selectors it is likely much faster (not to mention much less code to write) to use querySelectorAll than applying some manual filtering yourself, which is why libraries like jQuery use querySelectorAll when the browser supports it.
Here is an example about the difference between querySelector and getElementsByTagName.
In this example,the writer choose the querySelector to solve the problem.
The getElementsByTagName also returns a live nodeList, and when we append the links to the in-memory unordered list, the link is removed from the document, and the length of the collection is affected.
So
if(you don't want to change the NodeList during the follow-up script work){
"use querySelectorAll"}
else if(you want to change the NodeList during the follow-up script work) {
"use getElementsByTagName"
}
And you can have a try to use getElementsByTagName in this example,you will see it can't work.
Selectors
Apart from the return values already covered in another answers one other key difference is that getElementBy*TagName ( or -Id -Class ) and querySelector/querySelectorAll is that the latter accepts a selector where the others a tag, id or class. With querySelector() you can do things like:
document.querySelectorAll('p.my16')
which is not possible with getElementByTagName
ForEach
If one needs a foreach on the result of a getElements* function a simple trick (spread) will allow that.
[...document.getElementsByClassName('my16')].forEach(e=>console.log(`hallo ${e}`))
Btw. I agree with the poster that performance is highly irrelevant. It makes sense to focus on the use case at hand.
Both the selectors give different outputs. Check the image.
In this example:
<html>
<head>
<meta charset="utf-8">
<title>Yandex</title>
</head>
<body>
Яндекс,
Yandex
</body>
<script>
var elems1 = document.getElementsByTagName('a'), // return 2 lements, elems1.length = 2
elems2 = document.querySelectorAll("a"); // return 2 elements, elems2.length = 2
document.body.appendChild(document.createElement("a"));
console.log(elems1.length, elems2.length); // now elems1.length = 3!
// while elems2.length = 2
</script>
</html>
the element that is created is placed after the script tag, and cannot be read by querySelector. Only the getElementsByTagName can find the new element.
There is HTML page with contents like this.
Documentation at MDN says that childNodes returns a collection of child nodes of the given element which is a NodeList.
So, according to the doc, the first child for the NodeList should be <h1>PyCon Video Archive</h1>.
But, in Developer Tools (Chromium), it says the other way.
![enter image description here][2]
So, why exactly the first node is not <h1>PyCon Video Archive</h1>?
Why a text object as first element?
I would appreciate some help here.
EDIT
So, I just figured out that in Firebug (FF), the same function behaves differently.
My new question: Is using .childNodes() an unreliable way of accessing DOM elements?
To get the first element child, you can use...
document.body.firstElementChild;
...but older brwosers don't support it.
A method that has greater support is the children collection...
document.body.children[0];
...which has pretty good support but still has some holes in terms of older browsers.
(Just double checked, and as long as you don't support Firefox 3, and as long as you don't include HTML code comments in the markup, using .children will be safe.)
To ensure that you have the widest browser support, create a function...
function firstElementChild( parent ) {
var el = parent.firstChild;
while( el && el.nodeType !== 1 )
el = el.nextSibling;
return el;
}
and use it like this...
var h1 = firstElementChild( document.body );
Because there's a white-space text-node before the h1 element. Presumably, in the source (if you view source), the h1 opening tag's been either indented, or moved to a new line within the body (or both) in order for readability. At a guess, I'd imagine that it's something like the following:
<body>
<h1>PyCon Video Archive</h1>
<!-- ...other html... -->
If you revise that to:
<body><h1>PyCon Video Archive</h1><!-- ...other html... -->
Then the first childNode will, indeed, be the h1 element.
It's worth noting that text, even outside of an element tag, is still a child-node of the parent element. Albeit one that can't be easily targeted with a selector.
HTML markup:
<div>
<a id="foo"> </a>
</div>
jQuery:
$('div').each(function(){
$('#foo', this).dosmth(); // 1
$('#foo').dosmth(); // 2
});
Which method would be faster to run dosmth?
Since we're getting a variety of answers, hopefully here's some clarity (check the examples here):
The fastest - There's no need to loop. Skip the $("div").each part and just do $("#foo"). foo is an ID, and thus lookup is instantaneous.
Middling - $("#foo") in a loop. Note that you also don't want this because it will execute the function for every div on the page (and for this reason on a larger document with a lot of divs this would be the slowest).
Slowest - $("#foo", this). The context node doesn't help in the first place, and then consider that jQuery will first build a jQuery object out of this and turn it into $(this).find("#foo"). That's all unnecessary, of course.
Bottom line: in most cases (e.g. sometimes when confirming that an ID is in one context and not another) context nodes are unnecessary with ID lookup.
Here are some resources from the jQuery source:
Handling for most of the cases here - note that $("#id") is singled out for handling as document.getElementById
find - what happens when you pass a context
Since an #id should be unique in the DOM your markup will be invalid (I am assuming more than one <div/> based upon using .each())
Change the id to a class and use the following:
<div>
<a class="foo"> </a>
</div>
<div>
<a class="foo"> </a>
</div>
And the script
$('div').each(function(){
$('.foo', this).dosmth(); //or $(this).find(".foo");
});
But if you only have one element with an id of foo selecting by id will be the fastest, plus you can drop the need for using .each()
$('#foo').dosmth(); //or document.getElementById("foo");
jquery selectors by id only is the fastest way to search because it uses getElementbyId in javascript.
so this one is the fastest:
$('#foo').dosmth();
if you use a context like:
$('#foo', this).dosmth();
it is translated into:
$(this).find('#foo').dosmth();
so that will make another useless operation because your #foo is unique
Regards,
Max
$('#foo', this).dosmth();
This will search within the context of the div and not the whole DOM, which will make the selector faster. This only makes sense to use when the DOM is large, otherwise, just use the normal selector: $('#foo').dosmth();
If you're using an id there's only ever going to be one. So you can just do:
$('a#foo').dosmth();
You don't need to use each() to go through each div and get all the a#foo's out of it. That WILL waste time, creating loops for no reason. Instead use:
$('a#foo').each(function(){ ... });
or even just:
$('a#foo').dosmth();
You can also do $('div a#foo').dosmth(); if you want.
Please read the discussion below regarding this answer or check Bryan answer above about the differences in speed of the selectors.
I would go with
$('a#foo', this).dosmth();
Update But instead of retrieving all the divs before, I would check for
only the desired one at the first time
like this
$('div a#foo').each(function(){
}
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?