About querySelector() with multiple selectors - javascript

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>

Related

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.

How can I manipulate the DOM without a library like jQuery? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am used to using jQuery for manipulating the DOM, for example:
var mything = $("#mything");
mything.on("click", function() {
mything.addClass("red");
mything.html("I have sinned.");
});
But now I want to do the same things with Vanilla JavaScript. Is this possible? How can I do it?
Notice: This question is intended to be a comprehensive resource on Vanilla JavaScript DOM manipulation.
When manipulating the Document Object Model with Vanilla JS, you will be directly be accessing the Document and Nodes. A document contains Elements, particularly HTMLElements and SVGElements which are both Nodes. An Element may contain Text too.
Finding Elements
You can get the first element which matches a CSS selector with mynode.querySelector(), and all elements that match the selector with myNode.querySelectorAll(). Most of the time myNode will be Document, so you can get anything in the document which matches the selector – however, you can look through only a node's descendants when myNode is a an element.
document.querySelectorAll('p:hover'); // Returns a NodeList of hovered paragraphs
This is similar to jQuery('p:hover').
There are also more specialized methods like:
myNode.getElementById()
myNode.getElementsByTagName()
myNode.getElementsByClassName()
myNode.getElementsByName()
Which have self-explanatory names. Notice that .getElementBy... returns a single element while .getElementsBy... (plural elements) returns a NodeList, which is essentially an array of nodes, but it doesn't have the standard array methods.
See also: What's the best way to loop through a set of elements in JavaScript?
Each element may also have a:
parentNode
previousSibling
previousElementSibling (excludes text nodes)
nextSibling
nextElementSibling (excludes text nodes)
firstChild
firstElementChild (excludes text nodes)
lastChild
lastElementChild (excludes text nodes)
childElementCount (same as children.length)
And NodeLists of:
childNodes
children (excludes text nodes)
In this way, we can traverse the DOM.
For example, to get the last child of the first paragraph element in the parent of #clickme here:
document.getElementById('clickme').addEventListener('click', function() {
console.log(this.parentNode.getElementsByTagName('p')[0].lastChild);
});
<div>
<blockquote>This is a really great quote.</blockquote>
<p>This is a <em>really</em> interesting paragraph. <span>this will be selected</span></p>
<p>In fact, here's another!</p>
<button id="clickme">Click me!</button>
</div>
...you find its parentNode, use getElementsByTagName on that to only get paragraph descendants, take the first one of those, and get its lastChild.
To get the text contained in it, you could get its text node (its first child) then use text.wholeText.
Creating & Deleting
You can create an element with document.createElement('aTagName') or clone another one with newElement = myElement.cloneNode(). Pass cloneNode true as it's first argument to also duplicate its descendants. Don't clone elements with an ID because it will cause 2 elements with the same ID to appear in the same document.
You can then append the new element (or an existing one) to a parent element using parent.appendChild(newElement) or append it after another element with parent.insertBefore(newElement, referenceElement). An insertAfter method doesn't exist, but it can be created:
HTMLElement.prototype.insertAfter = function(newEl, refEl) {
if (refEl.nextSibling) refEl.parentNode.insertBefore(newEl, refEl.nextSibling);
else refEl.parentNode.appendChild(newEl);
};
A node can be removed with parent.removeChild() or replaced with parent.replaceChild(newChild) or just removed inline with mynode.remove().
function poof() {
this.remove();
}
var elements = document.getElementsByClassName('poof');
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', poof);
}
<span class="poof">hi,</span>
<span class="poof">click</span>
<span class="poof">to</span>
<span class="poof">delete</span>
<span class="poof">me;</span>
<span class="poof">it</span>
<span class="poof">was</span>
<span class="poof">fun</span>
<span class="poof">being</span>
<span class="poof">a</span>
<span class="poof">span</span>
Classes and styles
By "and styles," I mean just classes. Styles are for CSS. You can apply CSS styles only to elements who have had a class added with JavaScript.1
Elements in HTML have a classList property which is a DOMTokenList representing a space-separated property, in this case class. You can .add(), .remove(), and .toggle() classes in the classList or check if it .contains() a class.
document.getElementById('clickme').addEventListener('click', function() {
document.getElementById('colors').classList.toggle('green');
});
.green { color: green }
<div id="colors">hello!</div>
<button id="clickme">Click me!</button>
Attributes
Elements with certain attributes can be selected with querySelector and querySelectorAll. Most attributes are properties of the element you're working with already. For example:
myDiv.hidden = true; // Hides element from view and from screenreaders
But if they're not, any attribute can be accessed with getAttributeNode, setAttributeNode, and removeAttributeNode. AttributeNodes have ownerElements and values.
"data-*" attributes can be accessed with myelement.dataset. For example, mydiv.dataset.pie = 'yummy' would add data-pie="yummy" to the div.
Events
Events are slightly more complicated. Binding one (like jQuery('selector').on) is pretty easy:
myElement.addEventListener('event-name', afunction);
(Other objects also have this method – for example, window)
Events can also be removed:
myelement.removeEventListener('event-name', afunction);
See: removeEventListener
An event list can be found here.
The function passed to the addEventListener will be passed an argument of the event occurring and have a this of the element the event listener is bound to.
However, events aren't this simple: something as trivial as clicking on a button may fire many event listeners on different elements and for different events.
– Browser Input Events: Can We Do Better Than The Click? by Smashing Magazine
See also: What is event bubbling and capturing?
1 If you really need to modify a style with JS, use myElement.style.styleProperty = 'value' to change the inline style attribute.

What is the equivalent of jQuery's $(".cell:first") in D3?

