React - Shrink nav image on scroll down of the page - javascript

I currently have my nav set up how i'd like, I would just like the logo image to shrink from 91px to 60px when the user scrolls down the page, and then grow back to 91px when they're at the top. I've seen some questions online but none of them seem to work or be specific enough for the solution I want. Any help would be really appreciated. Updated my code with my attempt from this codesandpit from this question - Resize Header onscroll React
When I scroll down I get this error -
TypeError: headerEl is null
import React, { Component } from "react";
import logo from "../images/sdrlogo.jpeg";
import { FaAlignRight } from "react-icons/fa";
import { Link } from "react-router-dom";
export default class Navbar extends Component {
state = {
isOpen: false,
}
handleToggle = () => {
this.setState({ isOpen: !this.state.isOpen });
}
componentDidMount() {
window.addEventListener("scroll", this.resizeHeaderOnScroll);
}
resizeHeaderOnScroll() {
const distanceY = window.pageYOffset || document.documentElement.scrollTop,
shrinkOn = 200,
headerEl = document.getElementById("js-header");
if (distanceY > shrinkOn) {
headerEl.classList.add("smaller");
} else {
headerEl.classList.remove("smaller");
}
}
render() {
return <nav className="navbar">
<div class="nav-center">
<div class="nav-header">
<Link to="/">
<img className="logo" src={logo} alt="Derby Golf Centre" />
</Link>
<button type="button" className="nav-btn" onClick={this.handleToggle}>
<FaAlignRight className="nav-icon" />
</button>
</div>
<ul className={this.state.isOpen ? "nav-links show-nav" : "nav-links"}>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/lesson">Book a Lesson</Link>
</li>
<li>
<Link to="/opening-times">Opening Times</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
<li>
<Link to="/driving-range">Driving Range</Link>
</li>
</ul>
</div>
</nav>;
}
}

So, I took some time and went through the code. I missed the CSS file on codesandbox, I've amended my code and using getElementbyID, I change the logo from a class to an ID and then used that. In the CSS I created a class called logoShrink and added transitions to both.
.logoShrink {
height: 60px !important;
transition: 0.5s;
}
if anyone is bothered, code is below!
import React, { Component } from "react";
import logo from "../images/sdrlogo.jpeg";
import { FaAlignRight } from "react-icons/fa";
import { Link } from "react-router-dom";
export default class Navbar extends Component {
state = {
isOpen: false,
}
handleToggle = () => {
this.setState({ isOpen: !this.state.isOpen });
}
componentDidMount() {
window.addEventListener("scroll", this.resizeHeaderOnScroll);
}
resizeHeaderOnScroll() {
const distanceY = window.pageYOffset || document.documentElement.scrollTop,
shrinkOn = 100,
headerEl = document.getElementById("logo");
if (distanceY > shrinkOn) {
headerEl.classList.add("logoShrink");
} else {
headerEl.classList.remove("logoShrink");
}
}
render() {
return <nav className="navbar">
<div class="nav-center">
<div class="nav-header">
<Link to="/">
<img id="logo" src={logo} alt="Derby Golf Centre" />
</Link>
<button type="button" className="nav-btn" onClick={this.handleToggle}>
<FaAlignRight className="nav-icon" />
</button>
</div>
<ul className={this.state.isOpen ? "nav-links show-nav" : "nav-links"}>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/lesson">Book a Lesson</Link>
</li>
<li>
<Link to="/opening-times">Opening Times</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
<li>
<Link to="/driving-range">Driving Range</Link>
</li>
</ul>
</div>
</nav>;
}
}

I have been scouring the Internet to achieve this effect, but the closest I got was this very helpful blog article by Phuc. Attaching the link with this answer, see if it helps you.
The outcome of following his code would be —
Slight shrinkage of the Navbar
Reduction of opacity of the background of the Navbar
Blurring of the background of the section of the page that gets behind the Navbar
It is a neat effect which can be used (with a few changes) to suit your exact purpose.

Related

rewriting javascript event listener and toggling of classlist in reactjs

