Logo does not change on scrolling using React - html

Summary:
I have this logo in my website header, I want this logo (image [1]) to change to another one (image [2]) on scrolling down, and to change back to the original logo (image [1]) when I scroll back to the top.
What i tried:
I tried to make it with EventListener and useEffect in the header page, but I'm getting this error below:
ERROR in src\layouts\Navbar\index.jsx
Line 12:3: 'listenScrollEvent' is not defined no-undef
My code:
import React, { useState } from 'react'
import { useEffect } from "react";
export default () => {
useState = {
imageSrc: '',
imageAlt: ''
}
listenScrollEvent = e => {
if (window.scrollY > 400) {
this.setState({
imageSrc: './/img/Smartlogger_logo.png',
imageAlt: 'smartlogger white logo'
})
} else {
this.setState({
imageSrc: './../../views/Home/image/smartLoggerheader_logo.png',
imageAlt: 'smartlogger colored logo'
})
}
}
useEffect(() => {
window.addEventListener('scroll', this.listenScrollEvent)
}, []);
return (
<header className='header-area header-sticky'>
<div className='container'>
<div className='row'>
<div className='col-12'>
<nav className='main-nav'>
{/* ***** Logo Start ***** */}
<a href='/#' className='logo'>
<style>{css}</style>
<img
src={this.setState}
alt='Smartlogger logo'
/>
</a>
{/* ***** Logo End ***** */}
</nav>
</div>
</div>
</div>
</header>
)
}

You need to make the following changes in your code. It should fix the issue.
Inside render() - img src replace src={this.setState} with src={this.state.imageSrc}
Inside listenScrollEvent function replace window.scrollY with event.srcElement.body.scrollY
it will look like this (I have used random images here):
listenScrollEvent = event => {
if (event.srcElement.body.scrollY > 400) {
this.setState({
imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
imageAlt: 'smartlogger white logo'
})
} else {
this.setState({
imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
imageAlt: 'smartlogger colored logo'
})
}
}
Full working code : (I have added style={{height:'200vh'}} on container div just to test it on my local. You can remove it)
import React from 'react'
import { Link } from 'react-router-dom'
export default class App extends React.Component {
state = {
imageSrc: 'https://c.tenor.com/TReUojNlZ6wAAAAi/js-javascript.gif',
imageAlt: ''
}
listenScrollEvent = event => {
if (event.srcElement.body.scrollY > 400) {
this.setState({
imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
imageAlt: 'smartlogger white logo'
})
} else {
this.setState({
imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
imageAlt: 'smartlogger colored logo'
})
}
}
componentDidMount() {
window.addEventListener('scroll', this.listenScrollEvent)
}
render() {
return (
<header className='header-area header-sticky'>
<div className='container' style={{height:"200vh"}}>
<div className='row'>
<div className='col-12'>
<nav className='main-nav'>
{/* ***** Logo Start ***** */}
<a href='/#' className='logo'>
{/* <style>{css}</style> */}
<img
src={this.state.imageSrc}
alt='Smartlogger logo'
/>
</a>
{/* ***** Logo End ***** */}
</nav>
</div>
</div>
</div>
</header>
)
}
}
Hope that's how you wanted it to work. Try running it on your local and then you can modify it as per your requiremnets.

