Search code examples
javascriptjquerycssreactjssass

React Scroll bug. Worked on pure HTML CSS JS, but it doesn't work on React


I found a minor bug which is weird. The main point is I tried to click my .nav li and I expected it would close the navbar. If it's on pure HTML CSS and JS it will work, but if it's on React it doesn't work. Here's my React:

import React from 'react';
import Nav from './javascripts/Navigation';
import Ham from './javascripts/Hamburger';
import { Link } from 'react-scroll';
import '../styles/Navbar.scss';

const Navbar = () => {
    React.useEffect(() => {
        Nav();
        Ham();
    }, []);
    return (
        <nav className="nav-container">
                <ul className="nav">
                    <div id="nav-icon">
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                    <li>
                        <Link
                            to="container"
                            smooth={true}
                            duration={500}
                            offset={-107}
                        >
                            Home
                        </Link>
                    </li>
                    <li>
                        <Link
                            to="abt-container"
                            smooth={true}
                            duration={500}
                            offset={-107}
                        >
                            About
                        </Link>
                    </li>
                </ul>
        </nav>
    );
};
export default Navbar;

SCSS

$w: #fff;
%nav {
    position: sticky;
    display: flex;
    padding: 15px 30px;
}
%slide {
    position: absolute;
    height: .4rem;
    box-shadow: 1px 1px 0 #666;
    transition: .5s cubic-bezier(.25, 1, .3, 1.05);
    transform: skew(-20deg);
    bottom: 1px;
    height: 100%;
}
.nav-container {
    @extend %nav;
    top: 0;
    justify-content: center;
    background: linear-gradient(110deg, #333 50%, #444 50%);
    z-index: 1;
    .nav {
        @extend %nav;
        list-style: none;
        @media (max-width: 800px) {
            li {
                display: none;
            }
        }
        a {
            position: relative;
            padding: .6em 2em;
            font-size: 20px;
            color: $w;
            display: inline-block;
            text-decoration: none;
            text-shadow: 1px 1px 0 #888;
            z-index: 3;
        }
        .slide1 {
            @extend %slide;
            background-color: #eeeeee30;
            z-index: 2;
        }
        .slide2 {
            @extend %slide;
            opacity: 0;
            background-color: transparent;
            border: 1px solid #fff;
            z-index: 1;
        }
        #nav-icon {
            display: flex;
            justify-content: center;
            width: 50px;
            height: 43px;
            margin: 2px auto;
            transform: rotate(0deg);
            span {
                display: block;
                position: absolute;
                height: 9px;
                width: 50%;
                background: $w;
                opacity: 1;
                transform: rotate(0deg);
                transition: .25s ease-in-out;
                border-radius: 9px;
                &:nth-child(even) {
                    left: 50%;
                    border-radius: 0 9px 9px 0;
                }
                &:nth-child(odd) {
                    left: 0px;
                    border-radius: 9px 0 0 9px;
                }
                &:nth-child(1),
                &:nth-child(2) {
                    top: 0px;
                }
                &:nth-child(3),
                &:nth-child(4) {
                    top: 18px;
                }
                &:nth-child(5),
                &:nth-child(6) {
                    top: 36px;
                }
            }
        }
        #nav-icon.open {
            span {
                &:nth-child(1),
                &:nth-child(6) {
                    transform: rotate(45deg);
                }
                &:nth-child(2),
                &:nth-child(5) {
                    transform: rotate(-45deg);
                }
                &:nth-child(1) {
                    left: 5px;
                    top: 10px;
                }
                &:nth-child(2) {
                    left: calc(50% - 5px);
                    top: 10px;
                }
                &:nth-child(3) {
                    left: -50%;
                    opacity: 0;
                }
                &:nth-child(4) {
                    left: 100%;
                    opacity: 0;
                }
                &:nth-child(5) {
                    left: 5px;
                    top: 24px;
                }
                &:nth-child(6) {
                    left: calc(50% - 5px);
                    top: 24px;
                }
            }
        }
    }
}
li {
    display: none;
}

jQuery

import $ from 'jquery';

const Hamburger = () => {
    let isOpen;
    $(document).on("click", "#nav-icon", function () {
        $(this).toggleClass("open");
        isOpen = !isOpen;
        if (isOpen) {
            $(".nav").css({ display: "block", padding: "15px 0 0 0" });
            $("#nav-icon").css({ paddingBottom: "20px" });
            $("li").css({ display: "block", textAlign: "center" });
        } else {
            $(".nav").css({ padding: "15px 30px" });
            $("#nav-icon").css({ paddingBottom: "0" });
            $("li").css({ display: "none" });
        }
    });
    $(document).on("click", ".nav li", function () {
        if (isOpen) {
            isOpen = false;
            $("#nav-icon").removeClass("open");
            $(".nav").css({ padding: "15px 30px" });
            $("#nav-icon").css({ paddingBottom: "0" });
            $("li").css({ display: "none" });
        }
    });
}
export default Hamburger;

I also tried this snippet which worked very well, but when I put it on React it didn't work as it should despite both codes being interpreted in the same way.