Please help, I have been trying to write the following code in react syntax but I don't seem to know how to do that and all search I've made online isn't helping
useEffect(() => {
const menuBtn = document.getElementById('menu-btn');
const menu = document.querySelector('.menu')
menuBtn.addEventListener('click', () =>{
menu.classList.toggle('menu-open')
})
}, []);
useEffect(()=>{
const navbar = document.getElementById('navbar')
const offset = 50
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
I want to change it because I keep getting the error below
[webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|/home/kimmoramicky/Desktop/fts_portfolio/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[2].oneOf[7].use[1]!/home/kimmoramicky/Desktop/fts_portfolio/node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[2].oneOf[7].use[2]!/home/kimmoramicky/Desktop/fts_portfolio/node_modules/bootstrap/dist/css/bootstrap.min.css': No serializer registered for Warning
while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> Array { 1 items } -> webpack/lib/ModuleWarning -> Warning
but when I comment that part out, I don't get the error. Also, the code doesn't work as expected when the page is loaded again.
Below is the complete code for clearer understanding
import React, {useState, useEffect, useRef} from 'react'
import Link from 'next/link'
import { getPosts, getTags, getCategories } from '../services';
import { useRouter } from 'next/router';
import 'react-ionicons'
import 'react-ionicons'
import { FaFacebookSquare, FaGithubSquare, FaInstagramSquare, FaYoutubeSquare } from 'react-icons/fa'
import {ImMenu} from 'react-icons/im'
import {BiSearchAlt} from 'react-icons/bi'
import Button from 'react-bootstrap/Button';
import Container from 'react-bootstrap/Container';
import Form from 'react-bootstrap/Form';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import { filter } from 'domutils';
const Header = () => {
useEffect(() => {
const menuBtn = document.getElementById('menu-btn');
const menu = document.querySelector('.menu')
menuBtn.addEventListener('click', () =>{
menu.classList.toggle('menu-open')
})
}, []);
useEffect(()=>{
const navbar = document.getElementById('navbar')
const offset = 50
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
return (
<div className='header1'>
<video id='headerVideo' loop={true} autoplay="autoplay" muted>
<source src="/vidfy-african-cheerful-young-stylish-man-and-w_1920x1080.mp4" type="video/mp4"></source>
Your browser does not support the video tag.
</video>
<div>
<nav id='navbar'>
<div className="menu">
<div>
<a href="#" className='brand'>Menu</a>
<ul>
<li>Blog</li>
<li>What's New</li>
<li>Categories</li>
</ul>
<ul>
<li>Projects</li>
<li>News</li>
<li>Store</li>
<li>About</li>
<li>Contact</li>
</ul>
<ul className='social-media'>
<li><a href="#">
<FaFacebookSquare />
</a></li>
<li><a href="#">
<FaInstagramSquare />
</a></li>
<li><a href="#">
<FaGithubSquare />
</a></li>
<li><a href="#">
<FaYoutubeSquare />
</a></li>
</ul>
<form action="">
<div className="input-wrap">
<input type="search" placeholder='Search...' />
<button type='submit'>
<BiSearchAlt />
</button>
</div>
</form>
</div>
</div>
<div className="container">
<img src="/logo.png" alt="logo" />
<div className="container-inner">
<ul>
<li>Home</li>
<li>Home</li>
<li>Home</li>
</ul>
<form action="">
<div className="input-wrap">
<input type="search" placeholder='Search...' className='text-is-white' />
<button type='submit'>
<BiSearchAlt />
</button>
</div>
</form>
</div>
<ImMenu className='fa-bars' id='menu-btn' />
</div>
</nav>
</div>
<div className="headText">
<p>Grow your business and ideas with KimmoTech</p>
</div>
</div>
)
}
export default Header
so you have a bunch of stuff you should not do that way:
First:
const menuBtn = document.getElementById('menu-btn');
Should be:
export const Component = () => {
const [menuOpen, setMenuOpen = useState(false)
const handleMenuButtonClick = () => setMenuOpen(!menuOpen)
return (
…
<div className={‘menu ${menuOpen ? ‘menu-open’ : ‘menu-closed’}’}/>
<button onClick={handleMenuButtonClick} />
…
)
Then this:
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
This is better to use as hook and when you do that you have two things you should have in mind, first one this should be DEBOUNCED otherwise you’l have quite nasty perf hit, second when you subscribe to event like that you have to also unsubscribe:
cons [scrollActive, setScrollActive] = useState(false)
useEffect(() => {
const scroll = window.addEventListener(‘scroll’, (e) => setScrollActive(e > condition));
return () => winodw.removeEventListener(scroll)
}, [])
…
<navbar classNsme={‘navbar ${scrollActive ? ‘scroll-active’ : ‘’}}/>
They would look more like react then it is right now…
The issue occurred because you used webpack on react app.
It'll be disappeared if you create the app with "create-react-app".
Hope it'll be helpful for you.
I would recommend to switch from useEffect to using useState to manage the state of your components including classes. Also React provides an easy and smooth way to manage your event handlers such as onClick.
Here is an example of how I would approach your problem, it's not going to be your code exactly but hopefully you can replicate the approach to work with you code:
import React, { useState } from 'react';
const Example = () => {
const [classes, setClasses] = useState('class-1');
const handleToggleClasses = () => {
setClasses(classes === 'class-1' ? 'class-2' : 'class-1')
}
return (
<>
<div className={classes}>
my classes will change when the button is clicked
</div>
<button onClick={handleToggleClasses}>
click me to toggle classes
</button>
</>
)
}
This button in this code has a an onClick handler which is equivalent to assigning it an event listener using vanilla JS like you did in your useEffect and I pass to it the handleToggleClasses function which just toggles between the classes using useState to keep track of that. It's cleaner, more performant and concise.
you can read more about handling events here
I hope that helps!

How can I use onClick

On the small screen, it doesn't show/hide. How can I change this code to work on the small screen?
import React from 'react';
import assets from './assets';
import { FaBars, FaFacebook, FaRegHeart, FaInstagram, FaLinkedin, FaStar, FaStarHalf, FaTimes, FaTwitter } from 'react-icons/fa';
var navLinks = document.getElementById('navLinks');
function showMenu() {
navLinks.style.right = "0";
}
function hideMenu() {
navLinks.style.right = "-200px";
}
Above var worked a few times mistakenly, but I still did not get it.
export const Home = () => (
<>
<section class="header">
<nav>
<img src={assets.logo} alt='Logo' width='100%' />
<div id='navLinks' class='nav-links'>
<FaTimes class='fa' onClick={showMenu} />
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>COURSE</li>
<li>BLOG</li>
<li>CONTACT</li>
</ul>
</div>
<FaBars class='fa' onClick={hideMenu} />
</nav>
<div class="text-box">
<h1>World's Biggest University</h1>
<p>Making website is now one of the easiest thing in the world. You just need to learn HTML, CSS,<br />Javascript and you are good to go.</p>
Visit Us to Know More
</div>
</section>
</>
)
My CSS file is below.
I've tried many ways but I couldn't.
nav .fa{
display: none;
}
As some people have mentioned before you are not utilising the React library correctly.
The (untested) code below should suit you better. As you can see a state has been used instead of directly changing style properties. I have tried to comment the code as clearly as possible but for more in depth instructions i recommend reading the React docs.
import React, { useState } from 'react';
import assets from './assets';
import { FaBars, FaFacebook, FaRegHeart, FaInstagram, FaLinkedin, FaStar, FaStarHalf, FaTimes, FaTwitter } from 'react-icons/fa';
export const Home = () => {
// Set state value to keep track of menu open or closed status
const [navOpen, setNavOpen] = useState(false)
// Open the active menu
const open = () => {
setNavOpen(true)
}
// Close the active menu
const close = () => {
setNavOpen(false)
}
return (
<>
<section class="header">
<nav>
<img src={assets.logo} alt='Logo' width='100%' />
<div id='navLinks' class='nav-links'>
<button onClick={open}>
<FaTimes class='fa' />
</button>
{
// Only render the UL node when the navOpen variable is true
navOpen && (
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>COURSE</li>
<li>BLOG</li>
<li>CONTACT</li>
</ul>
)
}
</div>
<button onClick={close}>
<FaBars class='fa' />
</button>
</nav>
<div class="text-box">
<h1>World's Biggest University</h1>
<p>Making website is now one of the easiest thing in the world. You just need to learn HTML, CSS,<br />Javascript and you are good to go.</p>
Visit Us to Know More
</div>
</section>
</>
)
}

ReactJs TypeError: Cannot read property 'clicked' of undefined

I am getting this error while I use state.clicked statement inside if-else condition.
Below is my code,
Hamburger.js
import React, { useEffect, useRef } from "react";
import { Link } from "react-router-dom";
const Hamburger = ({ state }) => {
let menu = useRef(null);
useEffect(() => {
if (state.clicked === false) {
//close our menu
menu.style.display = "none";
} else if (
state.clicked === true ||
(state.clicked === true && state.initial === null)
) {
// open our menu
menu.style.display = "block";
}
});
return (
<div ref={(el) => (menu = el)} className="hamburger-menu">
<div className="menu-secondary-background-color"></div>
<div className="menu-layer">
<div className="container">
<div className="wrapper">
<div className="menu-links">
<nav>
<ul>
<li>
<Link to="/opportunities">Opportunities</Link>
</li>
<li>
<Link to="/opportunities">Work</Link>
</li>
<li>
<Link to="/opportunities">Contact</Link>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
);
};
export default Hamburger;
Header.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Hamburger from "./Hamburger";
const Header = () => {
const [state, setState] = useState({
initial: false,
clicked: null,
menuName: "Menu",
});
const [disabled, setDisabled] = useState(false);
const handleMenu = () => {
disableMenu();
if (state.initial === false) {
setState({
inital: null,
clicked: true,
menuName: "Close",
});
} else if (state.clicked === true) {
setState({
clicked: !state.clicked,
menuName: "Menu",
});
} else if (state.clicked === false) {
setState({
clicked: !state.clicked,
menuName: "Close",
});
}
};
// Determine if our menu button should be disabled
const disableMenu = () => {
setDisabled(!disabled);
setTimeout(() => {
setDisabled(false);
}, 1200);
};
return (
<header>
<div className="container">
<div className="wrapper">
<div className="inner-header">
<div className="logo">
<Link to="/">Adarsh Goldar</Link>
</div>
<div className="menu">
<Link to="/">Home</Link>
<Link to="/">About</Link>
<button disabled={disabled} onClick={handleMenu}>
Menu
</button>
</div>
</div>
</div>
</div>
<Hamburger />
</header>
);
};
export default Header;
Yes because you have not passed state as a prop to do that change your code as below
in header.js
<Hamburger state={state}/>
well you need to pass state props to Your component:
<Hamburger state={state}/>
and for Hamburger you dont need to use useEffect and ref to update the style because when state props change the component will re render and every code will run again.
here how to do it with jsx style:
const Hamburger = ({ state }) => {
return (
<div style={{display:state.clicked?"block":"none"}} className="hamburger-menu">
<div className="menu-secondary-background-color"></div>
<div className="menu-layer">
<div className="container">
<div className="wrapper">
<div className="menu-links">
<nav>
<ul>
<li>
<Link to="/opportunities">Opportunities</Link>
</li>
<li>
<Link to="/opportunities">Work</Link>
</li>
<li>
<Link to="/opportunities">Contact</Link>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
);
};
export default Hamburger;
and you can wrap Hamburger component in React.memo to prevent unnecessary re render so it will re render only when state props change : export default React.memo(Hamburger)`

Converting CSS and HTML hamburger menu into a react component

I am following a tutorial that creates an animated navbar and hamburger manu using css, html and javascript, and I want to create the navbar as a react component for my project.
The code that I am trying to covert is the following, which is an app.js file which is embedded in the html with tags:
const navSlide = () => {
const burger = document.querySelector('.burger');
const nav = document.querySelector('.nav-links');
burger.addEventListener('click', () => {
nav.classList.toggle('nav-active');
})
}
navSlide();
I have tried to implement this code into my navbar react component as follows:
import React from "react";
const NavBar = () => {
const nav = document.querySelector(".nav-links");
return (
<div className="navbar">
<nav>
<ul className="nav-links">
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => {
nav.classList.toggle("nav-active");
}}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
export default NavBar;
However this returns the error of:
"TypeError: Cannot read property 'classList' of null"
The CSS of .nav-active is as follows:
.nav-active {
transform: translateX(0%);
}
The result of this I am expecting is for the navbar to toggle open and closed when the burger symbol is clicked
The best option is to go with state. This is should work, have not tested though. ReactJS 16.8 is required for useState hook to work.
import React, {useState} from "react";
const NavBar = () => {
const [navOpened, setNavOpened] = useState(false);
const navClassNames = navOpened ? 'nav-links nav-active' : 'nav-links';
return (
<div className="navbar">
<nav>
<ul className={navClassNames}>
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => setNavOpened(!navOpened)}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
To refer to DOM elements you should be using ref, you can use a useRef hook docs.
import React, { useRef } from "react";
const NavBar = () => {
const nav = useRef(null); // initial value of ref null
return (
<div className="navbar">
<nav>
<ul className="nav-links" ref={nav}> {/* Put the ref in your JSX */}
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => {
nav.current.classList.toggle("nav-active"); // Now you can use the ref here, current is required to get the current value of the ref
}}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
export default NavBar;

Input field looses focus in React

I know that it is happening because React is re-rendering the page and hence the input field is losing the focus. I have tried everything I could think of but none worked! I can't find a way to resolve the issue.
Here's my React code.
import React, { ChangeEvent, Component, ReactNode } from 'react'
import { Root, /*Routes*/ } from 'react-static'
import { Link } from '#reach/router'
import { Helmet } from 'react-helmet'
import './app.css'
// import FancyDiv from '#components/FancyDiv'
/* According to the following documentation,
* https://github.com/nozzle/react-static/blob/master/docs/concepts.md#writing-universal-node-safe-code
*/
let Sidenav = { init(_: any, options: any) { return {_, options} } }
let updateTextFields = () => {}
if (typeof window !== 'undefined') {
const materialize = require('materialize-css')
Sidenav = materialize.Sidenav
updateTextFields = () => materialize.updateTextFields()
}
interface IComponentProps {
}
interface IComponentState {
'long_url': string
}
const LongUrlInput = (props: any) =>
<input
key='in1'
id="long_url"
type="text"
className="validate"
onChange={ props.handleLongUrl }
/>
export default class App extends Component<IComponentProps, IComponentState> {
constructor(props: any) {
super(props)
this.state = {
'long_url': ''
}
}
componentDidMount(): void {
const elem = document.querySelectorAll('.sidenav')
Sidenav.init(elem, {})
updateTextFields()
}
handleLongUrl = (event: ChangeEvent<HTMLInputElement>): void => {
console.log('types', event.target.value, this.state.long_url)
this.setState({
'long_url': event.target.value
})
}
render(): ReactNode {
return (
<Root>
<Helmet>
<title>ShortURI - URL Shortener</title>
<meta name="description" content="Create short URLs and also monitor traffic with proper analysis report." />
</Helmet>
<nav className={'indigo'}>
<div className="nav-wrapper" style={{ padding: '0 20px' }}>
<Link to={'/'} className={'brand-logo'}>ShortURI</Link>
<a href={''} data-target="slide-out" className="sidenav-trigger">
<i className="material-icons">menu</i>
</a>
<ul id="nav-mobile" className="right hide-on-med-and-down">
<li><Link to={'/about'}>About</Link></li>
<li><Link to={'/#!'}>Login</Link></li>
<li><Link to={'/#!'}>Register</Link></li>
</ul>
</div>
</nav>
<ul id="slide-out" className="sidenav">
<li><a className="sidenav-close" href="#">Clicking this will close Sidenav</a></li>
</ul>
<div className="content">
<div className="row">
<div className="card">
<div className="card-content black-text">
<span className="card-title">Card Title</span>
<div className="input-field col s6">
<LongUrlInput handleLongUrl={this.handleLongUrl}/>
<label className="active" htmlFor="long_url">Long URL</label>
</div>
</div>
</div>
</div>
{/*<FancyDiv>*/}
{/*<Routes/>*/}
{/*</FancyDiv>*/}
</div>
</Root>
)
}
}
PS: I am new to React. Any help would be appreciated.
Note: Typing some random words to suppress StackOverflow Warning - "Looks like your question is mostly code, blah-blah".
I changed <Root>...</Root> to <div>...</div> and somehow it is now working. Weird!

Categories