I am trying to animate a side menu, but for some reason it just wont work!
I read on a few answers that using a map to map the <li> items could be the issue (and I have tried with just a few items with static text), but that doesnt seem to be the problem.
Additionally, I have also put the generated items into a state, and wrapped the set with an useEffect to prevent retriggering of the map, still doesn't work. Only the transition is not working, the menu is appearing where it must appear, closes itself when I click the menu button, etc.
EDIT: I think it is worth mentioning that while working on it on my development server, when I am updating my css file (its not refreshing the page), when I change some of the styles that make the sidebar move, it moves with animation, so the animation is there, it is just not working when I am clicking the open/close button but it just appears instantly.
P.S. uuid() generates unique key for each element, so that is not an issue either.
export default function SideBar({ names, closeSidebarOnClick, show }) {
const [robots, setRobots] = useState();
useEffect(() => {
setRobots(GenerateNavItems());
}, names);
function GenerateNavItems() {
return names.map((robotName) => {
return (
<li key={uuid()} className={`nav-item`} onClick={() => {closeSidebarOnClick();}}>
{robotName}
</li>
);
});
}
return (
<nav className={`sidebar ${show ? "show-sidebar" : ""}`}>
<ul>{robots}</ul>
</nav>
);
}
.nav-item {
padding-left: 15px;
text-decoration: none;
font-weight: bold;
}
.nav-item:hover {
color: red;
cursor: pointer;
}
.toggle-sidebar-button {
user-select: none;
cursor: pointer;
padding-left: 1.5rem;
padding-top: 0.5rem;
border: none;
background: transparent;
z-index: 300;
/* z-index: 50; */
}
.toggle-sidebar-button:active {
border: none;
}
.toggle-sidebar-button-line {
width: 35px;
height: 5px;
background-color: black;
margin: 6px 0;
}
.sidebar {
height: 100%;
position: fixed;
top: 0;
left: 0;
width: 200px;
z-index: 200;
background-color: white;
transform: translateX(-100%);
transition: transform 1s ease;
}
.sidebar.show-sidebar {
transform: translateX(0);
}
.sidebar ul {
height: 100%;
list-style: none;
padding-left: 3rem;
padding-top: 3rem;
display: flex;
flex-direction: column;
}
<div id="root">
<div class="content row">
<div class="toggle-sidebar-button">
<div class="toggle-sidebar-button-line"></div>
<div class="toggle-sidebar-button-line"></div>
<div class="toggle-sidebar-button-line"></div>
</div>
<nav class="sidebar ">
<ul>
<li class="nav-item">ROBOT 1</li>
<li class="nav-item">ROBOT 2</li>
</ul>
</nav>
<div class="rendered-robot col">
<div>CHOOSE ROBOT</div>
</div>
</div>
</div>
It's possible that as you are re-rendering the nav component so the transition is not been visible. I suggest you to try this experiment - In closeSidebarOnClickdont don't let this do a re-render and just use the - document.getElementsByTagName('nav')[0].classList.add('show-sidebar');
There is a way to create a React.createRef() or useRef as mentioned here but this again is not to be used frequently.
Well, it may be a bad practice but when it comes to css animations what is the alternative we have? I use these animations via redux all the time :) I think the dom must not be manipulated directly frequently but since this is a nav bar and likely there is only one nav in the entire app, this much shall be ok. Let me know in case you find a typical react way of getting css animations work.
I was having this same issue, and uuid() WAS the problem. Simply using a different type of key solved the transition issue.
Related
I have written a simple navbar that has a scroll animation.
Example:
<div className={"nav active"}>
Here CSS on active is not working. Even if I write .active or nav.active in my CSS file, I have no result. In my code I have used a on scroll event listener so when I scroll down, the navbar appears black and when I go on the navbar, it disappears. But the problem is that CSS is working in nav, but not working in
active and as a result black color in active is not appearing.
const [show , handleshow]= useState(false);
const transitionNavBar = () => {
if (window.scroll > 100){
handleshow(true);
}else{
handleshow(false);
}
};
useEffect(() => {
window.addEventListener("scroll",transitionNavBar);
return () => window.removeEventListener("scroll", transitionNavBar);
}, []);
return (
<div className={`nav ${show && "active"}`}>
<div className="nav_contents">
<img className='nav_logo'
src="http://assets.stickpng.com/images/580b57fcd9996e24bc43c529.png"
alt=""
/>
<img className='nav_avatar' src="https://i.pinimg.com/originals/0d/dc/ca/0ddccae723d85a703b798a5e682c23c1.png"
alt=""
/>
</div>
</div>
)
}
.nav{
position: fixed;
top: 0;
padding: 20px;
width: 100%;
height: 30px;
z-index: 1;
}
.nav.active{
background-color: #111;
}
.nav_contents{
display: flex;
justify-content: space-between;
}
.nav_logo{
position: fixed;
top: 10px;
left: 0%;
width: 80px;
object-fit: contain;
padding-left: 20px;
cursor: pointer;
}
.nav_avatar{
position: fixed;
cursor: pointer;
right: 20px;
width: 30px;
height: 30px;
}
Instead of this
<div className={`nav ${show && "active"}`}>
Try this:
<div className={`nav ${show ? "active":""}`}>
I think you need to write your CSS in a seperate file, if you are not using styled components for example.
Write your CSS classes inside a seperate file and then import the css file inside your JSX/JS.
For example:
import "./navbar.css"
Edit: Sorry, just reread and noticed your nav is working.
As the other posts suggest, you would probably need to use a conditional operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
I don't know what was a problem but i uninstall ns install vs code again my issue was solved I really don't know how it worked but it worked.
I'm having issues with an overlay navbar which is supposed to slide in from top.
Not sure why, but if i click the hamburger menu, the proper css class is applied, but the transition doesn't occur (it acts like a show/hidden).
However, if i change the top property value from the dev tool, it works as expected.
what am i missing?
Here's the react component
import React, { useState } from "react";
import NavMenuIcon from "../../public/svg/nav.svg";
import styles from '../../styles/Navbar.module.scss'
import Link from 'next/link'
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
const Menu = props => (
<div className={ props.toggle ? `${styles.menu__container} ${styles.toggle}` : `${styles.menu__container}` }>
<div className={styles.menu__content}>
<ul className={styles.menu__list}>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
return (
<div className={styles.navbar}>
<i>Meuartelie</i>
<NavMenuIcon className={styles.hamburger} onClick={toggleMenu} />
<Menu toggle={menu}></Menu>
</div>
);
}
export default Navbar;
and here's the css
#import './variables.scss';
.navbar {
box-sizing: border-box;
position: fixed;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5%;
z-index: 5;
}
.menu__container {
font-family: 'Jokerman Std';
position: absolute;
top: -300px;
left: 0;
width: 100vw;
height: 300px;
z-index: -1;
background-image: url("../public/png/welcome_mobile.png");
background-color: black;
background-size: cover;
overflow: hidden;
transition: 850ms;
}
.menu__content {
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20%;
}
.menu__container.toggle {
top: 0;
transition: all 1s ease-in;
}
.menu__list {
width: 80%;
text-align: center;
list-style: none;
padding: 0px;
line-height: 2;
margin: 0px;
margin-bottom: 20px;
}
.menu__item {
transition: padding-left .7s ease, color .7s ease;
/* underline css*/
text-decoration: none;
position: relative;
}
.menu__item:hover {
color: $base-color;
padding-left: 20px;
cursor: pointer;
}
.menu__item:after {
content: '';
height: 2px;
/* adjust this to move up and down. you may have to adjust the line height of the paragraph if you move it down a lot. */
bottom: 0px;
/* center - (optional) use with adjusting width */
margin: 0 auto;
left: 0;
right: 0;
width: 40%;
background: #fff;
/* optional animation */
-o-transition:.5s;
-ms-transition:.5s;
-moz-transition:.5s;
-webkit-transition:.5s;
transition: .5s;
}
/* optional hover classes used with anmiation */
.menu__item:hover:after {
background: $base-color;
}
.hamburger {
z-index: 1000;
cursor: pointer;
}
This has to do with how react determines what to rerender.
Whenever react determines a new reference in the render tree, it automatically rerenders that element.
CSS transitions on the other hand will transform from a starting state, to the end state.
If the start and end are the same, no transition is applied.
So what's actually happening:
The button gets clicked.
The render tree is modified. specifically const Menu is assigned a new reference.
React removed any old elements from the dom, in this case <div class="menu__container">
React adds the new elements to the dom, in this case <div class="menu_container toggle">
the browser renders the element and applies the initial css rules. the element receives the top:0 as the initial state. there is no transition since the old element was removed.
How to fix it:
Make sure you React knows we're still rendering the same element, so it will update the existing dom element, instead of removing and adding a new element.
several ways to do this, the recommended way is to extract the element from the render function. The reference becomes static then
//extracted from render function:
const Menu = (props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} toggleMenu={toggleMenu}></Menu>
</div>
);
}
Sometimes its inconvenient to extract the component from the render function, for example If the components is dynamically created/determined.
You can then use a the hook useMemo to tell react to store the reference, and only recalulate the value if a prop in the dependency-array changes.
function NavbarAndMenuMemoized() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
// useMemo will not recalculate Menu each render, instead it will keep the same reference for Menu.
const Menu = useMemo(()=>{
return(props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
}, [])
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} ></Menu>
</div>
);
}
live demo Example
I'm building a Navbar with react-bootstrap, but come across the following problem.
When I scroll to the top/right/bottom or keep the page still, everything looks fine.
But when I scroll to the left, there is a white space appear on the right of the content, making navbar exceeding the length of the main body.
Here's my script:
const HomePage = () => {
return (
<div id="homePageTitle">
<h1>Home Page</h1>
</div>
)
}
class Header extends React.Component {
render() {
return (
<div>
<Navbar inverse collapseOnSelect id="navbar">
<Navbar.Header>
<Navbar.Brand>
<p>Brand</p>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav pullRight>
<NavItem eventKey={1}>Home</NavItem>
<NavItem eventKey={2}>Projects</NavItem>
<NavItem eventKey={3}>Passions</NavItem>
<NavItem eventKey={4}>Contact</NavItem>
</Nav>
</Navbar.Collapse>
</Navbar>
<HomePage />
</div>
)};
}
Here's my css:
/*---------Header----------*/
.navbar-brand {
min-height: 100px;
}
.navbar.navbar-inverse{
width: 100%;
height: 100px;
margin-bottom: 0;
background-color: lightskyblue;
border: lightskyblue;
border-radius: 0;
position: fixed;
top: 0;
transition: opacity 0.5s;
}
.navbar-right {
margin-top: 30px;
font-size: 18px;
}
/*---------HomePage----------*/
#homePageTitle {
padding-top: 80px;
padding-bottom: 420px;
width: 100%;
text-align: center;
background-color: lightcoral;
margin: 0;
color: #507e9a;
}
Even just a clue as to why this happens is much appreciated! Thanks!
The body has a default margin of 8px. If you want the whitespace there, then add the same
margin: 0 8px;
to the navbar class. Alternatively you could add:
body{
margin: 0;
}
and that would remove the white space altogether. This happens because the #homePageTitle is still in the document flow whereas the navbar being position: fixed is removed from document flow and thus isn't influenced by the margin of the body since it is no longer bounded by it.
You can see this even more by adding:
body{
position:relative;
}
This will set body as the context for the navbar to be fixed in, and thus be influenced by the body's margin yet again. This is probably the preferred way to do it if you want to keep the margin there as you won't need to set margins on any other position: fixed elements.
Without being able to play around with the page itself it's hard to say for sure, but I'm fairly sure this is your issue.
I have found someone with a similar problem and it is solved by disabling horizontal scroll as suggested in this post. This perfectly solves my problem.
I am trying to show an edit icon on a cells hover state, using font awesome.
How can make the class name unique so I can target it with css for each row?
import {Icon} from 'react-fa'
if(this.props.day.achievements) {
listItems = this.props.day.achievements.map((achievement) => (
<div className="cell" key={achievement + "_achievements"}>
{achievement}
<div className="edit">
<a href="#">
<Icon name="pencil-square" className="edit" />
</a>
</div>
</div>
))
}
I am using the following css:
.cell:hover .edit {
display: block;
}
.edit {
padding-top: 7px;
padding-right: 7px;
position: absolute;
right: 0;
top: 0;
display: none;
}
.edit a {
color: #000;
}
How can I display the icon for each cell?
Since you're using position:absolute on the edit wrapper, try adding position:relative to the .cell. I suspect your icons ARE showing but they're all floating up to the top overlapping with each other.
I'm trying to make an animated menu that when I hover over it , the background (or image) reduces and at the same time the text expands.
Thats my style sheet :
.menus {
float: left;
background-image: url(images/menus_bg.png);
width: 208px;
height: 283px;
}
.menusimg {
width: 208px;
height: 283px;
position: absolute;
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center center;
background-image: url(images/menu1.png);
}
.menusimg:hover {
background-size: 80% 80%;
}
.menusimg, .menusimg:hover {
-webkit-transition: background-size 0.2s ease-in ;
}
.menustxtbox {
font-family: MP;
padding-top: 240px;
width: 208px;
height: 283px;
color: #4c4c4c;
font-size: large;
text-shadow: gray 0.1em 0.1em 0.2em;
}
.menustxtbox:hover {
padding-top: 235px;
font-size: x-large;
color: #4fa3f9;
}
.menustxtbox, .menutxtbox:hover {
-webkit-transition:font-size 0.1s linear;
-moz-transition:font-size 0.1s linear;
}
and the html :
<div class="menus">
<div class="menusimg">
</div>
<div class="menustxtbox">
Text
</div>
</div>
Any ideas? A simple Java script or anything that will solve this problem? :)
Thank you in advance ^^
I second what ntgCleaner said.
In addition you can use:
$('.menus').hover(function(){
$('.menusimg').addClass('active');
$('.menustxtbox').addClass('active');
}, function(){
$('.menusimg').removeClass('active');
$('.menustxtbox').removeClass('active');
});
And your css would have:
.menusimg.active, .menusimg.active{
-webkit-transition: background-size 0.2s ease-in ;
}
etc.
Well, without any code to see that you've done anything or tried anything with javascript, I would suggest this:
Change your CSS to make real sizes of font size first:
.menustxtbox {
font-size:40px;
}
then make some jquery
$('.menus').hover(function(){
$('.menusimg').animate({width: "100px"});
$('.menustxtbox').animate({fontSize: "90px"});
}, function(){
$('.menusimg').animate({width: "208px"});
$('.menustxtbox').animate({fontSize: "40px"});
});
Then delete your :hover css styles
And if you want to use hover, I would suggest looking into hoverintent
UPDATE for a comment below
To do this for each separate menu item, you will have to name things a certain way. Here's an example.
HTML
<div class="menu">
<div class="menuItem" id="menu1">
<div class="menusimg"></div>
<div class="menustxtbox"></div>
</div>
<div class="menuItem" id="menu2">
<div class="menusimg"></div>
<div class="menustxtbox"></div>
</div>
<div class="menuItem" id="menu3">
<div class="menusimg"></div>
<div class="menustxtbox"></div>
</div>
</div>
Then with jQuery, you will have to use $(this) and .children()
$('.menuItem').hover(function(){
$(this).children('.menusimg').animate({width: "100px"});
$(this).children('.menustxtbox').animate({fontSize: "90px"});
}, function(){
$(this).children('.menusimg').animate({width: "208px"});
$(this).children('.menustxtbox').animate({fontSize: "40px"});
});
When you use $(this), you will do whatever you want to the specific thing you are trying to use. Then you just go up or down from there using parent or children to do something to either of those.