querySelectorAll doesnt do anything - javascript

Why i cant get this extremly simple script to work. It works with querySelector, but not with querySelectorAll
HTML
<p id="log2"></p>
<script type="text/javascript" src="dom.js"></script>
JS
var q = document.querySelectorAll("#log2").innerHTML+="<p>qwerty</p>";

querySelectorAll returns a NodeList (which is like an array), not a single HTMLElement.
You need to either grab the first element from it ([0]) or loop over it.
There is no point in using All with an ID selector since IDs must be unique.
<p class="log2"></p>
<p class="log2"></p>
<p class="log2"></p>
<script>
var nodes = document.querySelectorAll(".log2");
for (var i = 0; i < nodes.length; i++) {
nodes[i].innerHTML+="<p>qwerty</p>";
};
</script>

document.querySelectorAll returns a NodeList of elements (i.e. multiple elements).
To pick up the first element use either document.querySelectorAll("#log2")[0], or document.querySelector("#log2").
In your case you are trying to get an element by ID, which should be only one element on the page. Hence, you may use cross browser method document.getElementById("log2") instead.

Related

Why does jQuery return more than one element when selecting by type and ID? [duplicate]

I fetch data from Google's AdWords website which has multiple elements with the same id.
Could you please explain why the following 3 queries doesn't result with the same answer (2)?
Live Demo
HTML:
<div>
<span id="a">1</span>
<span id="a">2</span>
<span>3</span>
</div>
JS:
$(function() {
var w = $("div");
console.log($("#a").length); // 1 - Why?
console.log($("body #a").length); // 2
console.log($("#a", w).length); // 2
});
Having 2 elements with the same ID is not valid html according to the W3C specification.
When your CSS selector only has an ID selector (and is not used on a specific context), jQuery uses the native document.getElementById method, which returns only the first element with that ID.
However, in the other two instances, jQuery relies on the Sizzle selector engine (or querySelectorAll, if available), which apparently selects both elements. Results may vary on a per browser basis.
However, you should never have two elements on the same page with the same ID. If you need it for your CSS, use a class instead.
If you absolutely must select by duplicate ID, use an attribute selector:
$('[id="a"]');
Take a look at the fiddle: http://jsfiddle.net/P2j3f/2/
Note: if possible, you should qualify that selector with a type selector, like this:
$('span[id="a"]');
The reason for this is because a type selector is much more efficient than an attribute selector. If you qualify your attribute selector with a type selector, jQuery will first use the type selector to find the elements of that type, and then only run the attribute selector on those elements. This is simply much more efficient.
There should only be one element with a given id. If you're stuck with that situation, see the 2nd half of my answer for options.
How a browser behaves when you have multiple elements with the same id (illegal HTML) is not defined by specification. You could test all the browsers and find out how they behave, but it's unwise to use this configuration or rely on any particular behavior.
Use classes if you want multiple objects to have the same identifier.
<div>
<span class="a">1</span>
<span class="a">2</span>
<span>3</span>
</div>
$(function() {
var w = $("div");
console.log($(".a").length); // 2
console.log($("body .a").length); // 2
console.log($(".a", w).length); // 2
});
If you want to reliably look at elements with IDs that are the same because you can't fix the document, then you will have to do your own iteration as you cannot rely on any of the built in DOM functions.
You could do so like this:
function findMultiID(id) {
var results = [];
var children = $("div").get(0).children;
for (var i = 0; i < children.length; i++) {
if (children[i].id == id) {
results.push(children[i]);
}
}
return(results);
}
Or, using jQuery:
$("div *").filter(function() {return(this.id == "a");});
jQuery working example: http://jsfiddle.net/jfriend00/XY2tX/.
As to Why you get different results, that would have to do with the internal implementation of whatever piece of code was carrying out the actual selector operation. In jQuery, you could study the code to find out what any given version was doing, but since this is illegal HTML, there is no guarantee that it will stay the same over time. From what I've seen in jQuery, it first checks to see if the selector is a simple id like #a and if so, just used document.getElementById("a"). If the selector is more complex than that and querySelectorAll() exists, jQuery will often pass the selector off to the built in browser function which will have an implementation specific to that browser. If querySelectorAll() does not exist, then it will use the Sizzle selector engine to manually find the selector which will have it's own implementation. So, you can have at least three different implementations all in the same browser family depending upon the exact selector and how new the browser is. Then, individual browsers will all have their own querySelectorAll() implementations. If you want to reliably deal with this situation, you will probably have to use your own iteration code as I've illustrated above.
jQuery's id selector only returns one result. The descendant and multiple selectors in the second and third statements are designed to select multiple elements. It's similar to:
Statement 1
var length = document.getElementById('a').length;
...Yields one result.
Statement 2
var length = 0;
for (i=0; i<document.body.childNodes.length; i++) {
if (document.body.childNodes.item(i).id == 'a') {
length++;
}
}
...Yields two results.
Statement 3
var length = document.getElementById('a').length + document.getElementsByTagName('div').length;
...Also yields two results.
What we do to get the elements we need when we have a stupid page that has more than one element with same ID? If we use '#duplicatedId' we get the first element only. To achieve selecting the other elements you can do something like this:
$("[id=duplicatedId]")
You will get a collection with all elements with id=duplicatedId.
From the id Selector jQuery page:
Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM. This behavior should not be relied on, however; a document with more than one element using the same ID is invalid.
Naughty Google. But they don't even close their <html> and <body> tags I hear. The question is though, why Misha's 2nd and 3rd queries return 2 and not 1 as well.
If you have multiple elements with same id or same name, just assign same class to those multiple elements and access them by index & perform your required operation.
<div>
<span id="a" class="demo">1</span>
<span id="a" class="demo">2</span>
<span>3</span>
</div>
JQ:
$($(".demo")[0]).val("First span");
$($(".demo")[1]).val("Second span");
Access individual item
<div id='a' data-options='{"url","www.google.com"}'>Google</div>
<div id='a' data-options='{"url","www.facebook.com"}'>Facebook</div>
<div id='a' data-options='{"url","www.twitter.com"}'>Twitter</div>
$( "div[id='a']" ).on('click', function() {
$(location).attr('href', $(this).data('options').url);
});
you can simply write $('span#a').length to get the length.
Here is the Solution for your code:
console.log($('span#a').length);
try JSfiddle:
https://jsfiddle.net/vickyfor2007/wcc0ab5g/2/

