Click event won't process - javascript

I am trying to do a question page where when you click on the plus at the end of a question the answer appears. I have the answer hidden and try adding class show-text with display of block instead of none and switching the plus button to a minus, however on click it does nothing! any help would be greatly appreciated!
const questions2 = document.querySelectorAll(".question")
questions2.forEach(function(info) {
const btn = info.querySelector(".question-btn")
btn.addEventListener("click", function() {
questions2.forEach(function(item) {
if (item !== info) {
item.classList.remove("show-text")
}
})
info.classList.toggle("show-text")
})
})
.question-text {
display: none
}
.show-text .question-text {
display: block
}
.minus-icon {
display: none
}
.show-text .minus-icon {
display: inline
}
.show-text .plus-icon {
display: none
}
<section class="questions">
<!-- Title -->
<div class="title">
<h2 class="heading">Questions</h2>
<div class="underline"></div>
</div>
<!-- Questions -->
<div class="section-center">
<article class="question">
<div class="question-title">
<p>Question here</p>
<button type="button" class="question-btn">
<span class="plus-icon"><i class="far fa-plus-square"></i></span>
<span class="minus-icon"><i class="far fa-minus-square"></i></span>
</button>
</div>
<div class="question-text">
<p>Answer here</p>
</div>
</article>
</div>
</section>

You could try letting your button keeps track of which answer it controls with the aria-controls attribute and use the aria-hidden attribute to toggle the display property of the answer.
const questions = document.querySelectorAll(".question");
questions.forEach(question => {
const questionBtn = question.querySelector("button");
const btnControls = questionBtn.getAttribute("aria-controls");
const answer = document.getElementById(btnControls);
questionBtn.addEventListener("click", () => {
if(answer.getAttribute("aria-hidden") == "true") {
answer.setAttribute("aria-hidden", "false")
} else {
answer.setAttribute("aria-hidden", "true")
}
})
})
div[aria-hidden="true"] {
display: none;
}
<section class="questions">
<!-- Title -->
<div class="title">
<h2 class="heading">Questions</h2>
<div class="underline"></div>
</div>
<!-- Questions -->
<div class="section-center">
<article class="question">
<div class="question-title">
<p>Question here</p>
<button type="button" class="question-btn" aria-controls="answer1">
<span class="plus-icon"><i class="far fa-plus-square"></i></span>
<span class="minus-icon"><i class="far fa-minus-square"></i></span>
</button>
</div>
<div id="answer1" class="question-text" aria-hidden="true">
<p>Answer here</p>
</div>
</article>
</div>
</section>

Related

Event delegation does not work if the bound target is nested

