React fade in element - javascript

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>
);

Related

How to change to Active when its clicked

I have a list and it's already set to Active, I mean the first list is already active, but my question is how to make the other list active also only when it's clicked and as long it's clicked and on the same page it keeps active.
return (
<div className="sidebar">
<div className="sidebarWrapper">
<div className="sidebarMenu">
<h3 className="sidebarTitle">Dashboard</h3>
<ul className="sidebarList">
<Link to="/" className="link">
<li className="sidebarListItem active">
<LineStyleIcon className="sidebarIcon" />
Home
</li>
</Link>
<li className="sidebarListItem">
<TimelineIcon className="sidebarIcon" />
Analytics
</li>
<li className="sidebarListItem">
<TrendingUpIcon className="sidebarIcon" />
Sales
</li>
</ul>
);
CSS:
.sidebarTitle {
font-size: 18px;
font-weight: 700;
color: rgb(187, 186, 186);
}
.sidebarList {
list-style: none;
padding: 5px;
}
.sidebarListItem.active,
.sidebarListItem:hover {
background-color: black;
color: white;
}
Your component will need to keep state for the currently active item. The initial state can be 0, the index of the first element in the list of items. When another item is clicked, we can setActive(index) for the index of the clicked item. Determining an individual item's active property is a derivative of the active state and the item's index, active == index -
function App({ items = [] }) {
const [active, setActive] = React.useState(0)
return items.map((value, index) =>
<ListItem
key={index}
value={value}
active={index == active}
onClick={_ => setActive(index)}
/>
)
}
function ListItem({ value, active, onClick }) {
return <button
type="button"
class={active ? "active" : "inactive"}
onClick={onClick}
children={value}
/>
}
ReactDOM.render(<App items={["🫐", "🍑", "🥝"]}/>, document.body)
button { border-color: dodgerblue; font-size: 4rem; background-color: white; }
.active { border-color: tomato; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
I would store the list items in an array so that I can loop over them later
const listItems = ["Home", "Analytics", "Sales"];
and I would create a state to store the active index
const [activeIndex, setActiveIndex] = React.useState(0);
and finally I would render the list items like this:
<ul className="sidebarList">
{listItems.map((listItem, index) => {
return (
<li
onClick={() => setActiveIndex(index)}
className={`sidebarListItem ${
index === activeIndex ? "active" : ""
}`}
>
{listItem}
</li>
);
})}
</ul>
when the user clicks on the item .. the activeIndex will be set as the item-index for the clicked item ..
and I am adding the className: "active" to the list-item only when the item-index === activeIndex

function in list only works on the first list item in react

I'm looping through a list, every list item has a arrow button that can show a hidden div underneath it. But right now every button triggers the first item in the list.
Im working right now in react, but i cant find the right solution
My JS react file
const CardConference = ({ content }) => {
const showItems = () => {
const items = document.getElementsByClassName("hidden-items")[0];
const arrow = document.getElementsByClassName("arrow-down")[0]
if (items.style.display == "block") {
items.style.display = "none";
arrow.classList.toggle('rotate-arrow')
} else {
items.style.display = "block";
arrow.classList.toggle('rotate-arrow')
}
}
return (
<div className="card card-conferences ">
<h4> {content[0].title} </h4>
<ul>
{content[0].cities.map((city) => (
<div>
<li className="city-name"> {city.name}
<button className="btn button-arrow" onClick={showItems} ><FaAngleDown color="#717171" className="arrow-down" /></button>
</li>
<ul className="hidden-items">
{city.conferenties.map((conf) => (
<li className="hidden-item">{conf} </li>
))}
</ul>
</div>
))}
</ul>
</div >
);
}
export default CardConference;
And this is my css
.arrow-down {
position: absolute;
margin-top: -11px;
margin-left: -6px;
transition: transform 1s;
}
.rotate-arrow {
transform: rotate(180deg);
}
..hidden-items {
display: none;
}
Because you only get getElementsByClassName for first element. You need to pass an index to get exactly the element.
const showItems = (i) => () => {
const items = document.getElementsByClassName("hidden-items")[i];
const arrow = document.getElementsByClassName("arrow-down")[i]
...
cities.map((city, index)
...
onClick={showItems(index)}
...

React component that zooms into an image while keeping dimensions on mouse over - React + Bootstrap

So I have been looking all over the place over the past few days and cannot find anything that works. I am using React with Bootstrap. I want a stateless functional component that takes an image path and returns an img element which, when hovered over by the mouse, zooms into the image while keeping the dimensions of the element the same.
I have tried:
Changing the style attribute in the onMouseOver and onMouseOut events like so
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
style={{ overflow: "hidden" }}
onMouseOver={(e) => (e.currentTarget.style = { transform: "scale(1.25)", overflow: "hidden" })}
onMouseOut={(e) => (e.currentTarget.style = { transform: "scale(1)", overflow: "hidden" })}
/>
);
};
export default ImageHoverZoom;
Creating a custom css class and applying that to the img element.
index.css:
.hover-zoom {
overflow: hidden;
}
.hover-zoom img {
transition: all 0.3s ease 0s;
width: 100%;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
imageHoverZoom.jsx:
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
);
};
export default ImageHoverZoom;
I have also tried a class component with state
import React, { Component } from "react";
class ImageHoverZoom extends Component {
state = {
path: this.props.imagePath,
mouseOver: false,
};
render() {
const { path, mouseOver } = this.state;
return (
<img
className={`img-fluid w-100`}
src={path}
onMouseOver={() => this.setState({ mouseOver: true })}
onMouseOut={() => this.setState({ mouseOver: false })}
style={
mouseOver
? { transform: "scale(1.25)", overflow: "hidden"}
: { transform: "scale(1)", overflow: "hidden"}
}
alt=""
/>
);
}
}
I would ideally not like to use state as I know it gets updated asynchronously and I feel like that would lead to some lag on the client side when mousing over the image. Any help is much obliged, thank you in advance!
EDIT:
I tried Rahul's answer below in my project, as well as in a brand new project. Here are the relevant (I think) files. Same thing as before. No change on mouse over.
App.js
import "./App.css";
import ImageHoverZoom from "./common/imageHoverZoom";
function App() {
return <ImageHoverZoom imagePath="http://picsum.photos/400/600" />;
}
export default App;
imageHoverZoom.jsx
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css
.img-wrapper {
overflow: hidden;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
Wrap the img tag in a div and then hide the overflow from div:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
</div>
);
};
export default ImageHoverZoom;
add the styling to img-wrapper:
.img-wrapper{
overflow:hidden;
}
img.hover-zoom:hover {
transform: scale(1.25);
}
So I solved it with Rahul's help (Thank you!). The css notation was flipped, like Rahul suggested, and to prevent the width from changing I had to set width: 100% in img.hover-zoom
Here is the component:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css:
.img-wrapper {
overflow: hidden;
}
img.hover-zoom {
transition: all 0.3s ease 0s;
width: 100%;
}
img.hover-zoom:hover {
transform: scale(1.25);
}