About querySelector() with multiple selectors

I had a situation in which I wanted to focus either an input tag, if it existed, or it's container if it didn't. So I thought of an intelligent way of doing it:
document.querySelector('.container input, .container').focus();
Funny, though, querySelector always returns the .container element.
I started to investigate and came out that, no matter the order in which the different selectors are put, querySelector always returns the same element.
For example:
var elem1 = document.querySelector('p, div, pre');
var elem2 = document.querySelector('pre, div, p');
elem1 === elem2; // true
elem1.tagName; // "P".
My question is: What are the "reasons" of this behavior and what "rules" (if any) make P elements have priority over DIV and PRE elements.
Note: In the situation mentioned above, I came out with a less-elegant but functional solution:
(document.querySelector('.container input') ||
document.querySelector('.container') ).focus();
document.querySelector returns only the first element matched, starting from the first element in the markup. As written on MDN:
Returns the first element within the document (using depth-first
pre-order traversal of the document's nodes|by first element in
document markup and iterating through sequential nodes by order of
amount of child nodes) that matches the specified group of selectors.
If you want all elements to match the query, use document.querySelectorAll (docs), i.e. document.querySelectorAll('pre, div, p'). This returns an array of the matched elements.
The official document says that,
Returns the first element within the document (using depth-first pre-order traversal of the document's nodes|by first element in document markup and iterating through sequential nodes by order of amount of child nodes) that matches the specified group of selectors.
So that means, in your first case .container is the parent element so that it would be matched first and returned. And in your second case, the paragraph should be the first element in the document while comparing with the other pre and div. So it was returned.
That's precisely the intended behavior of .querySelector() — it finds all the elements in the document that match your query, and then returns the first one.
That's not "the first one you listed", it's "the first one in the document".
This works, essentially, like a CSS selector. The selectors p, div, pre and pre, div, p are identical; they both match three different types of element. So the reason elem1.tagName == 'P' is simply that you have a <p> on the page before any <pre> or <div> tags.
You can try selecting all elements with document.querySelectorAll("p.a, p.b") as shown in the example below and using a loop to focus on all elements that are found.
<html>
<body>
<p class="a">element 1</p>
<p class="b">element 2</p>
<script>
var list=document.querySelectorAll("p.a, p.b");
for (let i = 0; i < list.length; i++) {
list[i].style.backgroundColor = "red";
}
</script>
</body>
</html>

Javascript: How to append childs to section element?

Assuming that I have the following HTML:
<body>
<section role="main">
</section>
</body>
1) Can I do this?
var section = document.getElementsByTagName("section");
2) Can I do this?
var section = document.querySelector("section[role=main]");
3) And finally, how can I append childs to this element? appendChild() doesn't work.
var p = document.createElement("p").innerText("A paragraph.");
section.appendChild(p);
You can use either 1 or 2,
Using getElementsByTagName -
check out the fiddle - http://jsfiddle.net/2jzho6hh/3/
this one returns an array of all elements with tag name section, so to access the first section element you have to use the 0 index on the array. For the second element use 1 index on the array and so on..
Using querySelector,
check the fiddle - http://jsfiddle.net/2jzho6hh/2/
querySelector returns first element matching the selector you have specified as in this case section[role=main] which means select the first sectionelement with attribute role and its value being main
There is also one other method querySelectorAll which is, you may think, a union of above two methods. It selects elements on the basis of CSS selector syntax just like querySelector does and it returns an array of all elements matching the selector just like the getElementsByTagName
Correct code is
var p = document.createElement("p");
p.innerHTML="A paragraph";
section.appendChild(p);
don't add innerHTML at the time of declaring p.If you do so p will not return child node.Declare p as a node and then add innerhtml to it.