For a comment list I use the event delegation pattern after a recommendation from Stackoverflow colleagues (mplungjan, Michel). It works well and I am very enthusiastic about this pattern. But as I already suspected, there will be problems if the bound element (button) contains two child elements (span, span).
Since I want to get the CommentID from the target in the parent element of the child element, it only works in the cases when you click exactly between the spans inside the button. Actually a case for currentTarget but that doesn't work in this case because the tapped element is the whole comment list.
Question: What do I have to do to fix it?
const commentList = document.querySelector('.comment-list');
commentList.addEventListener('click', (ev) => {
console.log('1. clicked');
const getObjectId = () => {
return ev.target.parentNode.parentNode.getAttribute('data-comment-id');
}
if (! getObjectId()) return false;
if (ev.target.classList.contains('delete')) {
console.log('2. Delete action');
console.log('3. for relatedID', getObjectId());
}
if (ev.target.classList.contains('edit')) {
console.log('2. Edit action');
console.log('3. for relatedID', getObjectId());
}
if (ev.target.classList.contains('flag')) {
console.log('2. Flag action');
console.log('3. for relatedID', getObjectId());
}
});
.controller {
display: flex;
gap:20px;
}
.comment {
margin-bottom: 20px;
background: gray;
}
.controller button > span {
background: orange;
}
.controller button span:first-child {
margin-right: 10px;
}
<div class="comment-list">
<div class="comment">
<div class="content">lorem 1. Dont work! Nested button.</div>
<div class="controller" data-comment-id="1">
<div class="delete">
<button class="delete"><span>delete</span><span>ICON</span></button>
</div>
<div class="edit">
<button class="edit"><span>edit</span><span>ICON</span></button>
</div>
<div class="flag">
<button class="flag"><span>flag</span><span>ICON</span></button>
</div>
</div>
</div>
<div class="comment">
<div class="content">lorem 2. Work! </div>
<div class="controller" data-comment-id="2">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
<div class="comment">
<div class="content">lorem 3. Work! </div>
<div class="controller" data-comment-id="3">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
</div>
The problem is that you're using .parentNode.parentNode to get to the element with data-comment-id, but the number of parents changes when the target is nested inside additional <span> elements.
Don't hard-code the nesting levels, use .closest() to find the containing controller node.
const getObjectId = () => {
return ev.target.closest('.controller').getAttribute('data-comment-id');
}
Building on my last comment in the other question
const tgtButtonWhenSpansInsideButton = e.target.closest("button")
Cache the objects
the closest method will get the button itself even if no children
Make sure you get the class from the containing element of what you want to call a button
const commentList = document.querySelector('.comment-list');
const getObjectId = (tgt) => tgt.closest('.controller').dataset.commentId;
commentList.addEventListener('click', (ev) => {
const tgt = ev.target.closest("button")
const objectId = getObjectId(tgt);
if (!objectId) return;
console.log(objectId,"clicked")
if (tgt.classList.contains('delete')) {
console.log('2. Delete action');
console.log('3. for relatedID', objectId);
}
if (tgt.classList.contains('edit')) {
console.log('2. Edit action');
console.log('3. for relatedID', objectId);
}
if (tgt.classList.contains('flag')) {
console.log('2. Flag action');
console.log('3. for relatedID', objectId);
}
});
.controller {
display: flex;
gap: 20px;
}
.comment {
margin-bottom: 20px;
background: gray;
}
.controller button>span {
background: orange;
}
.controller button span:first-child {
margin-right: 10px;
}
<div class="comment-list">
<div class="comment">
<div class="content">lorem 1. Dont work! Nested button.</div>
<div class="controller" data-comment-id="1">
<div class="delete">
<button class="delete"><span>delete</span><span>ICON</span></button>
</div>
<div class="edit">
<button class="edit"><span>edit</span><span>ICON</span></button>
</div>
<div class="flag">
<button class="flag"><span>flag</span><span>ICON</span></button>
</div>
</div>
</div>
<div class="comment">
<div class="content">lorem 2. Work! </div>
<div class="controller" data-comment-id="2">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
<div class="comment">
<div class="content">lorem 3. Work! </div>
<div class="controller" data-comment-id="3">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
</div>
In this case I would "traverse" the DOM up if it wasn't a button that was clicked, something like this
const commentList = document.querySelector('.comment-list');
commentList.addEventListener('click', (ev) => {
console.log('1. clicked', ev.target.tagName);
let target = ev.target
if (target.tagName === "SPAN") {
target = target.parentElement
}
const commentId = target.parentElement.parentElement.getAttribute('data-comment-id');
if (!commentId) return false;
if (target.classList.contains('delete')) {
console.log('2. Delete action');
console.log('3. for relatedID', commentId);
}
if (target.classList.contains('edit')) {
console.log('2. Edit action');
console.log('3. for relatedID', commentId);
}
if (target.classList.contains('flag')) {
console.log('2. Flag action');
console.log('3. for relatedID', commentId);
}
});
.controller {
display: flex;
gap:20px;
}
.comment {
margin-bottom: 20px;
background: gray;
}
.controller button > span {
background: orange;
}
.controller button span:first-child {
margin-right: 10px;
}
<div class="comment-list">
<div class="comment">
<div class="content">lorem 1. Dont work! Nested button.</div>
<div class="controller" data-comment-id="1">
<div class="delete">
<button class="delete"><span>delete</span><span>ICON</span></button>
</div>
<div class="edit">
<button class="edit"><span>edit</span><span>ICON</span></button>
</div>
<div class="flag">
<button class="flag"><span>flag</span><span>ICON</span></button>
</div>
</div>
</div>
<div class="comment">
<div class="content">lorem 2. Work! </div>
<div class="controller" data-comment-id="2">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
<div class="comment">
<div class="content">lorem 3. Work! </div>
<div class="controller" data-comment-id="3">
<div class="delete"><button class="delete">delete</button></div>
<div class="edit"><button class="edit">edit</button></div>
<div class="flag"><button class="flag">flag</button></div>
</div>
</div>
</div>

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