In your useState hook you want to have your src and your alt.
Remember useState return an array of 2 elements, the first is the value and the second is the setter for that value.
You can use the setter in your listenScrollEvent function and you can use the value in your jsx.
You are also using a css variable in you jsx that isn't defined anywhere.
It should look something like this:
import React, { useState, useEffect } from "react";
export default () => {
const [image, setImage] = useState({
src: "",
alt: "",
});
const listenScrollEvent = (e) => {
if (window.scrollY > 400) {
setImage({
src: "../img/Smartlogger_white_logo.png",
alt: "smartlogger white logo",
});
} else {
setImage({
src: "../img/Smartlogger_colored_logo.png",
alt: "smartlogger colored logo",
});
}
};
useEffect(() => {
window.addEventListener("scroll", listenScrollEvent);
}, []);
return (
<header className="header-area header-sticky">
<div className="container">
<div className="row">
<div className="col-12">
<nav className="main-nav">
{/* ***** Logo Start ***** */}
<a href="/#" className="logo">
<style>{css}</style>
<img src={require(image.src)} alt={image.alt} />
</a>
{/* ***** Logo End ***** */}
</nav>
</div>
</div>
</div>
</header>
);
};
An alternative solution that looks a little cleaner to me:
import React, { useState, useEffect } from "react";
import whiteLogo from "../img/Smartlogger_white_logo.png";
import coloredLogo from "../img/Smartlogger_colored_logo.png";
export default () => {
const [isScrolled, setIsScrolled] = useState(false);
const listenScrollEvent = (e) => setIsScrolled(window.scrollY > 400);
useEffect(() => {
window.addEventListener("scroll", listenScrollEvent);
}, []);
return (
<header className="header-area header-sticky">
<div className="container">
<div className="row">
<div className="col-12">
<nav className="main-nav">
{/* ***** Logo Start ***** */}
<a href="/#" className="logo">
<style>{css}</style>
<img
src={isScrolled ? whiteLogo : coloredLogo}
alt={
isScrolled
? "SmartLogger white logo"
: "SmartLogger colored logo"
}
/>
</a>
{/* ***** Logo End ***** */}
</nav>
</div>
</div>
</div>
</header>
);
};
EDIT: added note about css variable, fixed typo in code and formatted better
EDIT2: fixed image link and added the require in the img src attribute, this should fix the image loading
EDIT3: added alternative solution

Related

How to style an a element that redirects to react component?

App.js:
import './App.css';
import logo from './images/logo.png';
import Home from './components/Home';
import About from './components/About';
import Calculators from './components/Calculators';
import Classes from './components/Classes';
import Riddles from './components/Riddles';
import { useRoutes, BrowserRouter as Router } from 'react-router-dom';
const MenuBar = () => {
return (
<header className='App-header'>
<div className="container">
<a id="home" className="content-tab" href="/"> Home</a>
<a id="about" className="content-tab" href="/about"> About</a>
<a id="calcs" className="content-tab" href="/calculators"> Calculators</a>
<a id="riddles" className="content-tab" href="/riddles">Riddles</a>
<a id="classes" className="content-tab" href="/classes">Classes</a>
</div>
</header>
)
}
const App = () => {
let routes = useRoutes([
{ path: "/", element: <Home /> },
{ path: "about", element: <About /> },
{ path: "classes", element: <Classes />},
{ path: "calculators", element: <Calculators />},
{ path: "riddles", element: <Riddles /> },
// ...
]);
return routes;
};
function AppWrapper() {
return (
<div className="App">
<MenuBar />
<Router>
<App />
</Router>
</div>
);
}
export default AppWrapper;
Now, I want to make that whenever you select a page, like home, about, calculators, etc; it marks the link using border-bottom: 1px solid white;, but when using
.container a.active {
border-bottom: 1px solid white;
}
It just doesn't work.
Any ideas why?
Can it be because it's redirecting to a different url?
If so, then should I also import app.css into my Home, About, etc components?
You should be using Link, or NavLink if you want to apply active styling, instead of the raw anchor <a /> tag. Use the function callback for the className prop which is passed an isActive prop.
NavLink
import { NavLink } from 'react-router-dom';
const MenuBar = () => {
const getLinkClassNames = ({ isActive }) => [
"content-tab",
isActive ? "active-tab" : null,
]
.filter(Boolean)
.join(" ");
return (
<header className='App-header'>
<div className="container">
<NavLink
id="home"
className={getLinkClassNames}
to="/"
>
Home
</NavLink>
<NavLink
id="about"
className={getLinkClassNames}
to="/about"
>
About
</NavLink>
<NavLink
id="calcs"
className={getLinkClassNames}
to="/calculators"
>
Calculators
</NavLink>
<NavLink
id="riddles"
className={getLinkClassNames}
to="/riddles"
>
Riddles
</NavLink>
<NavLink
id="classes"
className={getLinkClassNames}
to="/classes"
>
Classes
</NavLink>
</div>
</header>
);
}
CSS
.active-tab {
border-bottom: 1px solid white;
}
If you prefer the v5 syntax then create a custom NavLink component.
import { NavLink as BaseNavLink } from "react-router-dom";
const NavLink = React.forwardRef(
({ activeClassName, activeStyle, ...props }, ref) => {
return (
<BaseNavLink
ref={ref}
{...props}
className={({ isActive }) =>
[
props.className,
isActive ? activeClassName : null
]
.filter(Boolean)
.join(" ")
}
style={({ isActive }) => ({
...props.style,
...(isActive ? activeStyle : null)
})}
/>
);
}
);
Usage:
<NavLink
id="home"
className="content-tab"
activeClassName="active-tab"
to="/"
>
Home
</NavLink>

