javascript - Get every div that doesn't contain a child specific element - javascript

I have a div group inside some that I want to have a class for every div that doesn't have a specific element like h1
For Example :
<div class="container">
<div class="parents">
<h1>AAAA</h1>
<div class="content">
<p>Hello</p>
</div>
</div>
</div>
I want to check each div inside some of it and get all the class that does not contain h1 and the result is like
class = 'content'
I'm trying my javascript code via parseFromString because the html code is taken out from string
var My_HTML = `
<div class="container">
<div class="parents">
<h1>AAAA</h1>
<div class="content">
<p>Hello</p>
</div>
</div>
</div>
`;
var parser = new DOMParser();
var doc = parser.parseFromString(My_HTML, "text/html");
var output = doc.querySelectorAll("div:not('div > h1')");

Step 1: Get all div elements.
Step 2: Filter the collection, keeping those that do not have an h1 descendant.
Step 3: For each matching element, map the element to its className property.
Step 4: There is no step 4.
[...document.querySelectorAll('div')].filter(d => !d.querySelector('h1')).map(d => d.className)
const My_HTML = `
<div class="container">
<div class="parents">
<h1>AAAA</h1>
<div class="content">
<p>Hello</p>
</div>
</div>
</div>
`;
const parser = new DOMParser();
const doc = parser.parseFromString(My_HTML, "text/html");
const everyDiv= [...doc.querySelectorAll('div')].filter(d => !d.querySelector('h1')).map(d => d.className);
console.log(everyDiv)

