I have this problem where if I click on an item, all the items in that column gets affected by the click. I want only that very item where I clicked to have the class. but in my code, all the other items of that column are getting the class when I click on a certain single item. I want to make a div go fullscreen when I click on that particular div. kinda like modal pop up, here I want that the div slowly animates to fullscreen
export default class App extends React.Component {
constructor(props) {
super(props);
this.addActiveClass = this.addActiveClass.bind(this);
this.state = {
activeIndex: -1
};
}
addActiveClass(activeIndex) {
this.setState(prev => ({
activeIndex: prev.activeIndex === activeIndex ? -1 : activeIndex
}));
}
render() {
return (
<div className="container">
{Array.from({ length: 30 }).map((item, index) => {
return (
<div
key={index}
style={{
background: randomColor(colors),
height: randomHeight(100, 200)
}}
className={this.state.activeIndex === index ? "full" : ""}
onClick={() => this.addActiveClass(index)}
/>
);
})}
</div>
);
}
}
* {
box-sizing: border-box;
}
body {
margin: 40px;
background-color: #fff;
color: #444;
font: 2em Sansita, sans-serif;
}
.container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
max-height: 100vh;
}
.container > * {
border: 2px solid orange;
border-radius: 5px;
margin: 10px;
padding: 10px 20px;
background-color: red;
color: #fff;
}
.full{
width: 100%;
height: 100%;
transition: 2s;
}
sandbox demonstrating the problem
I don't know exactly what kind of effect you would like to achieve with the selected item but items in the column are affected because of the default value of flexbox align-items which is stretch. The other items are stretching to match the selected element. Try to add align-items: flex-start to your .container class.
You'll have to make sure you're adding the class to the event target. Your onClick function needs to take the event as one of it's arguments, and you need to use event.target in the function body to make sure you're only affecting the element that is clicked. Hope that makes sense!
https://developer.mozilla.org/en-US/docs/Web/API/Event/target
The class adding logic works ok. You can observe this by checking the elements on developer tools. The problem is you are adding an inline style to your elements.
style={{
background: randomColor(colors),
height: randomHeight(100, 200)
}}
So, after each new render, this overrides the full class and give new height and new color.
The other problem is your full class. You are using 100% for height and width and probably this causes problems for your flex.
Just remove the inline style part and instead of using a percentage give custom width and height to full class, then you can see adding the class logic works well.
Pass down an event (e) from your onClick, then access that specific item through e.target.className. This way only the clicked item is updated, even if dynamically rendered from an array:
handleClick = e => { e.target.classNamme = 'class' }
<div className='' onClick={e => handleClick(e)}></div>
Alternatively, if you're keeping your classes in the state, you can just replace the string value with your variable and control in inside the function, as you've done in the example you've provided.
Adding !important overrides the inline-style height.
Height is set to 100vh to expand the div into full screen.
.full{
width: 100%;
height: 100vh !important;
transition: 2s;
}
Related
There are plenty other post regarding CSS transition not working due to the usage of display and I do get the point. The proposed solutions are mostly the usage of opacity and visibility instead of display. Well I dind't want to do it that way even if I could as my positioning is absolute and wouldn't take any space with visibility set to 0, just like with display: none.
However, when I've read this thread, where the accepted answer says "...cannot be applied to a class that is transitioning from display: block; to display: none;" I thought I'm smart and applied the transition to it's child elements, meaning the parent toggles from display: none to display: flex and the children transition from opacity: 0 to opacity: 1. It's still not working and my assumption is that this is due to the same issue of display/transition not wanting each other, but my question is why? First I thought the display value may be inherited but it's not as the children aren't flex.
could anyone explain why it behaves like that?
To as well provide some code here is my react code using styled components for the styling.
const NavigationWrapper = styled.nav`
& {
position: relative;
display: flex;
flex: 1 0;
align-items: center;
justify-content: flex-end;
}
#media (max-device-width: 992px) {
& button {
border: none;
background-color: red;
margin-right: 10px;
}
}
#media (min-device-width: 993px) {
& button {
display: none;
}
}
`;
const NavList = styled.ul`
& {
list-style: none;
display: flex;
margin: 0;
padding: 0;
}
& li {
padding: 10px 20px;
}
#media (max-device-width: 992px) {
& {
position: absolute;
display: ${(props) => (props.isMobileNav ? "flex" : "none")};
flex-direction: column;
right: 0;
top: 100%;
width: 100vw;
background-color: transparent;
}
& li {
opacity: ${(props) => (props.isMobileNav ? 1 : 0)};
background-color: white;
transition: opacity 1s ease;
}
}
`;
const Navigation = (props) => {
const [isMobileNav, setIsMobileNav] = useState(false);
return (
<NavigationWrapper>
<button onClick={() => setIsMobileNav(!isMobileNav)}></button>
<NavList isMobileNav={isMobileNav}>
<li>Blog</li>
<li>Profiles</li>
<li>Contact</li>
</NavList>
</NavigationWrapper>
);
};
EDIT: I tried it obviously the other way around with opacity on parent and display on children, just to try and, yes, it works. But leads to the solution I didn't want as I could set visibility to hidden or opacity to 0. I just want to know why the visibility of the parent affects the transition of the child.
Well, an element with display: none; effectively gets pulled out of the flow. The code is still there, but it is treated as if it is not.
If a parent element is "removed" in this way, all children of that parent are "removed" with it, and this happens instantly. So that is why you can't do transitions of child elements, when the parent is "gone".
A solution though (if you absolutely have to use display), could be to use a setTimeout() that waits for the duration of the transition, before setting display: none; (the other way, setting display: flex; should work with transitions on child elements just fine).
Something like this, although this simple example poses issues regarding unmounting, and you should quard it with a cancel mechanism, that can interrupt the timeout when unmounting the component:
<button
onClick={() => {
// Delayed
if (isMobileNav) {
setTimeout(() => {
setIsMobileNav(false);
}, 1000);
return;
}
// Instant
setIsMobileNav(true);
}}
>
</button>
I have a custom component in one file, lets say like that:
import styles from './CustomComponent.module.css'
export default CustomComponent = ({className, onClick, text, ...props}) => {
return (
<div className={styles.outer}>
<div className={`${styles.inner} ${className}`}>{text}</div>
</div>
)
}
That component is imported into another file, which has it's own components and css module:
import styles from './ParentComponent.module.css'
import CustomComponent from '../CustomComponent/CustomComponent.component.js'
export default ParentComponent = () => {
return (
<>
This is an example.
<CustomComponent text='Example' className={styles.overrideStyle} />
</>
)
}
What I want to happen is that the overrideStyle can be used to add css parameters and/or override some existing ones of the container style. What happens is that new parameters are added, if were not previously defined in the inner style, but already existing ones are not overwritten.
For example, if the CustomComponent css is something like:
.outer {
width: 100%;
padding-top: 20px;
}
.inner {
height: 100px;
width: 100%;
}
and overrideStyle is something like this:
.overrideStyle {
height: 500px;
padding: 20px;
}
I expected the resulting css properties of the CustomComponent inner div to behave as follows:
.(combined) {
height: 500px;
width: 100%;
padding: 20px;
}
What I get is:
.(combined){
height: 100px;
width: 100%;
padding: 20px;
}
New properties are added, but existing ones are not overwritten.
What rules are there that I don't understand and how can I force the className that is passed through props to supersede the inner one?
There is a typo in your CustomComponent it should be
<div className={${styles.containerStyle} ${className}}>{text}</div>
instead of only <div className={${styles.container} ${className}}>{text}</div>
Change styles.container to styles.containerStyle in CustomComponent
One fix provided by a friend of mine is to mark the properties of overrideStyle with !important:
.overrideStyle {
height: 500px !important;
padding: 20px !important;
}
This does not solve the problem, but it provides a quick and dirty fix for now.
I have a sidebar and I have 2 functions.
One opens it and the other one closes it.
Here are the two functions.
function openNav() {
document.getElementById("myNav").style.width = "100%";
}
function closeNav() {
document.getElementById("myNav").style.width = "0%";
}
I want to create a condition between both so I can toggle them but for that I need to detect if it's opened or closed.
How can I detect it so I can create a toggle function calling the functions above?
You don't need two functions. You can do this with just one CSS class and one function that toggles that one class on the sidebar element.
To do this, add the default width of 0% on the sidebar and then create another class, lets call it .sidebarToggle, that changes the width to 100%.
In javascript, you just need one function, lets call it toggleSidebar() that just toggles the .sidebarToggle class. This way, you not only need less code but also don't need to worry about checking whether the sidebar is opened or closed.
A Better Solution
A better approach is to change the transform property of the sidebar instead of the width. Changing transform property, in this case, is better as compared to changing width, because if you change the width, you will have to handle the:
resizing of the child elements of the sidebar as the sidebar's width is increased or decreased.
remove the left or right padding (if there's any) on the sidebar when the width of the sidebar is 0px or 0%. If you don't remove the padding, sidebar will not completely hide on 0px or 0% width.
With transform, you don't need to worry about the above mentioned points because, instead of resizing, we just translate the sidebar from one point to another.
Animating transform property is also more efficient as compared to animating the width property because changing transform property doesn't causes the browser to go through Layout and Paint steps of its critical rendering path whereas changing the width property will cause the browser to again go through these steps.
Following code snippet shows an example:
const btn = document.querySelector('button');
const sidebar = document.querySelector('.sidebar');
btn.addEventListener('click', () => {
sidebar.classList.toggle('sidebarToggle');
});
body {
margin: 0;
}
.sidebar {
display: flex;
flex-direction: column;
background: #666;
color: #fff;
padding: 15px 25px;
box-sizing: border-box;
width: 30%;
height: 100vh;
transform: translateX(-100%);
position: absolute;
transition: transform 0.5s ease;
}
span {
margin: 5px 0;
}
.sidebarToggle {
transform: translateX(0);
}
button {
position: absolute;
right: 0;
background: green;
color: #fff;
padding: 15px 25px;
}
<div class="sidebar">
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
</div>
<button>Toggle Sidebar</button>
This is best done with classes. That way, stying is better separated from behaviour and there is even a toggle function built in:
document.getElementById("button").onclick = function() {
document.getElementById("myNav").classList.toggle("width100");
};
#myNav {
background-color: red;
display: block;
height: 100px;
width: 0%;
}
#myNav.width100 {
width: 100%;
}
<nav id="myNav"></nav>
<button id="button">Toggle</button>
What I am trying to do:
I have a card that when hovered over expands and does a bunch of different animations i worked on. this is great for desktop and not mobile. So when the screen is a certain res I make a toggle button visible. I want this button in react to when clicked on enable the save:hover State its grabbing from my css.
The Problem
The button is inside the card aleady and everything in the css is setup for the parent div card.
my code structure simplified
<div className="card">
<-- CODE HERE -->
<Button className="myToggleButton" />
<div/>
I cant from what i can tell in css exclusively say myToggleButton:focus do these changes to the following classes since they are parent ones. So i think my only other way to do that is by telling it in react somehow to say that my div is in :hover state, but I can't quite figure out how despite my efforts. Thank you in advance for any help on this.
Instead of activating your animation with the :hover pseudo class, you can simply add or toggle another custom class, which would contain the values to transition or keyframes to animate, to the card element when the button is clicked. In this example I just use a transition, but you could also employ a more complex keyframe animation.
const card = document.querySelector(".card");
const button = document.querySelector(".card > button");
const animate = () => {
card.classList.toggle("small");
card.classList.toggle("big");
}
button.addEventListener("click", animate);
.card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
transition: all 250ms ease-in-out;
}
.small {
width: 200px;
height: 50px;
background-color: gray;
}
.big {
width: 400px;
height: 150px;
background-color: blue;
}
<div class="card small">
My Amazing Card
<button>Animate</button>
</div>
I am trying to centrally position a modal whose button is clicked in grid column 2. Can anyone please point me in the right direction. I wouldn't want to hard code any margins ofcourse (responsive).
Right now it (green div) is only showing up / restricted to grid-column 2 as seen below .
display: grid;
grid-template-columns: 1fr 1fr;
height: 100%;
margin: 10px;
class Login extends Component {
state = {
isShowModal: false
};
openModal = () => {
this.setState({ isShowModal: true })
}
render() {
const { isShowModal } = this.state;
return (
<>
<div className="grid-1">
</div>
<div className="grid-2">
{isShowModal && <Modal />}
<div className="add">
<button onClick={() => this.openModal()} type="button">Open modal</button>
</div>
</div>
</>
)
}
}
Hi I think what you are looking for is Width max-content with some padding and margin 0px auto.
margin: 0px auto;
width: max-content;
padding: 10px;
max-width:100%; //for responsive purposes.
Then make a surrounding div and position it fixed. And apply the above styles to the divs inside your surrounding div
What this is doing is making a surrounding div that you can design your stuff inside of that will be centered.
Side Note: If you div is falling underneath other elements just set the z-index to 1.
z-index:1;