let isOpen;
    $(document).on("click", "#nav-icon", function () {
        $(this).toggleClass("open");
        isOpen = !isOpen;
        if (isOpen) {
            $(".nav").css({ display: "block", padding: "15px 0 0 0" });
            $("#nav-icon").css({ paddingBottom: "20px" });
            $("li").css({ display: "block", textAlign: "center" });
        } else {
            $(".nav").css({ padding: "15px 30px" });
            $("#nav-icon").css({ paddingBottom: "0" });
            $("li").css({ display: "none" });
        }
    });
    $(document).on("click", ".nav li", function () {
        if (isOpen) {
            isOpen = false;
            $("#nav-icon").removeClass("open");
            $(".nav").css({ padding: "15px 30px" });
            $("#nav-icon").css({ paddingBottom: "0" });
            $("li").css({ display: "none" });
        }
    });
.nav-container .nav, .nav-container {
  position: sticky;
  display: flex;
  padding: 15px 30px;
}

.nav-container .nav .slide2, .nav-container .nav .slide1 {
  position: absolute;
  height: 0.4rem;
  box-shadow: 1px 1px 0 #666;
  transition: 0.5s cubic-bezier(0.25, 1, 0.3, 1.05);
  transform: skew(-20deg);
  bottom: 1px;
  height: 100%;
}

.nav-container {
  top: 0;
  justify-content: center;
  background: linear-gradient(110deg, #333 50%, #444 50%);
  z-index: 1;
}
.nav-container .nav {
  list-style: none;
}
@media (max-width: 800px) {
  .nav-container .nav li {
    display: none;
  }
}
li {
  display: none;
}
.nav-container .nav a {
  position: relative;
  padding: 0.6em 2em;
  font-size: 20px;
  color: #fff;
  display: inline-block;
  text-decoration: none;
  text-shadow: 1px 1px 0 #888;
  z-index: 3;
}
.nav-container .nav .slide1 {
  background-color: rgba(238, 238, 238, 0.1882352941);
  z-index: 2;
}
.nav-container .nav .slide2 {
  opacity: 0;
  background-color: transparent;
  border: 1px solid #fff;
  z-index: 1;
}
.nav-container .nav #nav-icon {
  display: flex;
  justify-content: center;
  width: 50px;
  height: 43px;
  margin: 2px auto;
  transform: rotate(0deg);
}
.nav-container .nav #nav-icon span {
  display: block;
  position: absolute;
  height: 9px;
  width: 50%;
  background: #fff;
  opacity: 1;
  transform: rotate(0deg);
  transition: 0.25s ease-in-out;
  border-radius: 9px;
}
.nav-container .nav #nav-icon span:nth-child(even) {
  left: 50%;
  border-radius: 0 9px 9px 0;
}
.nav-container .nav #nav-icon span:nth-child(odd) {
  left: 0px;
  border-radius: 9px 0 0 9px;
}
.nav-container .nav #nav-icon span:nth-child(1), .nav-container .nav #nav-icon span:nth-child(2) {
  top: 0px;
}
.nav-container .nav #nav-icon span:nth-child(3), .nav-container .nav #nav-icon span:nth-child(4) {
  top: 18px;
}
.nav-container .nav #nav-icon span:nth-child(5), .nav-container .nav #nav-icon span:nth-child(6) {
  top: 36px;
}
.nav-container .nav #nav-icon.open span:nth-child(1), .nav-container .nav #nav-icon.open span:nth-child(6) {
  transform: rotate(45deg);
}
.nav-container .nav #nav-icon.open span:nth-child(2), .nav-container .nav #nav-icon.open span:nth-child(5) {
  transform: rotate(-45deg);
}
.nav-container .nav #nav-icon.open span:nth-child(1) {
  left: 5px;
  top: 10px;
}
.nav-container .nav #nav-icon.open span:nth-child(2) {
  left: calc(50% - 5px);
  top: 10px;
}
.nav-container .nav #nav-icon.open span:nth-child(3) {
  left: -50%;
  opacity: 0;
}
.nav-container .nav #nav-icon.open span:nth-child(4) {
  left: 100%;
  opacity: 0;
}
.nav-container .nav #nav-icon.open span:nth-child(5) {
  left: 5px;
  top: 24px;
}
.nav-container .nav #nav-icon.open span:nth-child(6) {
  left: calc(50% - 5px);
  top: 24px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav class="nav-container">
                <ul class="nav">
                    <div id="nav-icon">
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                    <li>
                        <a>
                            Home
                        </a>
                    </li>
                    <li>
                        <a>
                            About
                        </a>
                    </li>
                </ul>
        </nav>


Solution

  • It's not a React bug. The reason of this behaviour lies in the "Link" implementation. When "Link" is handling click event, it stops event propagation. Therefore, your handler is not triggered.

    To fix that, you can pass your own click handler as "onClick" property to the "Link" component. This click handler can be written with jQuery as well, so you can use the existing code

    The recipe:

    1. Move the existing jQuery click handler into a separate function
    
    import $ from "jquery";
    
    export function closeNavbar() {
     $("#nav-icon").removeClass("open");
     $(".nav").css({ padding: "15px 30px" });
     $("#nav-icon").css({ paddingBottom: "0" });
     $("li").css({ display: "none" });
    }
    
    1. Pass this function as "onClick" property to the "Link" component
        <nav className="nav-container">
          <ul className="nav">
            <div id="nav-icon">
              ...
            </div>
            <li>
              <Link
                onClick={() => closeNavbar()}
                ...
              >
                Home
              </Link>
            </li>
          </ul>
        </nav>