Get next element with class (that's not a child or sibling) - javascript

With the press of a button, I want to toggle the class .active on the next div.bottom. These are basically accordions, but with a different structure.
Using nextElementSibling I guess won't work here to select the target element. How would one select such an element, that's neither a child nor a sibling (in plain JS)?
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>

I'd do it by using closest to go up to the container .wrapper element, then querySelector to find the bottom element:
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button && button.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
Or the same thing using optional chaining (relatively new):
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button?.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>

By using closest() you can traverse the DOM upwards. With this it's easy to just get the relevant .bottom and toggle the active class on this element.
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', (e) => {
e.currentTarget.closest('.wrapper').querySelector('.bottom').classList.toggle('active');
});
});
.bottom {
display: none
}
.bottom.active {
display: block
}
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle</button>
</div>
</div>
<div class="bottom">Hidden content</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle 2</button>
</div>
</div>
<div class="bottom">Hidden content 2</div>
</div>

Related

How to achieve level 3 div with javascript and apply styling

Hello I would like to reach a level 3 div and change the style of this div
in my example I would therefore like to be able to apply disply:none on style color red
to make the word Warning invisible
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
To select 3rd level div:
document.querySelector('#Zone > div > div > div')
Now the problem is you have 4 div at 3rd level. So needed to select all and check style color. That gives:
const warningNone = () => {
Array.from(document.querySelectorAll('#Zone > div > div > div')).forEach(el => {
if (el) {
if (el.style.color === 'red') {
el.style.display = 'none';
}
}
})
}
window.addEventListener('load', warningNone);
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
I modified the snippet to check the >div>div>div existence
By the way, I put the function to be fired when document loaded, otherwise your red will not apply
3...
try to split the query line in 2:
const warningNone = () => {
const els = document.querySelectorAll('#Zone > div > div > div');
els.forEach(el => {
if (el.style.color === 'red') {
el.style.display = 'none';
}
})
}
window.addEventListener('load', warningNone);
now in dev tools check which line fire the error

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>

How to select the content from the element that I clicked on (using data attributes)