Change HTML action="" using JavaScript

How can I change the string inside action="somthing" I've tried using
document.getElementsByClassName
but it doesn't seems to change anything.
My HTML
......
.........
<div class="my_button button" action='play_car'></div>
.....
......
My Javascript
document.getElementsByClassName('my_button').action = "play_boat";
.......
......
I've also tried
HTML
<div id="test" class="my_button button" action='play_car'></div>
Javascript
var a= document.getElementById('test');
console.log(a);
It just returns null
"get element-s by class name" returns a collection, not a single element.
Returns an array of all child elements which have any of the given class names. When called on the document object, the complete document is searched, including the root node.
Assuming that there is only a single element returned, then:
var elementsWithClass = document.getElementsByClassName('my_button')
elementsWithClass[0].action = "play_boat";
However, it may be more appropriate to use a loop - class names are generally designed to be used with multiple elements, and IDs (along with getElementById) for singular/unique elements.
Unfortunately, getElementsByClassName is not supported in even as "recent" a browser as IE8. To handle this, use a cross-browser library (jQuery or your preference) or a polyfill.
getElementsByClassName will return an array of all elements. Use document.getElementById if you want to address only one element. Also getElementsByClassName isn't supported by older browser. If that's an issue, you can use jQuery instead.
If you have only one element with this class name, you can get the first item:
document.getElementsByClassName('my_button')[0].action = "play_boat";
if you have many, iterate over them:
for (var i in document.getElementsByClassName('my_button')) {
document.getElementsByClassName('my_button')[i].action = "play_boat";
}
Please, check if the place of the javascript code is after the elements with the class "my_button".

get firstChild of div

I am trying to get the text of the span element. I have tried two methods using querySelectorAll and getElementByClassName but both give me the error "Uncaught TypeError: Object # has no method 'getElementsByTagName'" how do I go about getting the text of the span element?
javascript
var interstitial;
// 1st attempt
interstitial = document.querySelectorAll('div.top_bg').getElementsByTagName("span");
// 2nd attempt
interstitial = document.getElementByClassName('top_bg')[0].getElementsByTagName("span");
if (interstitial) {
console.log(interstitial[0].firstChild.innerHTML);
}
html
<div class="top_bg">
<span style="font-size:14px;line-height:30px">The text I am trying to get.</span>
</div>
Include the span in the query
interstitial = document.querySelectorAll('div.top_bg span');
document.querySelectorAll('div.top_bg') returns a collection so you'll have to select a node then apply getElementsByTagName
Also its getElementsByClassName not getElementByClassName notice the Elements
http://jsfiddle.net/dRvDt/1/
You are getting the error because the object returned by querySelectorAll is a NodeList, and NodeLists don't have a getElementsByTagName method. You should do something like:
var node, nodeList, spans
if (document.querySelectorAll) {
nodeList = document.querySelectorAll('div.top_bg');
node = nodeList[0];
if (node) {
spans = node.getElementsByTagName('span');
}
}
spans is now a NodeList of the spans contained in the first element of the first NodeList, if there was one. The first one will be at spans[0], and so on.
if you can use the jquery, you can look for the span html easy:
$("div.top_bg>span").firstChild().html();

Categories