Clear contents of outer div and add different number of inner divs - javascript

I want to change the number of inner divs from 2 to 3 but the javascript code isn't letting me. I want to get all the inner divs from the entire page and then add every three to the next inner div:
var innerElems = document.getElementsByClassName("inner");
var outerElems = document.getElementsByClassName("outer");
// Remove contents of outer divs
for (let i = 0; i < outerElems.length; i++) {
outerElems[i].innerHTML = "";
}
let x = 0;
for (let j = 0; j < innerElems.length; j++) {
outerElems[j].innerHTML = innerElems[x];
x++;
outerElems[j].innerHTML = innerElems[x];
x++;
outerElems[j].innerHTML = innerElems[x];
x++
}
<div class="outer" id="outer1ID">
<div class="inner">
<div class="anotherDiv">
Contents A
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents B
</div>
</div>
</div>
<div class="outer" id="outer2ID">
<div class="inner">
<div class="anotherDiv">
Contents C
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents D
</div>
</div>
</div>
<div class="outer" id="outer3ID">
<div class="inner">
<div class="anotherDiv">
Contents E
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents F
</div>
</div>
</div>
I want the final result to look like:
<div class="outer" id="outer1ID">
<div class="inner">
<div class="anotherDiv">
Contents A
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents B
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents C
</div>
</div>
</div>
<div class="outer" id="outer2ID">
<div class="inner">
<div class="anotherDiv">
Contents D
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents E
</div>
</div>
<div class="inner">
<div class="anotherDiv">
Contents F
</div>
</div>
</div>
When running the script, the HTML just shows "object HTMLelement" when adding to the outer divs

Considering that your inner divs will have the same content, you can use this approach to avoid unecessary changes in the DOM:
const $outer = document.getElementsByClassName('outer');
const $inner = document.querySelector('.inner');
function addInnerElement() {
for (const $el of $outer) {
$el.insertAdjacentElement('beforeend', $inner.cloneNode(true));
}
}
addInnerElement();

After removing the content of your outer element, you can create a loop that will iterate 3 times (you want 3 inner divs). In this loop, you can create a new div element with document.createElement('div').
You can now add the class inner with classList.add('inner').
After that, you can append the new inner div to the outer div with .appendChild(element);.
In the example below, I have added a little bit of CSS to differentiate the outer div(Purple) from the inner div(Cyan).
Here is an example of what you can do:
var outerElems = document.getElementsByClassName("outer");
// Remove contents of outer divs
for(let j = 0; j < outerElems.length; j++)
{
outerElems[j].innerHTML = "";
for(let i = 1; i < 4; i++)
{
var innerElems = document.createElement('div');
innerElems.style.background = "cyan";
innerElems.style.margin = "5px";
innerElems.innerText = "Inner Div " + i;
innerElems.classList.add('inner');
outerElems[j].appendChild(innerElems);;
}
}
.outer{
background-color: purple;
height: 100px;
margin: 5px;
padding: 5px;
}
.inner{
background-color: cyan;
height: 20px;
margin: 5px;
}
<div class="outer" id="outer1ID">
<div class="inner">
</div>
<div class="inner">
</div>
</div>
<div class="outer" id="outer2ID">
<div class="inner">
</div>
<div class="inner">
</div>
</div>
<div class="outer" id="outer3ID">
<div class="inner">
</div>
<div class="inner">
</div>
</div>

run code snippet to see that my solution works
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
.inner {
background-color: red;
margin: 0.5rem;
}
.outer {
background-color: blue
}
</style>
<body>
<div class="outer" id="outer1ID">
outer
<div class="inner">
inner
</div>
<div class="inner">
inner
</div>
</div>
<div class="outer" id="outer2ID">
outer
<div class="inner">
inner
</div>
<div class="inner">
inner
</div>
</div>
<div class="outer" id="outer3ID">
outer
<div class="inner">
inner
</div>
<div class="inner">
inner
</div>
</div>
<script>
var innerElems = document.getElementsByClassName("inner")
var outerElems = document.getElementsByClassName("outer")
var lastElement = outerElems[2]
for (let [i, el] of [...outerElems].entries()) { // loop over all the remaining outer elements
i !== 2 && el.append(lastElement.children[i].cloneNode(true)) // add inner elements from last outer element
}
// remove last one
lastElement.remove()
</script>
</body>
</html>

Related

Change the Page layout when a button is clicked

