How to search the children of a HTMLDivElement? - javascript

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.

Related

Add a class to an element's first child with javascript

New to js and trying to add a class to every section's first child automatically. I'm thinking something like:
<script>
var element = document.getElementsByTagName('section p:first-child');
element.classList.add('firstp');
</script>
but it's not producing any effect in the document. Any help?
getElementsByTagName returns a collection, not an element, so you'd have to iterate over it
To use a string, enclose the string in string delimiters, like '
To use a selector string to select elements, use querySelectorAll. getElementsByTagName only accepts tag names (like p, or span - it's not flexible at all):
var allFirstPs = document.querySelectorAll('section p:first-child');
allFirstPs.forEach((p) => {
p.classList.add('firstp');
});
To support older browsers (since NodeList.prototype.forEach is a somewhat new method), use Array.prototype.forEach instead:
var allFirstPs = document.querySelectorAll('section p:first-child');
Array.prototype.forEach.call(
allFirstPs,
function(p) {
p.classList.add('firstp');
}
);
You could use querySelectorAll (to get the paragraphs) and forEach (to iterate over each paragraph) to accomplish this:
var elements = document.querySelectorAll('section p:first-child');
elements.forEach((e) => {
e.classList.add('firstp');
});
.firstp {
color: red;
}
<section>
<p>1st</p>
<p>2nd</p>
<p>3rd</p>
</section>
<section>
<p>1st</p>
<p>2nd</p>
<p>3rd</p>
</section>
<section>
<p>1st</p>
<p>2nd</p>
<p>3rd</p>
</section>
use querySelectorAll:
var elements = document.querySelectorAll('section p:first-child');
and iterate over result:
[...elements].forEach((element) => element.classList.add('firstp'));

JavaScript: querySelector - match also the top element

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');

Append multiple items in JavaScript

I have the following function and I am trying to figure out a better way to append multiple items using appendChild().
When the user clicks on Add, each item should look like this:
<li>
<input type="checkbox">
<label>Content typed by the user</label>
<input type="text">
<button class="edit">Edit</button>
<button class="delete">Delete</button>
</li>
and I have this function to add these elements:
function addNewItem(listElement, itemInput) {
var listItem = document.createElement("li");
var listItemCheckbox = document.createElement("input");
var listItemLabel = document.createElement("label");
var editableInput = document.createElement("input");
var editButton = document.createElement("button");
var deleteButton = document.createElement("button");
// define types
listItemCheckbox.type = "checkbox";
editableInput.type = "text";
// define content and class for buttons
editButton.innerText = "Edit";
editButton.className = "edit";
deleteButton.innerText = "Delete";
deleteButton.className = "delete";
listItemLabel.innerText = itemText.value;
// appendChild() - append these items to the li
listElement.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
if (itemText.value.length > 0) {
itemText.value = "";
inputFocus(itemText);
}
}
But you can notice that I am repeating three times the appendChild() for listItem. Is it possible to add multiple items to the appendChild() ?
You can do it with DocumentFragment.
var documentFragment = document.createDocumentFragment();
documentFragment.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
listElement.appendChild(documentFragment);
DocumentFragments allow developers to place child elements onto an
arbitrary node-like parent, allowing for node-like interactions
without a true root node. Doing so allows developers to produce
structure without doing so within the visible DOM
You can use the append method in JavaScript.
This is similar to jQuery's append method but it doesnot support IE and Edge.
You can change this code
listElement.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
to
listElement.append(listItem,listItemCheckbox,listItemLabel,editButton,deleteButton);
Personally, I don't see why you would do this.
But if you really need to replace all the appendChild() with one statement, you can assign the outerHTML of the created elements to the innerHTML of the li element.
You just need to replace the following:
listElement.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
With the following:
listItem.innerHTML+= listItemCheckbox.outerHTML + listItemLabel.outerHTML + editButton.outerHTML + deleteButton.outerHTML;
listElement.appendChild(listItem);
Explanation:
The outerHTML attribute of the element DOM interface gets the serialized HTML fragment describing the element including its descendants. So assigning the outerHTML of the created elements to the innerHTML of the li element is similar to appending them to it.
Merging the answers by #Atrahasis and #Slavik:
if (Node.prototype.appendChildren === undefined) {
Node.prototype.appendChildren = function() {
let children = [...arguments];
if (
children.length == 1 &&
Object.prototype.toString.call(children[0]) === "[object Array]"
) {
children = children[0];
}
const documentFragment = document.createDocumentFragment();
children.forEach(c => documentFragment.appendChild(c));
this.appendChild(documentFragment);
};
}
This accepts children as multiple arguments, or as a single array argument:
foo.appendChildren(bar1, bar2, bar3);
bar.appendChildren([bar1, bar2, bar3]);
Update – June 2020
Most all current browsers support append and the "spread operator" now.
The calls above can be re-written as:
foo.append(bar1, bar2, bar3);
bar.append(...[bar1, bar2, bar3]);
Let's try this:
let parentNode = document.createElement('div');
parentNode.append(...[
document.createElement('div'),
document.createElement('div'),
document.createElement('div'),
document.createElement('div'),
document.createElement('div')
]);
console.log(parentNode);
You need to append several children ? Just make it plural with appendChildren !
First things first :
HTMLLIElement.prototype.appendChildren = function () {
for ( var i = 0 ; i < arguments.length ; i++ )
this.appendChild( arguments[ i ] );
};
Then for any list element :
listElement.appendChildren( a, b, c, ... );
//check :
listElement.childNodes;//a, b, c, ...
Works with every element that has the appendChild method of course ! Like HTMLDivElement.
You can use createContextualFragment, it return a documentFragment created from a string.
It is perfect if you have to build and append more than one Nodes to an existing Element all together, because you can add it all without the cons of innerHTML
https://developer.mozilla.org/en-US/docs/Web/API/Range/createContextualFragment
// ...
var listItem = document.createElement("li");
var documentFragment = document.createRange().createContextualFragment(`
<input type="checkbox">
<label>Content typed by the user</label>
<input type="text">
<button class="edit">Edit</button>
<button class="delete">Delete</button>
`)
listItem.appendChild(documentFragment)
// ...
You could just group the elements into a single innerHTML group like this:
let node = document.createElement('li');
node.innerHTML = '<input type="checkbox"><label>Content typed by the user</label> <input type="text"><button class="edit">Edit</button><button class="delete">Delete</button>';
document.getElementById('orderedList').appendChild(node);
then appendChild() is only used once.
It's possible to write your own function if you use the built in arguments object
function appendMultipleNodes(){
var args = [].slice.call(arguments);
for (var x = 1; x < args.length; x++){
args[0].appendChild(args[x])
}
return args[0]
}
Then you would call the function as such:
appendMultipleNodes(parent, nodeOne, nodeTwo, nodeThree)
Why isn't anybody mentioning the element.append() function ?!
you can simply use it to append multiple items respectively as so:
listItem.append(listItemCheckbox, listItemLabel, editButton, deleteButton);
This is a quick fix
document.querySelector("#parentid .parenClass").insertAdjacentHTML('afterend', yourChildElement.outerHTML);
Guys I really recommend you to use this one.
[listItemCheckbox, listItemLabel, editButton, deleteButton]
.forEach((item) => listItem.appendChild(item));
Since you can't append multiple children at once. I think this one looks better.
Also here's a helper function that uses the fragment technique as introduced in the #Slavik's answer and merges it with DOMParser API:
function createHtmlFromString(stringHtml) {
const parser = new DOMParser();
const htmlFragment = document.createDocumentFragment();
const children = parser.parseFromString(stringHtml, "text/html").body
.children;
htmlFragment.replaceChildren(...children);
return htmlFragment;
}
Now to append multiple children with this, you can make the code much more readable and brief, e.g.:
const htmlFragment = createHtmlFromString(`<div class="info">
<span></span>
<h2></h2>
<p></p>
<button></button>
</div>
<div class="cover">
<img />
</div>
`);
Here's also a working example of these used in action: example link.
Note1: You could add text content in the above tags too and it works, but if it's data from user (or fetched from API), you'd better not trust it for better security. Instead, first make the fragment using the above function and then do something like this:
htmlFragment.querySelector(".info > span").textContent = game.name;
Note2: Don't use innerHTML to insert HTML, it is unsecure.
Great way to dynamically add elements to a webpage. This function takes 3 arguments, 1 is optional. The wrapper will wrap the parent element and it's elements inside another element. Useful when creating tables dynamically.
function append(parent, child, wrapper="") {
if (typeof child == 'object' && child.length > 1) {
child.forEach(c => {
parent.appendChild(c);
});
} else {
parent.appendChild(child);
}
if (typeof wrapper == 'object') {
wrapper.appendChild(parent);
}
}
I would like to add that if you want to add some variability to your html, you can also add variables like this:
let node = document.createElement('div');
node.classList.add("some-class");
node.innerHTML = `<div class="list">
<div class="title">${myObject.title}</div>
<div class="subtitle">${myObject.subtitle}
</div>`;

JavaScript/Jquery nodes from ID

Hello I want to get the nodes of class "ms-qSuggest-listItem" from the id.
<div class="ms-qSuggest-list" id="ctl00_ctl38_g_c60051d3_9564_459e_8a40_e91f8abf4dcf_csr_NavDropdownList" style="width: 508px;">
<div class="ms-qSuggest-listItem">Everything</div>
<div class="ms-qSuggest-listItem">Videos</div>
<div class="ms-qSuggest-hListItem">People</div>
<div class="ms-qSuggest-listItem">Conversations</div>
</div>
I tried
var nodes = $get("ctl00_ctl38_g_c60051d3_9564_459e_8a40_e91f8abf4dcf_csr_NavDropdownList").class(ms-qSuggest-listItem);
If I wanna get the nodes from its direct class - here its working but I want to get it from the ID.
var nodes = $("div.ms-qSuggest-listItem");
Please help.
Thanks.
All the below selectors will give you the matching nodes.
var nodes = $("#ctl00_ctl38_g_c60051d3_9564_459e_8a40_e91f8abf4dcf_csr_NavDropdownList").find('.ms-qSuggest-listItem');
OR
var nodes = $("#ctl00_ctl38_g_c60051d3_9564_459e_8a40_e91f8abf4dcf_csr_NavDropdownList .ms-qSuggest-listItem");
OR
var nodes = $(".ms-qSuggest-listItem", "#ctl00_ctl38_g_c60051d3_9564_459e_8a40_e91f8abf4dcf_csr_NavDropdownList");
Demo: https://jsfiddle.net/tusharj/fr2d9yv1/
You can use the id in conjunction with the class name within the same selector like this:
var long_id = 'ctl00_ctl38_g....';
var nodes = $('#' + long_id + ' .ms-qSuggest-listItem');
Basically what is happening here is the selector is matching the id and then looking inside it's children for the specified class name.
A more explicit way of writing this would be to use the .find() function on the parent node itself.
Description: Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
var long_id = 'ctl00_ctl38_g....';
var parent_node = $('#' + long_id);
var children_nodes = parent_node.find('.ms-qSuggest-listItem');
JSFiddle demo

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