How to move/animate text values in navbar when I toggle the sidebar

How do I flex the contents of my navbar when I toggle the sidebar on/off in ReactJS? In my current website my sidebar overlaps the navbar so when I toggle it on, more than half of the title gets covered (sample1, sample2)
This is my Navbar.js
import React, { useState } from 'react'
import {Navbar, Container} from 'react-bootstrap'
import { Link } from 'react-router-dom';
import { useAuth } from "../contexts/AuthContext"
import './styles/styles.css';
import * as FaIcons from 'react-icons/fa';
import * as AiIcons from 'react-icons/ai';
import { IconContext } from 'react-icons';
import { SidebarItem } from './SidebarItem';
export default function Navubaru({component: Component, ...rest}) {
const { currentUser } = useAuth()
const [sidebar, setSidebar] = useState(false);
const showSidebar = () => setSidebar(!sidebar);
return (
<div>
<Navbar bg="myColor" variant="dark">
<IconContext.Provider value={{ color: '#fff' }}>
<Link to='#' className='menu-bars'>
<FaIcons.FaBars onClick={showSidebar} />
</Link>
<nav className={sidebar ? 'nav-menu active' : 'nav-menu'}>
<ul className='nav-menu-items' onClick={showSidebar}>
<li className='navbar-toggle'>
<Link to='#' className='menu-bars'>
<AiIcons.AiOutlineClose />
</Link>
</li>
{SidebarItem.map((item, index) => {
return (
<li key={index} className={item.cName}>
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
</li>
);
})}
</ul>
</nav>
</IconContext.Provider>
<Container>
<Navbar.Brand href="/">Welcome to my Website</Navbar.Brand>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Navbar.Text>
Signed in as: {currentUser && currentUser.email}
</Navbar.Text>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
)
}
I'd also like to flex/minimize the size of the main page when I toggle the sidebar if there's not much difference in the methods of doing it. I'd appreciate any help and thoughts given. Thank you
You can set padding-left to the navbar's wrapper when sidebar is open and remove it when the sidebar close.
Here is an example navbar.

ReactJS: Navbar Current Page Effect

I want the highlight effect when I'm on the current page. For example, if I'm on the About Page, I woudl like About to be highlighted in the navbar.
How would I do that?
Relevant Code
NavBar.js
function NavBar() {
const [open, setOpen] = useState(false);
NavBar.handleClickOutside = () => setOpen(false);
return(
<nav>
<div className='navbar-container'>
<a href='/' className='logo'>Daniel Zhang</a>
<div className='toggle-button' onClick={() => setOpen(!open)}>
<div className='bar' />
<div className='bar' />
<div className='bar' />
</div>
</div>
<div id='nav-links' className={open ? 'nav-open' : 'nav-collasped'}>
<a href='/'>Home</a>
<a href='/about'>About</a>
{/* <a href='/blog'>Blog</a> */}
<a href='/contact'>Contact</a>
</div>
</nav>
);
}
const clickOutsideConfig = {
handleClickOutside: () => NavBar.handleClickOutside,
};
export default onClickOutside(NavBar, clickOutsideConfig);
You could use the NavLink component from react router dom. Import like:
import { NavLink } from 'react-router-dom';
And instead of <a href='/about'>About</a> you can write:
<NavLink exact to='/about' activeClassName="highlighted">About</Navlink>
And so on for the other links present within the <div id='nav-links'.
Of course you can add the highlighted styles based on the .highlighted
More info, see https://reactrouter.com/web/api/NavLink

How can I give transition effect for this code?