I am working with HTML and JS and I am trying to add a click event listener so that the index = 0 value updates automatically on click.
Depending on which .tab element I click, the content inside of the div.tab should change to display either Hello1, Hello2 or Hello3.
The first tab should display the tab content hello1 accordingly.
I have provided both with data attributes in the HTML. I want the corresponding div.tab content to be output if both have the same data attribute with vanilla JS.
JS
const tabbarContent = document.querySelectorAll(".tab-content");
const tabbarButtons = document.querySelectorAll(".tab");
const index = 0;
tabbarContent[index].classList.add('active');
tabbarButtons[index].classList.add('active');
const dataAttribute1 = tabbarButtons[index].getAttribute("data-tab");
const dataAttribute2 = tabbarContent[index].getAttribute("data-tab");
function clickTab() {
for (let index = 0; index.length; index++) {
if (dataAttribute1[index] === dataAttribute2[index]) {
dataAttribute2.classList.add("active");
}
}
}
tabbarButtons[index].addEventListener("click", clickTab);
HTML
<div class="container">
<div class="inner-container">
<div class="tab tab1" data-tab="1">
1
</div>
<div class="tab tab2" data-tab="2">
2
</div>
<div class="tab tab3" data-tab="3">
3
</div>
</div>
<div class="inner-container">
<div class="tab-content content1" data-tab="1">
<h1>Hello1</h1>
</div>
<div class="tab-content content2" data-tab="2">
<h1>Hello2</h1>
</div>
<div class="tab-content content3" data-tab="3">
<h1>Hello3</h1>
</div>
</div>
</div>
Thanks
Jessy
If I understand correctly what you want to achieve, you could do something like:
const tabbarButtons = document.querySelectorAll(".tab");
const tabbarContents = document.querySelectorAll(".tab-content");
tabbarButtons.forEach((tabBtn) => {
tabBtn.addEventListener('click', () => {
tabbarContents.forEach(tabCnt => {
if (tabCnt.dataset.tab === tabBtn.dataset.tab) {
tabCnt.classList.add('active');
} else {
tabCnt.classList.remove('active');
}
})
});
})
.tab {
display: inline-block;
padding: 5px;
border: 1px solid #ccc;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
<div class="container">
<div class="inner-container">
<div class="tab tab1" data-tab="1">
1
</div>
<div class="tab tab2" data-tab="2">
2
</div>
<div class="tab tab3" data-tab="3">
3
</div>
</div>
<div class="inner-container">
<div class="tab-content content1" data-tab="1">
<h1>Hello1</h1>
</div>
<div class="tab-content content2" data-tab="2">
<h1>Hello2</h1>
</div>
<div class="tab-content content3" data-tab="3">
<h1>Hello3</h1>
</div>
</div>
</div>

How to get corresponding value to a button in JavaScript?

I don't want the value of button but instead I want the value of a span inside a div which corresponds to that button. I have a model in which buttons correspond to a div.
For example here you can see there are 3 buttons that correspond to each assignment.
Whenever a user clicks Assign button I need to return the value of the title span for example if someone clicks assign on first, it should print "English UOI 1" in the console.
The html code wraps up like this -
<div class="assignment-wrapper">
<div class="assignment-title"><span class="assignment-name">English UOI 1</span><span>15-06-2021</span></div>
<div class="assignment-functions">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
Now this is the example for structure but I would be doing this using javascript, I have retrieved data for database an I need to present each assignment in a table form.
If you have a better model you can suggest changes.
Any help is appreciated.
You can target up the element tree 2 parents and then back down two children and get the spans textContent.
const btn = document.querySelectorAll('.assignment-function-btn')
function getValue(e){
let target = e.target.parentNode.parentNode.children[0].children[0]
console.log(target.textContent)
}
btn.forEach(button => {
button.addEventListener('click', getValue)
})
.row {
display: flex;
justify-content: space-around;
}
.assignment-function-btn {
background: #ddd;
border: 1px solid black;
border-radius: 5px;
margin-left: 5px;
padding: 3px;
cursor: pointer;
}
<div class="assignment-wrapper row">
<div class="assignment-title">
<span class="assignment-name">English UOI 1</span>
<span>15-06-2021</span>
</div>
<div class="assignment-functions row">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
or...
If each of these classes per say, has its own line with the three buttons for each title/class, you could code a dataset attribute into the buttons parent element that represents the title/class that button is being used for. See my second snippit for that example...
Using dataset attribute:
In your HTML you add a data attribute like data-class-name="English UOI 1" to the direct parent element of your buttons. Then use the event target + parentNode to get the e.target.parentNode.dataset.className.
className is javascript camel-case for class-name.
<div class="assignment-title">
<span class="assignment-name">English UOI 1</span>
<span>15-06-2021</span>
</div>
<!--/ Here we add the data attribute data-class-name="English UOI 1" /-->
<div data-class-name="English UOI 1" class="assignment-functions row">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
const btn = document.querySelectorAll('.assignment-function-btn')
function getValue(e) {
let target = e.target.parentNode
console.log(target.dataset.className)
}
btn.forEach(button => {
button.addEventListener('click', getValue)
})
.row {
display: flex;
justify-content: space-around;
}
.assignment-title {
flex: auto;
}
.assignment-function-btn {
background: #ddd;
border: 1px solid black;
border-radius: 5px;
margin-left: 5px;
padding: 3px;
cursor: pointer;
}
<div class="assignment-wrapper">
<div class="row">
<div class="assignment-title">
<span class="assignment-name">English UOI 1</span>
<span>15-06-2021</span>
</div>
<div data-class-name="English UOI 1" class="assignment-functions row">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
<div class="row">
<div class="assignment-title">
<span class="assignment-name">Social Sciences</span>
<span>23-06-2021</span>
</div>
<div data-class-name="Social Sciences" class="assignment-functions row">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
<div class="row">
<div class="assignment-title">
<span class="assignment-name">Concepts of Algebra</span>
<span>26-06-2021</span>
</div>
<div data-class-name="Concepts of Algebra" class="assignment-functions row">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
</div>
You could use .closest to grab the .assignment-wrapper up the DOM tree and then, within the wrapper, find the .assignment-name element. Then grab its innerText.
Edit: Also, I strongly recommend using button elements for button behavior for accessibility purposes.
const btn = document.querySelector(".assign-task");
btn.addEventListener("click", (e) => {
const wrapper = e.target.closest(".assignment-wrapper");
const assignment = wrapper.querySelector(".assignment-name");
console.log(assignment.innerText);
})
<div class="assignment-wrapper">
<div class="assignment-title"><span class="assignment-name">English UOI 1</span><span>15-06-2021</span></div>
<div class="assignment-functions">
<button class="assign-task assignment-function-btn">Assign</button>
<button class="update-task assignment-function-btn">Update</button>
<button class="view-results assignment-function-btn">Results</button>
</div>
</div>
what i understand of you that you wants to get the text of .assignment-title span blongs to the parent of this button
Here's Example
var assignTask = document.getElementsByClassName('assign-task');
for(var i = 0; i < assignTask.length; i++) {
assignTask[i].addEventListener('click',function() {
var text = this.parentNode.parentNode.getElementsByClassName('assignment-name')[0].innerText;
console.log(text)
})
}
You can try the below
<div class="assignment-wrapper">
<div class="assignment-title">
<span class="assignment-name">English UOI 1</span
><span>15-06-2021</span>
</div>
<div class="assignment-functions">
<div class="assign-task assignment-function-btn">Assign</div>
<div class="update-task assignment-function-btn">Update</div>
<div class="view-results assignment-function-btn">Results</div>
</div>
</div>
<script>
function handleButtonClick(event) {
const buttonType = event.target.innerText;
const assignmentName = event.target
.closest(".assignment-wrapper")
.querySelector(".assignment-name").innerText;
console.log(`${assignmentName} -> ${buttonType}`);
}
document
.querySelectorAll(".assignment-function-btn")
.forEach((ele) => ele.addEventListener("click", handleButtonClick));
</script>

Edit css of "item" when clicking on corresponding "btn"

So I have this
<div class="btns">
<div class="btn1"></div>
<div class="btn2"></div>
<div class="btn3"></div>
<div class="btn4"></div>
</div>
<div class="prevs">
<div class="pre1"></div>
<div class="pre2"></div>
<div class="pre3"></div>
<div class="pre4"></div>
</div>
http://jsfiddle.net/uzpxjukv/
You have btn1, btn2, btn3 and btn4. I'm trying to make it so that when you press btn1, the div with the class pre1 should then get "display: block;" or something to make it visible. Then when btn2 is clicked, pre1 turns invisible again and pre2 turns visible.
Maybe something like this? If there will be more buttons, it should be more optimalized.
$('.btns').find('div').click(function(){
$('.prevs').find('div').eq($(this).index()).toggle();
});
$('.btns').find('div').click(function(){
$('.prevs').find('div').eq($(this).index()).toggle();
});
.prevs div:not(.pre1) {
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btns">
<div class="btn1">Button 1</div>
<div class="btn2">Button 2</div>
<div class="btn3">Button 3</div>
<div class="btn4">Button 4</div>
</div>
<div class="prevs">
<div class="pre1">Previews 1</div>
<div class="pre2">Previews 2</div>
<div class="pre3">Previews 3</div>
<div class="pre4">Previews 4</div>
</div>
JSFIDDLE DEMO -> http://jsfiddle.net/uzpxjukv/5/
$('.btns div').click(function() {
var classNumber = this.className.slice(-1);
$('.prevs div').hide();
$('.pre' + classNumber).show();
});
On click of the button div, first hide all the pre divs and then show only the relevant div.
Try it
$('.btns > div').on('click', function() {
var numberOfDiv = $(this).attr('class').slice('-1'),
prevs = $('.prevs');
prevs.find('> div').hide();
prevs.find('.pre' + numberOfDiv).show();
});
This example is with your html code, if is possible to change it, you can get a better code.
See the fiddle
I have changed your HTML a little bit..Changed the class attribute of the prevs divsti ids.
HTML
<div class="btns">
<div class="btn1" id="1" onClick="reply_click(this.id)"></div>
<div class="btn2" id="2" onClick="reply_click(this.id)"></div>
<div class="btn3" id="3" onClick="reply_click(this.id)"></div>
<div class="btn4" id="4" onClick="reply_click(this.id)"></div>
</div>
<div class="prevs">
<div id="pre1"></div>
<div id="pre2"></div>
<div id="pre3"></div>
<div id="pre4"></div>
</div>
JS
function reply_click(id) {
document.getElementById("pre" + id).style.display = "block";
}
Provided that you know what naming system the divs use, you could use something along these lines. (To see properly working, view using developer tool)
$('.btns div').on('click', function() {
var currClass = $(this).attr('class').slice(-1); //get end of number of div clicked
$('.prevs div').css('display', 'none'); //reset all divs to being hidden
$('.pre' + currClass).css('display', 'inline-block'); //show desired div
});
.btns div {
background-color: gray;
}
.btns div, .prevs div {
width: 2em;
height: 2em;
display: inline-block;
padding-right: 0.2em;
}
.prevs div {
background-color: red;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btns">
<div class="btn1"></div>
<div class="btn2"></div>
<div class="btn3"></div>
<div class="btn4"></div>
</div>
<div class="prevs">
<div class="pre1"></div>
<div class="pre2"></div>
<div class="pre3"></div>
<div class="pre4"></div>
</div>

Categories

Resources