JavaScript: querySelector - match also the top element - javascript

Is it possible for querySelector to somehow match also the top element? So for example:
Element:
<div class="container">
<div>Something</div>
</div>
JavaScript:
const container = element.querySelector('.container');
This won't match anything, because "container" is the top element. Is there an elegant way of querying element, that would test not only its children, but also the top element? I'm talking pure JavaScript.

You can test whether the selector refers to the top-level, and use a conditional expression:
const container = element.matches(".container") ? element : element.querySelector(".container");
For querySelectorAll you can do:
const containers = [...(element.matches(".container") ? [element] : []), ...element.querySelectorAll(".container")];
This returns an array instead of a NodeList, but for most purposes that difference shouldn't matter.

Not really elegant, but this would work.
If we resort to element.parentNode we would not guarantee targeting only the element.
//dummy up
const element = document.querySelector('.container');
const container = element.querySelector('.container');
const container2 = element.querySelector('.container')||(element.classList.contains('container')) ? element : null;
console.log(container);
console.log(container2);
<div class="container">
<div>Something</div>
</div>
const container = element.querySelector('.container');

Related

nextSibling returns null while sibling exists

In this part of code, why nextSibling returns null ?
const formIt = () => {
const titles = document.querySelectorAll('h1');
document.getElementById('content').innerHTML = '';
titles.forEach(title => {
console.log(title.nextSibling);
let p = title.nextSibling; //Returns null
let pWrapper = document.createElement('div');
pWrapper.appendChild(p);
document.getElementById('content').appendChild(pWrapper);
});
};
formIt();
<div id='content'>
<h1>...</h1>
<p>...</p>
<h1>...</h1>
<p>...</p>
<h1>...</h1>
<p>...</p>
</div>
On line 3 you set the innerHTML of content to an empty string.
That removes all the h1 and p elements from the DOM.
They aren’t siblings after that.
——
Fiddle with innerHTML after you have finished the loop.
Simply because, by the time the forEach() runs, you've removed all those objects from the DOM:
document.getElementById('content').innerHTML = '';
...so they no longer have any siblings.
There are two properties to iterate on Nodes or elements:
nextSibling
nextElementSibling
See the note at documentation of nextSibling:
Note: Browsers insert Text nodes into a document to represent whitespace in the source markup. Therefore a node obtained, for example, using Node.firstChild or Node.previousSibling may refer to a whitespace text node rather than the actual element the author intended to get.
[..]
You can use Element.nextElementSibling to obtain the next element skipping any whitespace nodes, other between-element text, or comments.
(emphasis mine)
See similar question:
javascript nextsibling function
Example
const headings = document.querySelectorAll('h1');
console.log("headings (count):", headings.length);
let firstHeading = headings[0];
console.log("first h1 nextSibling (data):", firstHeading.nextSibling.data);
console.log("first h1 nextElementSibling (data):", firstHeading.nextElementSibling.data);
let secondHeading = headings[1];
console.log("second h1 nextSibling (data):", secondHeading.nextSibling.data);
console.log("second h1 nextElementSibling (data):", secondHeading.nextElementSibling.data);
<div id='content'>
<h1>heading_1</h1>text_1
<p>paragraph_1</p>
<h1>heading_2</h1>
<p>paragraph_2</p>
</div>

How to re-Order html child elements in Dom based on id value