Trying to loop through click event and make the div´s with it´s texts visible. Does somebody what the mistake is?

Here is the html container:
<div class="arrow-1">
<div class="text-event">
<p class="text-style-11">Text 1
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-2">
<div class="text-event">
<p class="text-style-11">Text 2
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-3">
<div class="text-event">
<p class="text-style-11">Text 3
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-4">
<div class="text-event">
<p class="text-style-11"> Text 4
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-5">
<div class="text-event">
<p class="text-style-11"> Text 5
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
</div>
The paragraphs should be "visible" when text-event class clicked. Text style class is "hidden" by default. I did that already with other div boxes and it worked. Is there a 'p' declaration missing in the loop function? There is not even a console feedback when I pass the textEvent variable to the console.
const textEvent = document.querySelectorAll('.text-event');
for (var j = 0; j < textEvent.length; j++) (function (j) {
textEvent[j].addEventListener('click', onclick);
textEvent[j].onclick = function (ctrls) {
ctrls.forEach(ctrl => {
/* ctrl.getElementsByClassName('p')[0].innerHTML; */
ctrl.document.getElementsByClassName('text-style-11').style.visibility = "visible";
})
}
})(j);
I could not understand very well your code but this is how I would do it.
First get all the element with class "text-event"
loop over that array and add an event listener to each of them.
When you click in one of them select the element with the class of text-style-11
To something to that element.
const textContainers = document.querySelectorAll(".text-event");
textContainers.forEach((element) => {
element.addEventListener("click", () => {
const textElement = element.querySelector(".text-style-11");
textElement.style.visibility = "hidden";
});
});
Instead of adding styles directly, I recommend you to create a class and use classList toggle to add and remove that class.
textContainers.forEach((element) => {
element.addEventListener("click", () => {
const textElement = element.querySelector(".text-style-11");
textElement.classList.toggle("show");
});
});
I have tested this code it should work fine:
const textEvent = document.querySelectorAll('.text-event');
for (var j = 0; j < textEvent.length; j++) {
textEvent[j].addEventListener('click', (el) => {
const clickedElement = el.currentTarget;
const innerParagraph = clickedElement.querySelector('.text-style-11');
innerParagraph.style.visibility = 'visible';
});
}
You've already got a valid answer.. by the way here's the live snippet using the proper strategy to add an event listener to all your .text-event elements that will hide the inner paragraph embedded in the clicked box:
document.querySelectorAll('.text-event').forEach((el) => {
el.addEventListener('click', (event) => {
const clickedElement = event.currentTarget;
const innerParagraph = clickedElement.querySelector('.text-style-11');
innerParagraph.style.visibility = 'visible';
});
});
.text-event {
border: dotted gray 3px;
margin-bottom: 2px;
cursor: pointer;
}
.text-style-11{
visibility: hidden;
}
<div class="arrow-1">
<div class="text-event">
<p class="text-style-11">Alle Personen, die nicht sozialversicherungspflichtig beschäftigt sind (Beamte, Selbstständige, etc.)
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-2">
<div class="text-event">
<p class="text-style-11">Einmalige Wartezeit 1 Monate
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-3">
<div class="text-event">
<p class="text-style-11">Keine Karenzzeit
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-4">
<div class="text-event">
<p class="text-style-11">Versichert sind nur Erstdiagnosen während der Versicherungslaufzeit (Herzinfarkt, Schlaganfall, Krebs, Blindheit oder Taubheit)
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>
<div class="arrow-5">
<div class="text-event">
<p class="text-style-11">Übernahme des noch ausstehenden Restsaldos von bis zu 135.000 €
</p>
</div>
<div class="arrow">
<div class="diamond">
</div>
</div>
</div>