Consider the following.
$(function() {
$(".container *").each(function(i, el) {
console.log("Check Elements", $(el).prop("nodeName"), $(el).attr("class"));
if (!$(el).is("h1")) {
console.log("Class: " + $(el).attr("class"));
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="parents">
<h1>AAAA</h1>
<div class="content">
<p>Hello</p>
</div>
</div>
</div>
This itereates each of the elements within .container; only looking for a Class if it is not an <h1> element.
You could also use the :not() selector.
$(function() {
$(".container *:not(h1)").each(function(i, el) {
console.log("Check Elements", $(el).prop("nodeName"), $(el).attr("class"));
});
});
This will select the child elements that are not H1.

Related

How to insert html elements

I'm trying to insert the whole element into the container element however It throws some object into the DOM
JS:
const CONTAINER = document.getElementById('container');
let title = document.querySelector('h1').cloneNode(true);
CONTAINER.insertAdjacentHTML('beforeend', title);
HTML:
<div class="container" id="container"></div>
<h1>Test</h1>
Or, as a one-liner:
document.getElementById('container').insertAdjacentHTML('beforeend', document.querySelector('h1').outerHTML);
<div class="container" id="container">This is the target container</div>
<p>Some padding text</p>
<h1>Test</h1>
title it is an HTMLElementObject. Use appendChild instead.
const CONTAINER = document.getElementById('container');
let title = document.querySelector('h1').cloneNode(true);
CONTAINER.appendChild(title);
<div class="container" id="container"></div>
<h1>Test</h1>

Why Insert before first child not working

If the element has children insert a new div "i-new-element". parent2 and parent3 get a new div.
child3-parent3 has children but doesn't get a new giant. Why?
How can I make it possible for children who have children to get a new div?
it should look like:
<div id="child3-parent3">
<div id="i-new-element"></div>
<div id="child3"></div>
<div id="child4"></div>
</div>
var container = document.getElementById("container").querySelectorAll("#container > *");
container.forEach(function(div) {
{
if (div.hasChildNodes()) {
let parentElement = div;
let theFirstChild = parentElement.firstChild;
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt"
parentElement.insertBefore(newElement, theFirstChild)
}
}
});
<div id="container">
<div id="parent1"></div>
<div id="parent2">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3-parent3">
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
<div id="parent3">
<div id="child5"></div>
<div id="child6"></div>
</div>
</div>
Using
var container = document.getElementById("container");
var elements = container.querySelectorAll(":scope *");
instead of
var container = document.getElementById("container").querySelectorAll("#container > *");
should fix your problem.
var container = document.getElementById("container");
var elements = container.querySelectorAll(":scope *");
elements.forEach(function(div){
{
if (div.hasChildNodes()) {
let parentElement = div;
let theFirstChild = parentElement.firstChild;
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt"
parentElement.insertBefore(newElement, theFirstChild)
}
}
});
<div id="container">
<div id="parent1"></div>
<div id="parent2">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3-parent3">
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
<div id="parent3">
<div id="child5"></div>
<div id="child6"></div>
</div>
</div>
Edit: Further information about the :scope CSS pseudo-class can be found here and here.
I believe your problem is the following, even it is not described well in your sample HTML:
<div id="parent1"></div> per instance is actually not an empty node but contains some text i.e. <div id="parent1">some text here</div>.
You might believe or not, but node.hasChildNodes() will count any text as a child node (text node, nodeType = 3), so it will always return true is any text is present.
To avoid that, you can filter the text nodes first or just use this workaround:
Replace this line:
if (div.hasChildNodes()) {
with that line:
if (div.children.length) {
children property is not counting text nodes.
That's all you have to do, I believe.
var container = document.querySelectorAll("#container > *");
container.forEach(function(div) {
{
if (div.children.length) {
let parentElement = div;
let theFirstChild = parentElement.children[0];
let newElement = document.createElement("div")
newElement.id = "i-new-eleemnt";
newElement.innerHTML = 'i-new-eleemnt'
parentElement.insertBefore(newElement, theFirstChild);
}
}
});
<div id="container">
<div id="parent1">parent1</div>
<div id="parent2">parent2
<div id="child1">child1</div>
<div id="child2">child2</div>
<div id="child3-parent3">child3-parent3
<div id="child3">child3</div>
<div id="child4">child4</div>
</div>
</div>
<div id="parent3">parent3
<div id="child5">child5</div>
<div id="child6">child6</div>
</div>
</div>

How to wrap a group of sibling elements within a containing div using Vanilla JS?

I have the following HTML:
<div class="container">
<h1 class="heading">heading</h1>
<p class="paragraph">test</p>
<h2 class="subheading">123</h2>
<a class="link">321</a>
</div>
How can I wrap the last three elements within .container, so that the output looks like this:
<div class="container">
<h1 class="heading">heading</h1>
<div class="subcontainer">
<p class="paragraph">test</p>
<h2 class="subheading">123</h2>
<a class="link">321</a>
</div>
</div>
It would be better to do it within the HTML, but in case due to some constraints that you can't modify the HTML, you can do the followings
const container = document.querySelector('.container');
const subcontainer = document.createElement('div');
const lastThree = Array.from(container.children).slice(-3);
subcontainer.classList.add('subcontainer');
// move the elements into the subcontainer
lastThree.forEach(node => {
subcontainer.appendChild(node);
});
container.appendChild(subcontainer);
<div class="container">
<h1 class="heading">heading</h1>
<p class="paragraph">test</p>
<h2 class="subheading">123</h2>
<a class="link">321</a>
</div>
The class name container is quite generic, make sure that you only select the container element you want, or else you might modify other elements unintentionally.
old_html = document.getElementsByClassName("container")[0];
firstChild = old_html.children[0];
old_html.removeChild(firstChild);
new_html = "<div class='container'>"+ firstChild.outerHTML + "<div class = 'subcontainer'>" + old_html.innerHTML + "</div></div>";
console.log(new_html);
<div class="container">
<h1 class="heading">heading</h1>
<p class="paragraph">test</p>
<h2 class="subheading">123</h2>
<a class="link">321</a>
</div>

How to retrieve the div first child element sibling node using querySelector?

I have the DOM structure like below
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div><!-- want to access this div content -->
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
From the above HTML I want to access the div content of second div with classname table_cell inside first table_row div.
So basically I want to retrieve the content of div with classname table_cell with content chocolate products.
I have tried to do it like below
const element = document.querySelector('.rdt_TableBody');
const element1 = element.querySelectorAll('.rdt_TableRow')[0]
const element2 = element1.querySelectorAll('.rdt_TableCell')[0].innerHTML;
When I log element2 value it gives some strange output and not the text "chocolate products"
Could someone help me how to fix this. Thanks.
You can use:
the :nth-of-type pseudo-selector
combined with the immediate-child selector (>)
Example:
const selectedDiv = document.querySelector('.table_body > div:nth-of-type(1) > div:nth-of-type(2)');
Working Example:
const selectedDiv = document.querySelector('.table_body > div:nth-of-type(1) > div:nth-of-type(2)');
selectedDiv.style.color = 'white';
selectedDiv.style.backgroundColor = 'red';
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div> //want to access this div content
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
In your code
element1.querySelectorAll('.table_cell')[0], this is targeting the first element i.e., <div class="table_cell">first</div>. That's the reason why you are not getting the expected output.
I have made it to element1.querySelectorAll('.table_cell')[1], so that it'll target <div class="table_cell">chocolate products</div>.
const element = document.querySelector('.table_body');
const element1 = element.querySelectorAll('.table_row')[0]
const element2 = element1.querySelectorAll('.table_cell')[1].innerHTML;
console.log(element2);
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div>
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
Since the element that you want to target is the last div with having class table_cell, you can use :last-of-type on table_cell class using document.querySelector. But otherwise you can also use :nth-of-type if there are more than 2 elements and you want to target any element in between first and last.
Below is the example using :last-of-type.
const elem = document.querySelector(".table_row > .table_cell:last-of-type");
console.log(elem?.innerHTML);
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div> //want to access this div content
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
For more info you can refer :nth-of-type, :last-of-type and child combinator(>).

jQuery created element with insertAfter is not working

There are several divs on the page with class "wrap-me".
I want add an element "add-me" after "wrap-me", and doubly wrap the two in a "wrapper-inner" and "wrapper".
Basically - it should look like this:
<div class="wrapper">
<div class="wrapper-inner">
<div class="wrap-me"></div>
<div class="add-me"></div>
</div>
</div>
This is my implementation:
const $addMe = $("<div>", { class: "add-me" });
const $wrapper = $('<div>', { class: "wrapper" });
const $wrapperInner = $('<div>', { class: "wrapper-inner" });
$.each($(".wrap-me"), (i, wrapMe) => {
const $wrapMe = $(wrapMe);
$wrapMe.wrap($wrapper).wrap($wrapperInner);
$addMe.insertAfter($wrapMe);
});
Here's the output:
<div class="wrapper">
<div class="wrapper-inner">
<div class="wrap-me"></div>
</div>
</div>
It's almost there, the only thing is that the "add-me" is not getting inserted into the DOM. What's wrong?
//get the template
var wrapperTemplate = $('#wrapperTemplate').text();
$('.wrap-me').each(function(index, element){
//create a new element to put in the DOM
var $wrapper = $(wrapperTemplate);
//put the wrapper after the element we are going to replace
$wrapper.insertAfter(element);
//move the element into the wrapper where it should be
$wrapper.find('.wrapper-inner').prepend(element);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrap-me">A</div>
<div class="wrap-me">B</div>
<div class="wrap-me">C</div>
<div class="wrap-me">D</div>
<!-- HTML template for what we want to add, so it's not in the javascript -->
<script type="text/html" id="wrapperTemplate">
<div class="wrapper">
<div class="wrapper-inner">
<div class="add-me"></div>
</div>
</div>
</script>

Categories

Resources