I have a parent div with some child elements. I want to re-order child elements based on two id values. for example 1,4. It means to grab the item with id 1 and insert it above the item with id 4.
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Making a drag and drop component for react. And this is what i have tried
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
But problem is when i grab the first element and want to put it on the bottom (last child). It fails to do so. How to put a child element after last child with insertBefore() method?
Don't know how you are using insertBefore() but there should not be any issues:
Update: The issue could be that your code is running before the DOM is fully loaded. You can wrap your code with DOMContentLoaded:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Placing the first element as the last element using nextSibling:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const parentNode = document.querySelector('.parent');
const element = document.getElementById('1') //dragStart
const targetElement = document.querySelector('.parent').lastElementChild //get last child
parentNode.insertBefore(element, targetElement.nextSibling);
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Note: This answers the original question. The question has now been edited to reference React. You wouldn't use the following in a React project. You'd reorder the state that the DOM represents, and then let React handle updating the DOM.
You're right to use insertBefore:
function moveElement(move, before) {
// Get the element to move
const elToMove = document.getElementById(move);
// Get the element to put it in front of
const elBefore = document.getElementById(before);
// Move it
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
function moveElement(move, before) {
const elToMove = document.getElementById(move);
const elBefore = document.getElementById(before);
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
setTimeout(() => {
moveElement("1", "4");
}, 800);
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Side note: I suggest avoiding having id values that start with digits. Although they're perfectly valid HTML and they work just fine with getElementById, they're a pain if you need to target them with CSS, because a CSS ID selector (#example) can't start with an unescaped digit. For instance, document.querySelector("#1") fails. You have to escape the 1 with a hex sequence, which isn't terrifically clear: document.querySelector("#\\31") (the characters \, 3, and 1: 0x31 = 49 = the Unicode code point for 1).

How do I access a specific element in the inner HTML?

I am trying to use JS or jQuery to access an html value called "data-button". I can access the whole HTML div and pull out class name as well as the text content from the button, but I cant get the data-button value.
In the code below I have a captureRecipeButtons() function that can get the "recipe-1-container" div.
function captureRecipeButtons(){
let theWholeDiv = document.getElementsByClassName("recipe-1-container")[0];
let buttonValue = ?;
}
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
In my captureRecipeButtons() function I want buttonValue to equal "1" in my above code. Any help would be appreciated thanks.
You can use the full power of CSS selectors with querySelector:
function captureRecipeButtons(){
let button = document.querySelector(".recipe-1-container [data-button]");
}
querySelector returns the first matching element (or null if none do).
If you wanted the value of data-selector on that element, then getAttribute or dataset:
function captureRecipeButtons(){
let buttonValue = document.querySelector(".recipe-1-container [data-button]").getAttribute("data-button");
// or
let buttonValue = document.querySelector(".recipe-1-container [data-button]").dataset.button;
}
Live Copy:
function captureRecipeButtons(){
const button = document.querySelector(".recipe-1-container [data-button]");
console.log(button.getAttribute("data-button"));
// or
console.log(button.dataset.button);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
But note that dataset does some transformations.
But there are lots of different ways to do this. More to explore in the DOM.
So you could just get the button element by class or tag name as I have done. then data-button is legit just an attribute so just use getAttribute('data-button');
The way I've written below will just get the first button that is a direct child of theWholeDiv element.
function captureRecipeButtons(){
let theWholeDiv = document.getElementsByClassName("recipe-1-container")[0];
let buttonValue = theWholeDiv.getElementsByTagName('button')[0].getAttribute('data-button');
console.log(buttonValue);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
According to Mozilla Docs,
You can access the Data attributes via the dataset object.
function captureRecipeButtons(){
let theButton = document.querySelector("recipe-1-container > button");
let buttonValue = theButton.dataset.button;
}
Another way using the DOM, including some short-cuts to avoid excessive horizontal scrolling. The code precisely targets the first DIV element and its first BUTTON element, using the getAttribute() method to return the value of the indicated property. What is nice about JavaScript is the fabulous amount of chaining that one can do between parent and child elements.
function captureRecipeButtons() {
let d = document;
d.g = d.getElementsByTagName;
let buttonValue = d.g("div")[0].getElementsByTagName("button")[0].getAttribute("data-button");
console.log(buttonValue);
};
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
Alternatively, you could write code as follows:
function captureRecipeButtons() {
let d = document;
d.g = d.getElementsByTagName;
let button = d.g("div")[0].childNodes[1];
button.g = button.getAttribute;
let buttonValue = button.g("data-button");
console.log(buttonValue);
}
captureRecipeButtons();
<div class="recipe-1-container">
<button class="listed-recipe-link" data-button="1">Element</button>
</div>
The DIV element's first child node as per the format of the code is not the BUTTON element but a text object. The childNode[1] holds the BUTTON element, so you can use its getAttribute() method to retrieve the value of the data-button attribute.

How to search the children of a HTMLDivElement?

I have an HTMLDivElement, and my goal is to find a div nested beneath this.
Ideally I'd want something like getElementById, but that function doesn't work for HTMLDivElement.
Do I need to manually traverse the graph, or is there an easier way?
Demo: http://jsfiddle.net/ThinkingStiff/y9K9Y/
If the <div> you're searching for has a class, you can use getElementsByClassName():
document.getElementById( 'parentDiv' ).getElementsByClassName( 'childDiv' )[0];
If it doesn't have a class you can use getElementsByTagName():
document.getElementById( 'parentDiv' ).getElementsByTagName( 'div' )[0];
And if it has an id you can, of course, just use getElementById() to find it no matter where it is in the DOM:
document.getElementById( 'childDiv' );
//For immediate children
var children = document.getElementById('id').childNodes;
//or for all descendants
var children = document.getElementById('id').getElementsByTagName('*');
var div = ...
var divChildren = div.getElementsByTagName("div");
var divYouWant = [].filter.call(divChildren, function (el) {
return matchesSomeCondition(el);
});
Ideally, I'd want something like getElementById
And you can use getElementById just do document.getElementById(id) and since ids are unique that will find that single div item you wanted.
You can also use elem.getElementsByClassName to select a descendant of elem by class
You can use .querySelector(). The functions getElementById() and getElementByClassName() work perfectly fine on document, but they do not work on the child records returned by these functions. If you need many children elements, then consider .querySelectorAll()
Full Working Demo:
const topdiv = document.getElementById('top-div');
const seconddiv = topdiv.querySelector('#second-div');
seconddiv.innerHTML = '456';
<div id="top-div">
123
<div id="second-div">
abc
</div>
</div>
Demonstration of getElementById() Failing:
const topdiv = document.getElementById('top-div');
const seconddiv = topdiv.getElementById('second-div');
seconddiv.innerHTML = '456';
<div id="top-div">
123
<div id="second-div">
abc
</div>
</div>
Concise and readable:
document.getElementById('parentDiv').children[0];
Using .childNodes returns lots of extra children, whereas .children returns a smaller array.

How to get all CSS classes of an element?

I have an element with multiple classes and I'd like to get its css classes in an array. How would I do this? Something like this:
var classList = $(this).allTheClasses();
No need to use jQuery for it:
var classList = this.className.split(' ')
If you for some reason want to do it from a jQuery object, those two solutions work, too:
var classList = $(this)[0].className.split(' ')
var classList = $(this).prop('className').split(' ')
Of course you could switch to overkill development mode and write a jQuery plugin for it:
$.fn.allTheClasses = function() {
return this[0].className.split(' ');
}
Then $(this).allTheClasses() would give you an array containing the class names.
Note that you can also use myElement.classList as a simple array-like object:
const classList = myElement.classList;
This is supported by all major browsers for a while now, apart IE 9 and below.
This should do the work for you:
var classes = $('div').attr('class').split(" ");
This would be the jQuery solution for other solutions there are other answers !
Check this out:
var classes = $('selector').prop('classList');
element.classList.value
console.log("class")
console.log(document.getElementById('c2').classList.value)
<div id="c2" class="class1 class2"> i am two class</div>
getAttribute
console.log("class")
console.log(document.getElementById('c2').getAttribute('class'))
<div id="c2" class="class1 class2"> i am two class</div>
className
console.log("class")
console.log(document.getElementById('c2').className)
<div id="c2" class="class1 class2"> i am two class</div>
to make an array choose any one of above method
string.split(' ');
function showClasses() {
const div = document.querySelector('div');
const classes = div.className.split(' ');
const p = document.querySelector('p');
p.innerHTML = classes;
}
<div class="foo bar">This div has foo, bar classes</div>
<p class='output'>Above div classes appear here</p>
<button onClick="showClasses();">Show div classes</button>
HTML
<div class="foo bar">This div has foo, bar classes</div>
Vanilla JavaScript. It will return an array of classes.
const div = document.querySelector('div');
const classes = div.className.split(" "); // ['foo', 'bar']

Categories