I've tried
d3.select(".cell:first")
d3.selectAll(".cell").filter(":first")
d3.selectAll(".cell").select(":first")
but neither work
d3.select(".cell") already selects the first matched element:
Selects the first element that matches the specified selector string, returning a single-element selection. If no elements in the current document match the specified selector, returns the empty selection. If multiple elements match the selector, only the first matching element (in document traversal order) will be selected.
Source: https://github.com/mbostock/d3/wiki/Selections#wiki-d3_select
"How would I get the last item?"
D3 appears to return the results of d3.selectAll() in a collection, positioned in an array. For instance, requesting all paragraphs on the d3 homepage results in:
[ Array[32] ] // An array with a single array child. Child has 32 paragraphs.
So if we wanted to get the last paragraph from that collection, we could do the following:
var paragraphs = d3.selectAll("p");
var lastParag = paragraphs[0].pop();
Or more concisely:
var obj = d3.select( d3.selectAll("p")[0].pop() );
"What about :last-child?"
The :last-child selector isn't the same as getting the last element on a page. This selector will give you the elements that are the last child of their parent container. Consider the following markup:
<div id="foo">
<p>Hello</p>
<p>World</p>
<div>English</div>
</div>
<div id="bar">
<p>Oie</p>
<p>Mundo</p>
<div>Portuguese</div>
</div>
In this example, running d3.select("p:last-child") won't return any of your paragraphs. Even d3.selectAll("p:last-child") won't. Neither of those containers have a last child that is a paragraph (they are <div> elements: <div>English</div> and <div>Portuguese</div>).
If you want to get the first DOM element from the D3's selection, use .node() method:
var sel = d3.selectAll('p'); // all <P>, wrapped with D3.selection
var el = sel.node(); // the first <P> element

Is Id necessary in applying jquery functions?

Suppose I have two p tags in the document. I want to call two different effects using jQuery when onMouseOver event happens. Is it necessary that these two p tags be given Ids. Can't it be achieved without giving Ids to these tags ?
You don't have to give anything an id, however it is the best way to uniquely identify an element.
You can instead idenfity by class:
$(".myClass")
By attribute:
$("[src='image.jpg']")
By position in parent:
$("p:eq(2)")
A full list of selectors is available in the documentation
$('p:first'); // first p tag
$('p:last'); // last p tag
$('p').eq(1); // exactly the second p tag
There are several ways to select an element / elements:
$('.classname')
$('#id')
$('tagname')
$('[attr="value"]')
etc
although jQuery allows you to write faster and easier scripts, but unfortunately it makes you never understand the real JavaScript.
$("*") //selects all elements.
$(":animated") //selects all elements that are currently animated.
$(":button") //selects all button elements and input elements with type="button".
$(":odd") //selects even elements.
$(":odd") //selects odd elements.$("p") selects all <p> elements.
$("p.intro") //selects all <p> elements with class="intro".
$("p#intro") //selects the first <p> elements with id="intro".
$(this) //Current HTML element
$("p#intro:first") //The first <p> element with id="intro"
$("p:eq(2)") // The third <p> element in the DOM
$(".intro") //All elements with class="intro"
$("#intro") //The first element with id="intro"
$("ul li:first") //The first <li> element of the first <ul>
$("ul li:first-child") //The first <li> element of every <ul>
$("[href]") //All elements with an href attribute
$("[href$='.jpg']") //All elements with an href attribute that ends with ".jpg"
$("[href='#']") //All elements with an href value equal to "#"
$("[href!='#']") //All elements with an href value NOT equal to "#"
$("div#intro .head") //All elements with class="head" inside a <div> element with id="intro"
jQuery – Select element cheat sheet

How do I use jQuery to get a set of every element with certain text, but NOT the parents?

I want to use jQuery to select every element that has a certain text string in it, but not the parents of that element. How do I do this? I have no control whatsoever over the HTML code, but here is an example:
<body>
<div>
<p>This is a paragraph and <a>this is not</a>.</p>
Here we have a div.
</div>
</body>
If I use the word "this" as my match word, I want jQuery to provide me with a set containing the <a> and the <p>, but not the <div> or the <body>.
Again, I have no control AT ALL over the HTML!
Thanks!
** Clarification: I do want the parent of the element if the parent ALSO has a "this" in its immediate text. Thus, I want the <a> AND the <p>.
Update::
Here is what I came up with: jsfiddle
var myArray = $('*:contains("this")','body').filter(function(){
if($(this).contents().filter(function(){
return(this.nodeType == 3);
}).text().indexOf('this')===-1){
return false;
}
return true;
});
$.each(myArray,function(){
console.log(this.nodeName);
});
Starts similar to the link posted by Robin, but it forces to only search in the context of body elements - this keeps your scripts safe if they are not inline.
The next part is a filter that checks to see if the current element direct text nodes contain the text.
This is a bit convoluted, but to walk through it:
.contents() - docs - gets the immediate nodes
.filter() - docs - we want to only test on test nodes, so we filter them out
this.nodeType - w3 spec - check to see if its a text node
.test() - docs - gets a string of the text nodes.
.indexOf() - check that string for our string
Note I did the :contains() at the top and in the second filter, the first isn't needed per say but I think the initial test should reduce the number of deeper tests and speed it up slightly.
Here's my solution with pure JS.
Code:
function findElmsWithWord(word, elm, found){
if (elm.nodeType === 3 && elm.data.indexOf(word) !== -1)
found.push(elm.parentNode);
else
for (var i = 0; i < elm.childNodes.length; i++)
findElmsWithWord(word, elm.childNodes[i], found);
}
var elms = [];
findElmsWithWord('this', document.body, elms);
console.log(elms);
It recursively walks the dom until it finds the text nodes that contain the word in question. And then adds the text node's parent as a result.

Categories