ReactJS: Navbar Current Page Effect - html

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

Related

Logo does not change on scrolling using React

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

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.

How to "stick" icon next to the scrollbar and make it always be there (even if scrolling)?

I've been trying to put the icon very next to the scroll bar in my react page. Now I can't find a way to properly align items in the way I want.
Now here is what I got:
Now red square is where my icon is and green is where I want it to be. Also I want to be able to scroll page with icon still being there(so I want it always to be there). This will be my filtering inputs.
Here is my return of component:
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Form from 'react-validation/build/form';
import ArticleService from '../../services/article.service';
//import Filter from '../Filter/Filter';
import {
MDBDropdown,
MDBDropdownToggle,
MDBDropdownMenu,
MDBDropdownItem,
} from 'mdbreact';
import { Pagination } from 'semantic-ui-react';
import numeral from 'numeral';
const Dashboard = () => {
.....
return (
<div>
{loading ? (
<div className='text-white'>
<div className='float-right'> //here is where I'm using the icon
<MDBDropdown dropleft>
<MDBDropdownToggle caret color='#ffffff'>
<i className='fas fa-chevron-left'></i>
</MDBDropdownToggle>
<MDBDropdownMenu>
<MDBDropdownItem>
<p>Input1</p>
</MDBDropdownItem>
<MDBDropdownItem>
<p>Input2</p>
</MDBDropdownItem>
</MDBDropdownMenu>
</MDBDropdown>
</div>
<div className='container'>
{renderHeader()}
<div className='text-center'>
<Form className='input-group mb-3' onSubmit={addFilter}>
{' '}
<input
type='text'
className='form-control text-center transparent-input'
placeholder='Unesite željenu lokaciju na području grada Sarajeva npr. Dobrinja'
aria-label='Unesite željenu lokaciju na području grada Sarajeva npr. Dobrinja'
aria-describedby='basic-addon2'
value={location}
onChange={onChangeLocation}
/>
<div className='input-group-append'>
<button
className='btn btn-grad rounded'
onClick={submitNameFilter}
>
<i className='fas fa-search-location'></i>
</button>
</div>
</Form>
</div>
<div className='d-flex'>
<div className='row row-cols-1 row-cols-md-3 custom-p'>
{content.map(card)}
</div>
<div></div>
</div>
</div>
<div className='mb-5 mt-5 text-center'>
<Pagination
totalPages={Math.ceil(total / 15)}
onPageChange={(e, d) => {
parsedUrl.set('page', d.activePage);
const pushPageToURL = parsedUrl.toString();
setPage(d.activePage);
window.history.pushState(null, null, `?${pushPageToURL}`);
}}
activePage={page}
/>
</div>
</div>
) : (
<div className='spinner-border text-primary text-center' role='status'>
<span className='sr-only'>Loading...</span>
</div>
)}
</div>
);
};
export default Dashboard;
Note that I'm using bootstrap.
you have to use the fixed position, Which in bootstrap becomes position-fixed || fixed-top.
pure css example:
#icon {
position: fixed;
top: 0;
}
take a look at this example:
https://getbootstrap.com/docs/4.0/examples/navbar-top-fixed/

Added Bootstrap header and now the component is duplicating

I have added a Navbar from Bootstrap to a React App and for some reason, it is duplicating when I render the app. Not sure what is happening. Here is the code from below is my headercomponent.
import React, { Component } from 'react';
import Navbar from './Navbar.jsx';
class HeaderName extends Component {
render() {
return (
<div>
<div>
<Navbar />
</div>
<h1>The AquaStars New Website.</h1>
<img src="../public/images/picture_swimmers.png" />
</div>
)
}
}
export default HeaderName;
Below is the Navbar.jsx component.
import React,{ Component } from 'react';
import { Link } from 'react-router-dom';
import './Navbar.css';
class Navbar extends Component {
render() {
return (
<nav className="navbar navbar-expand-md navbar-dark bg-dark mb-4">
<a className="navbar-brand" href="#">Top navbar</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarCollapse">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="#">Home <span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Link</a>
</li>
<li className="nav-item">
<a className="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
</div>
</nav>
);
}
}
export default Navbar;
Below is the index.js. This is where I think the problem is.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import HeaderName from './components/header';
import FooterName from './components/footer';
import DescripTitle from './components/descrip';
import DescripName from './components/intro';
//create a new componet. Produce some HTML.
// const App = function() {
// return <div>Welcome to Aquastars Website</div>;
// }
const App = () => {
return (
<div>
<Router>
<div>
<Route exact path="/" component={HeaderName}/>
</div>
</Router>
<HeaderName />
<DescripName />
<DescripTitle />
<FooterName />
</div>
);
}
//Take this component's generated HTML and put it
// on the page(in the DOM).
ReactDOM.render(<App />, document.querySelector('.container'));
Any help would be appreciated.
Make sure you are not rendering the Navbar component in your app.js/index.js or wherever the headerName component is being imported. Can you post these files as well? What is inside the Navbar component. More information is needed.
-EDIT-
You can make a new Component that brings In any page contents into a "page component" then in your render:
<HeaderName/>
<Router exact path="/somedirectory" component{yourPageComponent}/>
<FooterName/>
This will make sure that header and footer are rendered on every page but only certain content is rendered on route changes. Cheers.