Vue.Js v-bind:class does not add class on click

I'm quite new to Vue.js and have been using it in work the last number of weeks. I've been asked to do a small update to existing code but cannot get v-bind:class working correctly.
In my simplified example, if a button is clicked, the other classes should become less prominent, i.e.
If 'Colour' is clicked anything with the class 'sound' or 'shape' reduce in opacity
If 'Shape' is clicked anything with the class 'colour or 'sound' reduce in opacity
If 'Sound' is clicked anything with the class 'colour' or 'shape reduce in opacity
Of course, two buttons shouldn't be selected at the same time as can happen in my example, selecting say, 'Shape' after previously selecting 'Colour' should undo the opacity from the class 'shape' and add to the necessary classes.
Whats happening though is that only the 'Sound' button is functioning correctly... 'Colour' does nothing and 'Shape' only half works!
I know I could use JavaScript to write a method to alter the DOM by getting the elementByClassName but I would like to stick to using Vue correctly (if it is possible)
var app = new Vue({
el: '#app',
data: {
colourAddClass: false,
shapeAddClass: false,
soundAddClass: false,
}
})
.opacity {
opacity: 0.3;
}
.colour {
color: blue;
}
.shape {
color: red;
}
.sound {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="colourAddClass = !colourAddClass"> Colours </button>
<button #click="shapeAddClass = !shapeAddClass"> Shapes </button>
<button #click="soundAddClass = !soundAddClass"> Sounds </button>
<br/><br/>
<span class="colour" v-bind:class="{'opacity': shapeAddClass, 'opacity': soundAddClass}">Yellow</span>
<span class="shape" v-bind:class="{'opacity': colourAddClass, 'opacity': soundAddClass}">Triangle</span>
<span class="sound" v-bind:class="{'opacity': colourAddClass, 'opacity': shapeAddClass}">Woof</span>
<span class="shape" v-bind:class="{'opacity': colourAddClass, 'opacity': soundAddClass}">Circle</span>
<br/>
<span class="sound" v-bind:class="{'opacity': colourAddClass, 'opacity': shapeAddClass}">Meow</span>
<span class="colour" v-bind:class="{'opacity': shapeAddClass, 'opacity': soundAddClass}">Purple</span>
<span class="shape" v-bind:class="{'opacity': colourAddClass, 'opacity': soundAddClass}">Circle</span>
<span class="colour" v-bind:class="{'opacity': shapeAddClass, 'opacity': soundAddClass}">Orange</span>
</div>
There are many ways to accomplish these types of tasks with VueJS. Below is one way. I'm sure others have shorter methods. To explain my method, I am using a single variable to track the current active button. I use a method to set that variable since we want to toggle on and off. I then bind the class attribute to some logic.
var app = new Vue({
el: '#app',
data: {
active: null
},
methods: {
toggleActive( type ) {
//if active is already of this type, set to null
this.active = ( this.active == type ) ? null : type;
}
}
})
.opacity {
opacity: 0.3;
}
.colour {
color: blue;
}
.shape {
color: red;
}
.sound {
color: green;
}
<div id="app">
<button #click="toggleActive('colour')"> Colours </button>
<button #click="toggleActive('shape')"> Shapes </button>
<button #click="toggleActive('sound')"> Sounds </button>
<br/><br/>
<span :class="{ colour: true, opacity: active && active != 'colour' }" >Yellow</span>
<span :class="{ shape: true, opacity: active && active != 'shape' }">Triangle</span>
<span :class="{ sound: true, opacity: active && active != 'sound' }">Woof</span>
<span :class="{ shape: true, opacity: active && active != 'shape' }">Circle</span>
<br/>
<span :class="{ sound: true, opacity: active && active != 'sound' }">Meow</span>
<span :class="{ colour: true, opacity: active && active != 'colour' }">Purple</span>
<span :class="{ shape: true, opacity: active && active != 'shape' }">Circle</span>
<span :class="{ colour: true, opacity: active && active != 'colour' }">Orange</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

How to access child element property in React?

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;
}

Categories