I want to change the layout of a page that has 3 columns:
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
... to 4 columns when a button is clicked:
<div>
<div class="container">
<div class="row" >
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
</div>
</div>
</div>
I have no clue on how to do this.
There are many ways you can add another div. Here is my approach :
function appendDiv(){
let row = document.getElementsByClassName('row');
// change className for all the col-md-4 div
document.querySelectorAll('.col-md-4').forEach(function(item) {
item.className = 'col-md-3';
})
//create new div;
let col = document.createElement('div');
// add classname to div
col.className = "col-md-3"
row[0].appendChild(col)
}
.col-md-4{
border : 1px solid blue;
height : 20px;
}
.col-md-3{
border : 1px solid green;
height : 20px;
}
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
<button onClick='appendDiv()'>click</button>
</div>
</div>
There's a few ways this could be done depending on your data, however, here's one angle.
If you have both your 4 column & 3 column versions of the data loaded on the page (but one hidden with css). You could run something like this.
HTML
<div id="colsThree" class="displayArea show">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<div id="colsFour" class="displayArea">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<button id="changeColumns">Click Me To Change Columns</button>
Javascript
const buttonEl = document.querySelector("#changeColumns");
buttonEl.addEventListener('click', () => {
const outputEls = document.querySelectorAll('.displayArea')
outputEls.forEach((outputEl) => {
outputEl.toggle("show")
})
});
CSS
.displayArea {
display: none;
}
.displayArea.show {
display: block;
}
Use forEach and appendChild method.
const btn = document.querySelector('#btn')
btn.onclick = function() {
const targetClasses = document.querySelectorAll('.col-md-4')
targetClasses.forEach((tag, idx) => {
tag.className = 'col-md-3'
const lastIdx = targetClasses.length - 1
if (idx === lastIdx) {
const tag = document.createElement('div')
, row = document.querySelector('.row')
tag.className = 'col-md-3'
tag.innerText = '4'
row.appendChild(tag)
}
})
console.log(targetClasses)
return
}
<div>
<button id="btn">Click me</button>
<div class="container">
<div class="row" >
<div class="col-md-4">1</div>
<div class="col-md-4">2</div>
<div class="col-md-4">3</div>
</div>
</div>
</div>
If you're only using vanilla HTML, CSS, and JavaScript, then one of the ways to achieve this is by adding a click listener to the button beforehand. FYI: for brevity's sake, I'll call the div element with row class as parent. When user clicks the button, then it should
remove col-md-4 class and add col-md-3 class to all the children elements of parent.
add a new div element with col-md-3 class into parent.
Here's a link to the codepen for your reference.
const button = document.querySelector('button');
const rowDiv = document.querySelector('.row');
button.addEventListener('click', (e) => {
Array.from(rowDiv.children).forEach(childDiv => {
childDiv.classList.remove('col-md-4');
childDiv.classList.add('col-md-3');
});
const newDiv = document.createElement('div');
newDiv.classList.add('col-md-3');
rowDiv.appendChild(newDiv);
// I disabled the button to prevent the user
// from clicking it the second time.
e.target.disabled = true;
});
.button-parent {
margin: 15px 0;
}
.row {
height: 100vh;
}
.row > div:nth-child(1) {
background: red;
}
.row > div:nth-child(2) {
background: blue;
}
.row > div:nth-child(3) {
background: yellow;
}
.row > div:nth-child(4) {
background: green;
}
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<div>
<div class="container">
<div class="button-parent">
<button class="btn btn-primary">Add div</button>
</div>
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
</body>

JavaScript, HTML search engine - how to get parent of an element?