How to bind this within js nested object iteration within a function. Jquery

again, probably a terrible title - but what I'm trying to do is to make a simple search feature on my website. You click a nav button, which updates the search bar, whi in turn triggers an onchange event to update the current appended list.
function update() {
var list = $("#comic__modern-list");
list.empty();
$.each(Object.keys(comics), function() {
var currentObject = comics[this];
var filter = comics[this].type;
var publisher = comics[this].publisher;
if (search == "") {
if(filter == "modern") {
list.append(`
<div class="comic__box">
<div class="comic__image-box">
<img src="${currentObject['data-item-image']}" alt="${currentObject['data-item-description']}" class="img-fluid">
<div class="comic__desc-wrap">
<div class="comic__desc">${currentObject['data-item-description']}, issue #${currentObject['issue']} (${currentObject['year']})</div>
</div>
</div>
<div style="text-align:center; margin-top: 1rem">
<button
class="btn btn-warning snipcart-add-item comic__button"
data-item-id="${currentObject['data-item-id']}"
data-item-price="${currentObject['data-item-price']}"
data-item-url="${currentObject['data-item-url']}"
data-item-description="${currentObject['data-item-description']}"
data-item-image="${currentObject['data-item-image']}"
data-item-name="${currentObject['data-item-name']}">
<div class="comic__desc-desk">£${currentObject['data-item-price']}<br>Add to cart</div><div class="comic__desc-mob">BUY <br> ${currentObject['data-item-description']}, Issue: ${currentObject['issue']} (${currentObject['year']})</div>
</button>
</div>
</div>
`)
}
} else if (search == publisher) {
list.append(`
<div class="comic__box">
<div class="comic__image-box">
<img src="${currentObject['data-item-image']}" alt="${currentObject['data-item-description']}" class="img-fluid">
<div class="comic__desc-wrap">
<div class="comic__desc">${currentObject['data-item-description']}, issue #${currentObject['issue']} (${currentObject['year']})</div>
</div>
</div>
<div style="text-align:center; margin-top: 1rem">
<button
class="btn btn-warning snipcart-add-item comic__button"
data-item-id="${currentObject['data-item-id']}"
data-item-price="${currentObject['data-item-price']}"
data-item-url="${currentObject['data-item-url']}"
data-item-description="${currentObject['data-item-description']}"
data-item-image="${currentObject['data-item-image']}"
data-item-name="${currentObject['data-item-name']}">
<div class="comic__desc-desk">£${currentObject['data-item-price']}<br>Add to cart</div><div class="comic__desc-mob">BUY <br> ${currentObject['data-item-description']}, Issue: ${currentObject['issue']} (${currentObject['year']})</div>
</button>
</div>
</div>
`)
}
});
}
The current list is generated by this, which works fine:
$.each(Object.keys(comics), function() {
var currentObject = comics[this];
var currentObject2 = comics[this].type;
console.log(currentObject2);
if (search == "") {
if(currentObject2 == "modern") {
var list = $("#comic__modern-list");
list.append(`
<div class="comic__box">
<div class="comic__image-box">
<img src="${currentObject['data-item-image']}" alt="${currentObject['data-item-description']}" class="img-fluid">
<div class="comic__desc-wrap">
<div class="comic__desc">${currentObject['data-item-description']}, issue #${currentObject['issue']} (${currentObject['year']})</div>
</div>
</div>
<div style="text-align:center; margin-top: 1rem">
<button
class="btn btn-warning snipcart-add-item comic__button"
data-item-id="${currentObject['data-item-id']}"
data-item-price="${currentObject['data-item-price']}"
data-item-url="${currentObject['data-item-url']}"
data-item-description="${currentObject['data-item-description']}"
data-item-image="${currentObject['data-item-image']}"
data-item-name="${currentObject['data-item-name']}">
<div class="comic__desc-desk">£${currentObject['data-item-price']}<br>Add to cart</div><div class="comic__desc-mob">BUY <br> ${currentObject['data-item-description']}, Issue: ${currentObject['issue']} (${currentObject['year']})</div>
</button>
</div>
</div>
`)
}
}
});
From what I can gather, this has to do with the keyword "this" no longer meaning what it did when it was outside of the function, so I'm assuming the fix will be to do with bind(), but I can't make heads nor tails of it.
p.s, if there's an easier/simpler way to set up a search system, please enlighten me!