import React from "react";
import "./About.css";
import About_Main from "../components/About_Main";
import About_Data1 from "../components/About_Data1";
import About_Data2 from "../components/About_Data2";
class About extends React.Component {
state = {
main: {
display:"block",
opacity:1
},
data: {
display:"none",
opacity:0
}
};
changeSelect = id => {
if (id === "select1") {
this.setState({
main: {
display:"block",
opacity:1
},
data: {
display:"none",
opacity:0
}});
} else if (id === "select2") {
this.setState({
main: {
display:"none",
opacity:0
},
data: {
display:"block",
opacity:1
}});
}
};
clickHandler = event => {
event.preventDefault();
this.changeSelect(event.target.id);
};
render() {
return (
<div>
<section className="selects">
<div
id="select1"
className="select"
onClick={this.clickHandler}
></div>
<div
id="select2"
className="select"
onClick={this.clickHandler}
></div>
</section>
<section className="about">
<div
className="about_div"
style={{ display: this.state.main.display, opacity: this.state.main.opacity }}
>
<About_Main></About_Main>
</div>
<div
className="about_data"
style={{ display: this.state.data.display, opacity:this.state.data.opacity }}
>
<About_Data1></About_Data1>
<About_Data2></About_Data2>
</div>
</section>
</div>
);
}
}
export default About;
By clicking 2 different buttons, I want to show only one out of two.
Also, I like to give a transition effect so I used opacity. But it is not working!
Is this the only way to set style property using javascript in react?
Can't I use querySelector or something else?
I feel like javascript is much easier than react 😭😭

Divider Disappears When Adding Floated Tag on Vertical Menu Semantic UI React

I'm really confused with an issue I can't wrap my head around. For demonstration purposes, I included a vertical Menu component from Semantic UI. As you can see in the first picture, everything is normal and how I want it, but when I add a floated='right' tag to <Menu />, the bottom dividers disappear.
This is the way it's supposed to be:
This is what happens when the 'floated' tag is added:
import React, { Component } from 'react'
import { Header, Menu } from 'semantic-ui-react'
export default class MenuExampleText extends Component {
state = {}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
const { activeItem } = this.state
return (
<Menu vertical> // <Menu vertical floated='right'> removes the divider
<Menu.Item
name='promotions'
active={activeItem === 'promotions'}
onClick={this.handleItemClick}
>
<Header as='h4'>Promotions</Header>
<p>Check out our new promotions</p>
</Menu.Item>
<Menu.Item
name='coupons'
active={activeItem === 'coupons'}
onClick={this.handleItemClick}
>
<Header as='h4'>Coupons</Header>
<p>Check out our collection of coupons</p>
</Menu.Item>
<Menu.Item
name='rebates'
active={activeItem === 'rebates'}
onClick={this.handleItemClick}
>
<Header as='h4'>Rebates</Header>
<p>Visit our rebate forum for information on claiming rebates</p>
</Menu.Item>
</Menu>
)
}
}
Unless you can reproduce the issue for us or someone has experienced this issue before I'm not sure if anyone will be able to help you. Reproducing your menu in HTML and using float:right; does not appear to have the same issue.
Edit: Updated snippet to more closely follow your codepen and included css fix for display: none that was your accepted answer.
const {
Menu,
Header
} = semanticUIReact
class App extends React.Component {
state = {}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
const { activeItem } = this.state
return (
<Menu vertical style={styles.sidebarMenu} floated='right'>
<Menu.Item
name='promotions'
active={activeItem === 'promotions'}
onClick={this.handleItemClick}
>
<Header as='h4'>Promotions</Header>
<p>Check out our new promotions</p>
</Menu.Item>
<Menu.Item
name='coupons'
active={activeItem === 'coupons'}
onClick={this.handleItemClick}
>
<Header as='h4'>Coupons</Header>
<p>Check out our collection of coupons</p>
</Menu.Item>
<Menu.Item
name='deals'
active={activeItem === 'deals'}
onClick={this.handleItemClick}
>
<Header as='h4'>Deals</Header>
<p>Check out our collection of deals</p>
</Menu.Item>
<Menu.Item
name='rebates'
active={activeItem === 'rebates'}
onClick={this.handleItemClick}
>
<Header as='h4'>Rebates</Header>
<p>Visit our rebate forum for information on claiming rebates</p>
</Menu.Item>
</Menu>
)
}
}
const styles = {
sidebarMenu: {
marginLeft: 0,
marginRight: 0,
height: '100vh'
},
}
// ----------------------------------------
// Render
// ----------------------------------------
const mountNode = document.getElementById('react-app-mount')
ReactDOM.render(<App />, mountNode)
.ui.floated.menu .item:last-child:before {
display: block !important;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js"></script>
<script src="https://unpkg.com/semantic-ui-react/dist/umd/semantic-ui-react.min.js"></script>
<div id="react-app-mount"></div>
For future viewers, the best solution I was able to come up with was creating a second menu within the menu item you are floating to the right. This should make it appear normal