My problem is:
Search script is working, but it only hides h3 elements from the code.
<h3 class="post-subtitle" style="display: flex;">Protokoły tunelowania VPN</h3>
<h3 class="post-subtitle" style="display: flex;">Certyfikat cyfrowy</h3>
I need the code to hide the whole div with "post" ID instead of just h3 element.
How do i do that?
HTML Code for Search Bar:
<div id="kontener" class="container">
<div style="text-align:center" id="search-bar">
<input type="text" id="searchbar" onkeyup="searchBar()" class="shadow-lg">
</div>
</div>
HTML Code on Website
<!-- First element -->
<div id="post">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="post-preview">
<a href="URL">
<h2 class="post-title"><i class="far fa-sticky-note fa-xs" aria-hidden="true"></i> ASO</h2>
<h3 class="post-subtitle" style="display: flex;">Protokoły tunelowania VPN</h3>
</a>
<p class="post-meta">11 Maj, 2021</p>
</div>
</div>
</div>
<hr>
</div>
<!-- End of First element -->
<!-- Second element -->
<div id="post">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="post-preview">
<a href="URL">
<h2 class="post-title"><i class="far fa-sticky-note fa-xs" aria-hidden="true"></i> ELSK</h2>
<h3 class="post-subtitle" style="display: flex;">Certyfikat cyfrowy</h3>
</a>
<p class="post-meta">26 Kwiecień, 2021</p>
</div>
</div>
</div>
<hr>
</div>
<!-- End of Second element -->
JavaScript code:
<script>
function searchBar() {
let input = document.getElementById('searchbar').value
input=input.toLowerCase();
let x = document.getElementsByClassName('post-subtitle');
for (i = 0; i < x.length; i++) {
if (!x[i].innerHTML.toLowerCase().includes(input)) {
x[i].style.display="none";
}
else {
x[i].style.display="flex";
}
}
}
</script>
You just need to target couple of parent nodes, either by .parentElement / .parentNode or use .closest function.
Example:
<script>
function searchBar() {
let input = document.getElementById('searchbar').value
input=input.toLowerCase();
let x = document.getElementsByClassName('post-subtitle');
for (i = 0; i < x.length; i++) {
if (!x[i].innerHTML.toLowerCase().includes(input)) {
x[i].closest('#post').style.display="none";
// Or this below (note each parentElement targets parent tag)
// x[i].parentElement.parentElement.parentElement.parentElement.parentElement.style.display="none";
}
else {
x[i].closest('#post').style.display="flex";
}
}
}
</script>

Wrapping elements while looping through HTMLCollection causes problem

I want to wrap each item of the container in a div. When I loop through HTMLCollection, some elements are accessed multiple times while others are left out
HTML
<div class="container">
<div class="item_1"></div>
<div class="item_2"></div>
<div class="item_3"></div>
<div class="item_4"></div>
<div class="item_5"></div>
<div class="item_6"></div>
<div class="item_7"></div>
<div class="item_8"></div>
<div class="item_9"></div>
</div>
JS
const container = document.querySelector('.container');
const items = container.children;
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
Looping directly through HTMLCollection gives this bizarre result
<div class="container">
<div class="item_2"></div>
<div class="item_4"></div>
<div class="item_6"></div>
<div class="item_8"></div>
<div class="wrapper">
<div class="item_1"></div>
</div>
<div class="wrapper">
<div class="item_5"></div>
</div>
<div class="wrapper">
<div class="item_9"></div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="item_7"></div>
</div>
</div>
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="wrapper">
<div class="item_3"></div>
</div>
</div>
</div>
</div>
</div>
problem gets solved when I convert HTMLCollection to an Array
const items = Array.from(container.children);
I can't understand what causes such behavior
You were iterating the container.children list which you were also changing during the iterations. This messed up the iteration. You can solve this, as you mentioned yourself, by converting the container.children to an array because then you are not iterating over the live container.children list but over an array copy of that. This copy is still referring to the correct child elements so they are moved correctly with the appendChild() function.
As an alternative you can use the querySelecterAll() to retrieve all the elements you want to wrap.
const container = document.querySelector('.container');
const items = container.querySelectorAll('.container > *');
for(let i = 0; i < items.length; i++) {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.appendChild(items[i]);
container.appendChild(wrapper);
}
.wrapper {
background-color: red;
}
<div class="container">
<div class="item_1">1</div>
<div class="item_2">2</div>
<div class="item_3">3</div>
<div class="item_4">4</div>
<div class="item_5">5</div>
<div class="item_6">6</div>
<div class="item_7">7</div>
<div class="item_8">8</div>
<div class="item_9">9</div>
</div>

Vanilla Javascript insertBefore() in for loop

