I'm creating multi-level-Accordion-menu component on React Redux.
class MultiLevelAccordianMenu extends PureComponent {
constructor(props){
super(props);
this.toggleList = this.toggleList.bind(this);
}
toggleList(event){
console.log(event)
}
render(){
return( <ul className="cd-accordion-menu animated">
<li className="has-children" onClick={this.toggleList}>
<input type="checkbox" name ="group-1" id="group-1" defaultChecked />
<label htmlFor="group-1">Group 1</label>
<ul>
<li className="has-children">
<input type="checkbox" name ="sub-group-1" id="sub-group-1" />
<label htmlFor="sub-group-1">Sub Group 1</label>
<ul>
<li>Image</li>
<li>Image</li>
<li>Image</li>
</ul>
</li>
</ul>
</li>
</ul> );
}
}
export default MultiLevelAccordianMenu;
while clicking on parent "li" element, i want to toggle the child "ul" element.
I have written a toggle function. In this how can i access and set the child property to make hide and show?
You can use state and change it on click. Just make className of your child element dynamic:
class MultiLevelAccordianMenu extends PureComponent {
constructor(props){
super(props);
this.state = {
isActive : false
};
this.toggleList = this.toggleList.bind(this);
}
toggleList(event){
this.setState({isActive: !this.state.isActive});
}
render(){
....
<li className="has-children" onClick={this.toggleList}>
.....
<ul className={this.state.isActive ? "active" : ""}>
....
}
and add to your css something like:
ul{
visibility: hidden;
}
.active{
visibility: visible;
}
If you want to add animation you can use css rule transition:
ul{
max-height: 0;
transition: max-height 0.15s ease-out;
overflow: hidden;
}
ul.active {
max-height: 500px;
transition: max-height 0.25s ease-in;
}
Related
This question already has answers here:
Transitions on the CSS display property
(37 answers)
Closed 1 year ago.
I have a program, where I use JavaScript to make one div appear and another disappear when I click something:
<div id="first" class="active"> some content here </div>
<div id="second" class="inactive"> some content here </div>
<button onclick="change()"> button </button>
function change(){
document.getElementById("first").className="inactive";
document.getElementById("second").className="active"
}
.active{ display: block; }
.inactive{display: none; }
I'd like to make it so that one div fades in while the other fades out.
I've tried transition: ease 1s;, transition: display 1s and using a custom transition, however none of them have worked.
Try clicking the different buttons on this Carrd - the words fade in and out. I'm going for that effect here.
I'd prefer a solution using only HTML, CSS and/or JavaScript -- trying to keep this project simple.
Use opacity:
function change() {
document.getElementById("first").className = "inactive";
document.getElementById("second").className = "active"
}
.active{
opacity:1;
}
.inactive{
opacity:0;
}
div{
transition:opacity 1s;
}
<div id="first" class="active"> some content here </div>
<div id="second" class="inactive"> some content here </div>
<button onclick="change()"> button </button>
To prevent the hidden element from taking up space, use:
function change() {
const f1 = document.getElementById("first");
f1.style.opacity = "0";
setTimeout(() => {
f1.style.display = "none"
const f2 = document.getElementById("second");
f2.style.display = "block";
setTimeout(() => {
f2.style.opacity = "1";
}, 50);
}, 1000);
}
.active {
display: block;
opacity: 1;
}
.inactive {
display: none;
opacity: 0;
}
div {
transition: opacity 1s;
}
<div id="first" class="active"> some content here </div>
<div id="second" class="inactive"> some content here </div>
<button onclick="change()"> button </button>
I am working on a piece of code to toggle the visibility of some UL items by clicking corresponding buttons by toggling a class on the UL that changes opacity/height to 0 (so that I can also apply transitions). The second element doesn't work when the first is toggled to be invisible. The onclick event does not register.
The code works when the button and h3 are not styled to appear on the same line, and breaks when I try to use flex, float, or inline to position the two elements side by side. Is there a method that I can use to position them as such and still retain full functionality?
const buttons = document.getElementsByClassName("toggle");
const lists = document.getElementsByClassName("list");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
toggle(i);
})
};
function toggle(i) {
if (lists[i].classList.contains("hide")) {
lists[i].classList.remove("hide");
} else {
lists[i].classList.add("hide");
}
}
<div id="sidebar">
<div class="side">
<div class="header">
<h3>Protein</h3>
<button class="toggle"></button>
</div>
<div>
<ul class="list">
<li class="fi">Beef</li>
<li class="fi">Fish</li>
<li class="fi">Lamb</li>
<li class="fi">Pork</li>
<li class="fi">Poultry</li>
<li class="fi">Shellfish</li>
<li class="fi">Vegetarian</li>
</ul>
</div>
</div>
<div class="side">
<div class="header">
<h3>Cuisine</h3>
<button class="toggle"></button>
</div>
<ul class="list">
<li class="fi">African</li>
<li class="fi">American</li>
<li class="fi">Asian</li>
<li class="fi">British</li>
<li class="fi">Cajun Creole</li>
<li class="fi">Carribean</li>
<li class="fi">Eastern European</li>
<li class="fi">Show More</li>
</ul>
</div>
</div>
<style>
.header{
display: flex;
align-items: center;
}
.hide {
height: 0;
opacity: 0;
margin: 0;
}
</style>
gif of the issue
As the response for user120242 says you are overlaying the below side class div you can resolve it by adding your <div class="side"> element a overflow: hidden; style to avoid overflowing the below div, so try this:
If you want to place elements next to each other you need working with flexbox as #user120242 said.
const buttons = document.getElementsByClassName("toggle");
const lists = document.getElementsByClassName("list");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
toggle(i);
})
};
function toggle(i) {
if (lists[i].classList.contains("hide")) {
lists[i].classList.remove("hide");
} else {
lists[i].classList.add("hide");
}
}
.side {
overflow: hidden;
}
<div id="sidebar">
<div class="side">
<div class="header">
<h3>Protein</h3>
<button class="toggle"></button>
</div>
<div>
<ul class="list">
<li class="fi">Beef</li>
<li class="fi">Fish</li>
<li class="fi">Lamb</li>
<li class="fi">Pork</li>
<li class="fi">Poultry</li>
<li class="fi">Shellfish</li>
<li class="fi">Vegetarian</li>
</ul>
</div>
</div>
<div class="side">
<div class="header">
<h3>Cuisine</h3>
<button class="toggle"></button>
</div>
<ul class="list">
<li class="fi">African</li>
<li class="fi">American</li>
<li class="fi">Asian</li>
<li class="fi">British</li>
<li class="fi">Cajun Creole</li>
<li class="fi">Carribean</li>
<li class="fi">Eastern European</li>
<li class="fi">Show More</li>
</ul>
</div>
</div>
<style>
.header{
display: flex;
align-items: center;
}
.hide {
height: 0;
opacity: 0;
margin: 0;
}
</style>
I've added pointer-events: none so it doesn't intercept mouse events and it works, but you should probably find another way to deal with it. I don't know what you're doing for animations, code, nor the rest of the styling, so this might be the best solution depending on what you're doing.
I've added a "Show Problem" button to show what's happening. The list is (repainted in a new stacking context triggered by opacity) and covering the second list.
Another solution is to set the position style. So adding position: relative to the .hide { position: relative } will also work
Scroll to bottom to read a detailed description of the cause (opacity style).
// show problem
test.onclick=()=>{
if (lists[0].classList.contains("hide1")) {
lists[0].classList.remove("hide1");
} else {
lists[0].classList.add("hide1");
}
}
const buttons = document.getElementsByClassName("toggle");
const lists = document.getElementsByClassName("list");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
toggle(i);
})
};
function toggle(i) {
if (lists[i].classList.contains("hide")) {
lists[i].classList.remove("hide");
} else {
lists[i].classList.add("hide");
}
}
.header { display:flex; flex-direction:row }
.header{
display: flex;
align-items: center;
}
.hide {
height: 0;
opacity: 0;
margin: 0;
pointer-events: none /* don't intercept mouse events */
}
/* show problem */
.hide1 {
height: 0;
margin: 0;
opacity: 0.5;
border: 1px solid red;
}
.hide1 * {
border: 1px solid blue;
}
<button id="test">Show Problem</button>
<div id="sidebar">
<div class="side">
<div class="header">
<h3>Protein</h3>
<button class="toggle"></button>
</div>
<div>
<ul class="list">
<li class="fi">Beef</li>
<li class="fi">Fish</li>
<li class="fi">Lamb</li>
<li class="fi">Pork</li>
<li class="fi">Poultry</li>
<li class="fi">Shellfish</li>
<li class="fi">Vegetarian</li>
</ul>
</div>
</div>
<div class="side">
<div class="header">
<h3>Cuisine</h3>
<button class="toggle"></button>
</div>
<ul class="list">
<li class="fi">African</li>
<li class="fi">American</li>
<li class="fi">Asian</li>
<li class="fi">British</li>
<li class="fi">Cajun Creole</li>
<li class="fi">Carribean</li>
<li class="fi">Eastern European</li>
<li class="fi">Show More</li>
</ul>
</div>
</div>
<style>
.header{
display: flex;
align-items: center;
}
.hide {
height: 0;
opacity: 0;
margin: 0;
pointer-events: none
}
</style>
The problem is related to the z-index stacking context being triggered by the opacity style due to re-render: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
You can prove this by setting opacity: 0.9 on the second list's header, so that the second list's header renders in the same context layer, and see that you will then be able to click the button.
This SO answer summarizes it well:
What has bigger priority: opacity or z-index in browsers?
If an element with opacity less than 1 is not positioned, implementations must paint the layer it creates, within its parent stacking context, at the same stacking order that would be used if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’
I am trying to make the toggled descriptions either slide or fade in, instead of suddenly appear. I also want to preserve the feature of the text moving up and down to accommodate the text that has been toggled on. Ideal situation would be to do this with CSS or Javascript, without jQuery etc.
Already tried CSS opacity transition, but the text doesn't move up and down to accommodate the toggled on text;
function view(id) {
var x = document.getElementsByClassName("descriptions");
var i;
for (i = 0; i < x.length; i++) {
if (x[i].id !== id)
x[i].style.display = "none";
}
var e = document.getElementById(id);
if (e.style.display == 'block')
e.style.display = 'none';
else
e.style.display = 'block';
}
.descriptions {
display: none;
}
<div class="toggle" id="a" onclick="view('a1');">Toggle Div 1
<div id="a1" class="descriptions"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
<div class="toggle" id="b" onclick="view('a2');">Toggle Div 2
<div id="a2" class="descriptions"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
<div class="toggle" id="c" onclick="view('a3');">Toggle Div 3
<div id="a3" class="descriptions"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
Maybe something like this using the height?
function view(id) {
let el = document.getElementById(id);
if (el.classList.contains('hide')) {
el.classList.remove('hide');
} else {
el.classList.add('hide');
}
}
.toggle {
overflow: hidden;
}
.descriptions {
max-height: 100px;
transition: all 0.5s ease-in;
}
.hide {
max-height: 0 !important;
transition: all 0.2s ease-out;
}
<div class="toggle" id="a" onclick="view('a1');">Toggle Div 1
<div id="a1" class="descriptions hide"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
<div class="toggle" id="b" onclick="view('a2');">Toggle Div 2
<div id="a2" class="descriptions hide"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
<div class="toggle" id="c" onclick="view('a3');">Toggle Div 3
<div id="a3" class="descriptions hide"> Here's some text we want to toggle visibility of. Let's do it!</div>
</div>
I have a Basket component which needs to toggle a BasketContents component when clicked on. This works:
constructor() {
super();
this.state = {
open: false
}
this.handleDropDown = this.handleDropDown.bind(this);
}
handleDropDown() {
this.setState({ open: !this.state.open })
}
render() {
return(
<div className="basket">
<button className="basketBtn" onClick={this.handleDropDown}>
Open
</button>
{
this.state.open
?
<BasketContents />
: null
}
</div>
)
}
It uses a conditional to either display the BasketContents component or not. I now want it to fade in. I tried adding a ComponentDidMount hook to BasketContents to transition the opacity but that doesn't work. Is there a simple way to do this?
An example using css class toggling + opacity transitions:
https://jsfiddle.net/ybktodLc/
Here's the interesting CSS:
.basket {
transition: opacity 0.5s;
opacity: 1;
}
.basket.hide {
opacity: 0;
pointer-events:none;
}
And the render function:
render() {
const classes = this.state.open ? 'basket' : 'basket hide'
return(
<div className="basket">
<button className="basketBtn" onClick={this.handleDropDown}>
{this.state.open ? 'Close' : 'Open'}
</button>
<BasketContents className={classes}/>
</div>
)
}
I would use react-motion like this:
<Motion style={{currentOpacity: spring(this.state.open ? 1 : 0, { stiffness: 140, damping: 20 })}}>
{({currentOpacity}) =>
<div style={{opacity: currentOpacity}}>
<BasketContents />
</div>
}
</Motion>
I haven't tested it, but it should work.
I was doing this for a mobile menu hamburger button for expanding and closing the nav. I wanted to keep rendering the contents but just want a smooth transition every time I opened/closed the menu. This is my solution. On compontentDidMount() and on every menu hamburger button click and close button click I set the opacity to 0 and wait for 1 millisecond in setTimeout before adding the transition:
handleMenuExpand = () => {
this.handleTransition(false);
}
handleMenuShrink = () => {
this.handleTransition(true);
}
handleTransition = (isHidden) => {
this.setState({
transitionStyle: {
opacity: '0'
},
isNavHidden: isHidden
});
setTimeout(() => this.setState({
transitionStyle: {
transition: 'opacity 0.8s',
opacity: '1'
}
}), 1
);
}
componentDidMount() {
this.handleTransition(this._isMobile);
}
return(
<nav className="navbar-container" style={this.state.transitionStyle}>
{ (this.state.isNavHidden) ?
<ul className="navbar-content">
<li className="menu-expand-container" style={topBarStyle} >
<img
src={MenuHamburgerPic}
style={menuButtonStyle}
alt="Menu Pic"
onClick={this.handleMenuExpand}
/>
</li>
</ul>
:
<ul className="navbar-content">
{(this._isMobile) &&
<li style={closeButtonContainerStyle} >
<img
src={MenuClosePic}
style={closeMenuButtonStyle}
alt="Menu Pic"
onClick={this.handleMenuShrink}
/>
</li>
}
<li>NAV ELEMENT 1</li>
<li>AOTHER NAV ELEMENT</li>
</ul>
}
</nav>
);
I want to create a css with following specifications :
When I click on a link / button in a form, it should expand its options horizontally.
It should collapse after selecting the option.
EDIT:
To elaborate my query, let me attach the few images which will explain the exact query.
Step 1. Onhover or OnClick on Image Field, it should expand a Div included with 4 Images.
Step 2. If I select one of the Four Image, the collapse the Div and Display the Selected image in the Image field.
Please suggest the best possible code.
well..hopefully i have understood you correctly..
var expandFn = function(){
var wrappBl = document.getElementsByClassName('expand')[0];
var trigg = document.getElementById('trigger');
var el = document.getElementsByTagName('input');
var elArr = [].slice.call(el);
this.getAction = function(){
trigg.addEventListener('click', function(){
if(wrappBl.classList.contains('active')){
wrappBl.classList.remove('active');
}
else {
wrappBl.classList.add('active');
}
});
}
getAction()
this.checkIf = function(){
for (var i = 0; i < elArr.length; i++) {
elArr[i].addEventListener('click', function(){
if( this.checked === true){
wrappBl.classList.remove('active');
};
})
}
}
checkIf();
}
setTimeout(function(){expandFn();},0)
.expand input,
.expand label{
display: block;
width: 50%;
}
div.expand{
width: 100px;
-webkit-transition: transform 400ms ease, opacity 400ms ease;
transition: transform 400ms ease, opacity 400ms ease;
visibility:hidden;
opacity: 0;
}
div.expand.active{
visibility: visible;
opacity: 1;
}
<div class="wrap">
<a id="trigger" href="#">expand</a>
<div class="expand">
<h4>title of the option</h4>
<input name="option1" type="checkbox">
<label for="option1">choose that</label>
<input name="option2" type="checkbox">
<label for="option2">choose that</label>
<input name="option3" type="checkbox">
<label for="option3">choose that</label>
</div>
</div>