How to create collapse expand list in react

I try to create a collapse and expand side menu in React (v 16.5) with the following criteria -
On page load first item (Circulars) will be in expanded view. Any of one item can expand at a time, like, if user clicks on the second item (Specifications), the first item will collapse. I also want some CSS animation during collapse/expand transaction , like smoothly down/up the body section of each item and change the arrow icons. My approach is to add/remove a CSS class on each item (sidebar-nav-menu-item) dynamically, like -
sidebar-nav-menu-item item-active
So, when a item was in expanded view it should be like above class and remove item-active when its in collapse view. By default, the body divs (sidebar-nav-menu-item-body) should be hidden through CSS when the item in a collapse mode.
import React, { Component } from 'react';
className SidebarNavs extends React.Component{
constructor(props) {
super(props);
}
render() {
return(
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<div className="sidebar-nav-menu-item" data-id="circulars">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Circulars</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className="sidebar-nav-menu-item" data-id="specifications">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Specifications</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className="sidebar-nav-menu-item" data-id="wo">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Work Orders</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
</div>
</div>
)
}
}
export default SidebarNavs;
CSS:
.sidebar-nav-menu-item{
display:block;
}
.sidebar-nav-menu-item-body{
display:none;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body{
display:block;
}
You should use a state variable to show your collapsiable item active / in-active.
I modified your code a bit to fit it into your requirements.
class App extends Component {
constructor() {
super();
this.state = {
activeCollapse: 'circulars'
};
}
handleExpandCollaps = (name) => {
if (this.state.activeCollapse === name) {
this.setState({ activeCollapse: '' })
} else {
this.setState({ activeCollapse: name })
}
}
moreInfoClick = (e) => {
e.stopPropagation();
console.log("clicked");
}
render() {
return (
<div>
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "circulars" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("circulars")} data-id="circulars" >
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Circulars</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "specifications" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("specifications")} data-id="specifications">
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Specifications</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "wo" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("wo")} data-id="wo">
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Work Orders</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
</div>
</div>
</div>
);
}
}
Note: I have used CSS for font-awesome icons. Hope you have added font-awesome
Demo
To do that I would use React.useState, since its a small state to control and to animate I would use CSS:
The component would look like this:
function SidebarNavs() {
const [activeItem, setActiveItem] = React.useState(1);
return (
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<SidebarItem
title="Circulars"
setActiveItem={setActiveItem}
index={1}
activeItem={activeItem}
>
Sidebar Content Here
</SidebarItem>
<SidebarItem
title="Specifications"
setActiveItem={setActiveItem}
index={2}
activeItem={activeItem}
>
Sidebar Content Here
</SidebarItem>
<SidebarItem
title="Specifications"
setActiveItem={setActiveItem}
index={3}
activeItem={activeItem}
>
Work Orders
</SidebarItem>
</div>
</div>
);
}
function SidebarItem({ title, children, setActiveItem, activeItem, index }) {
const expanded = activeItem === index;
const cls = "sidebar-nav-menu-item " + (expanded ? "item-active" : "");
return (
<div className={cls}>
<div className="sidebar-nav-menu-item-head">
<div className="sidebar-nav-menu-item-head-title">{title}</div>
<div className="sidebar-nav-menu-item-head-help">
<button
type="button"
className="btn-help"
onClick={() => setActiveItem(index)}
>
View more info
</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true" />
</div>
</div>
<div className="sidebar-nav-menu-item-body">{children}</div>
</div>
);
}
The CSS would look like this:
.sidebar-nav-menu-item {
border: 1px solid #CCC;
margin-bottom: 20px;
}
.sidebar-nav-menu-item .sidebar-nav-menu-item-body {
overflow: hidden;
max-height: 0;
transition: all linear 0.5s;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body {
max-height: 100px;
transition: all linear 0.5s 0.3s;
}

Categories

Resources