I have the following html structure.
<div id="page1" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
<div id="page2" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
<div id="page3" class="page">
<div class="firstchild"></div>
<div class="secondchild"></div>
<div class="thirdchild"></div>
<div class="forthchild"></div>
</div>
And my javascript structure is.
var pageCLASS = frame.querySelectorAll(".page");
//var pageCLASS = frame.getElementsByClassName('page')[0];
var leng = pageCLASS.length;
for (var i = 0; i < leng; ++i) {
var pageID = frame.getElementById('page' + (i + 1));
var firstchild = frame.getElementsByClassName('firstchild')[0];
var secondchild = frame.getElementsByClassName('secondchild')[0];
var thirdchild = frame.getElementsByClassName('thirdchild')[0];
pageID.insertBefore(thirdchild, firstchild.nextSibling);
//pageCLASS.insertBefore(thirdchild, firstchild.nextSibling);
}
Now I have problems with the thirdchild being moved to below the firstchild and above the secondchild in all of page1, page2, and page3 together. The code above only moves it in page1, but for the other 2 which does nothing. The frame shown in the source is an iframe stored on the same domain with access to it's elements. Can I please get some advice on what I am doing wrong as I want to move all thirdchilds in each div to below the first child in each of their parent div?
The problem you are having is that you are constantly targeting the same elements with e.g.
var firstchild = frame.getElementsByClassName('firstchild')[0];
because this instruction always returns the first occurrence of such an element in the iframe and never the second or third.
In order to be sure that you are targeting the correct elements you can rewrite some of your code to only search for the elements that are contained within a certain parent and not inside the whole iframe.
You can then use something like this instead
var firstChild = pageID.querySelector('.firstchild');
which will only search for an element (the first occurrence) with class firstchild that is contained within some other element (in this case the element saved in pageID).
Check below (I exchanged the form for document so we can test here):
var pageCLASS = document.querySelectorAll(".page");
var leng = pageCLASS.length;
for (var i = 1; i <= leng; i++) {
var pageID = document.getElementById('page' + i);
var firstChild = pageID.querySelector('.firstchild');
var thirdChild = pageID.querySelector('.thirdchild');
firstChild.parentNode.insertBefore(thirdChild, firstChild.nextSibling);
}
.page {
border: 1px solid #09f;
}
<div id="page1" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>
<div id="page2" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>
<div id="page3" class="page">
<div class="firstchild">first child</div>
<div class="secondchild">second child</div>
<div class="thirdchild">third child</div>
<div class="forthchild">fourth child</div>
</div>

Trying to use javaScript to make the second image shows instead of the first

I'm trying to show the next image by just adding the style: display(none) but it is not working at all
<script>
function displayBanner() {
var count = 1;
if (count == 1) {
document.getElementById("banner_img3").style.display = "none";
}
}
displayBanner();
</script>
<header>
<div class="banner">
<div id="banner">
<div class="banner_img" id="banner_img1"></div>
<div class="banner_img" id="banner_img2"></div>
<div class="banner_img" id="banner_img3"></div>
</div>
</div>
</header>
Your element does not exist when you execute the script before the element. Move the script to after the elements or move displayBanner into a load event
I assume you want to rotate the images
var count = 0, max;
function displayBanner() {
document.getElementById("banner_img"+count).style.display = "none";
count++
if (count === max ) count = 0
document.getElementById("banner_img"+count).style.display = "block";
}
window.addEventListener("load", function() {
max = document.querySelectorAll(".banner_img").length;
document.getElementById("banner_img0").style.display = "block";
setInterval(displayBanner, 3000);
})
.banner_img { display:none; text-align:center }
<header>
<div class="banner">
<div id="banner">
<div class="banner_img" id="banner_img0"><img src="http://lorempixel.com/output/animals-q-c-640-480-1.jpg" /><br/>Image 1</div>
<div class="banner_img" id="banner_img1"><img src="http://lorempixel.com/output/animals-q-c-640-480-2.jpg" /><br/>Image 2</div>
<div class="banner_img" id="banner_img2"><img src="http://lorempixel.com/output/animals-q-c-640-480-3.jpg" /><br/>Image 3</div>
</div>
</div>
</header>
your div didn't have any source add a source using <img src=""></img> for the div try below code for that
<header>
<div class="banner">
<div id="banner">
<div class="banner_img" id="banner_img1"><img src="http://templatesforcv.com/wp-content/uploads/06.jpg" ></div>
<div class="banner_img" id="banner_img2"><img src="http://templatesforcv.com/wp-content/uploads/06.jpg"></div>
<div class="banner_img" id="banner_img3"><img src="http://templatesforcv.com/wp-content/uploads/06.jpg"></div>
</div>
</div>
</header>
window.onload=displayBanner();
function displayBanner() {
var count = 1;
if (count == 1) {
document.getElementById("banner_img3").style.display = "none";
}
}
working demo:https://jsfiddle.net/athulmathew/yh2esp1c/21/

Categories

Resources