I'm trying to close the offcanvas menu in React Bootstrap when I click a link

Viewed 1373

I'm using a combination of React Bootstrap and React, to make a single page application, I've tried a few methods to get the Offcanvas menu to close when I click a link. I tried making an inline script on the link that toggles the menu, but what I found is the menu closes as I want it to but then the link only takes me halfway to where it should navigate to.

this is my code so far:

import React from "react";
import { Navbar, Nav, Container, Offcanvas } from "react-bootstrap";
import { StaticImage } from "gatsby-plugin-image";
import styled from "styled-components";
import { Link } from "gatsby";

const Wrapper = styled.div`
  background-color: #9ac2ba;
  .Nav-Brand {
    display: flex;
  }
  .navbar {
    background-color: #9ac2ba;
  }
`;

const LinkWrapper = styled.div`
  margin-top: 20px;
  font-size: 1.5rem;
  text-align: center;
  color: #333;
  .nav-link {
    color: #333;
  }
  .nav-link:hover {
    color: #000;
  }
`;

const Navigation = () => {
  return (
    <Wrapper>
      <Navbar
        as="nav"
        variant="light"
        fixed="top"
        expand={false}
        className="shadow"
      >
        <Container>
          <Navbar.Brand href="/" className="Nav-Brand">
            <StaticImage
              src="../images/Spf-Brand-01.jpg"
              alt="Brand Image"
              layout="constrained"
              placeholder="blurred"
              height={50}
              loading="eager"
            />
            <h1 className="visually-hidden">
              SPF Paint &amp; Decorating, Birmingham
            </h1>
          </Navbar.Brand>
          <Navbar.Toggle
            aria-controls="offcanvasNavbar"
            aria-labelledby="offcanvasNavbarLabel"
          />
          <Navbar.Offcanvas id="offcanvasNavbar" placement="start">
            <Offcanvas.Header closeButton>
              <Offcanvas.Title id="offcanvasNavbarLabel">
                <span className="visually-hidden">SPF Nav Menu</span>
              </Offcanvas.Title>
            </Offcanvas.Header>
            <Offcanvas.Body>
              <Nav className="justify-content-end flex-grow-1 pe-3">
                <StaticImage
                  src="../images/Spf-Brand-01.jpg"
                  alt="Brand Image"
                  layout="constrained"
                  placeholder="blurred"
                  height={50}
                  loading="eager"
                  className="offcanvas-brand"
                />
                <LinkWrapper>
                  <Link to="/" className="nav-link">
                    Home
                  </Link>
                  <Link to="/#services" className="nav-link">
                    Services
                  </Link>
                  <Link to="/#faq" className="nav-link">
                    FAQ
                  </Link>
                  <Link to="/#contact" className="nav-link">
                    Contact
                  </Link>
                </LinkWrapper>
              </Nav>
            </Offcanvas.Body>
          </Navbar.Offcanvas>
        </Container>
      </Navbar>
    </Wrapper>
  );
};

export default Navigation;

this is the build of the site: https://pland.netlify.app/

2 Answers

All your links are to content on the same page, so using Link isn't necessary. Instead, replace these with regular anchor tags <a href=''>Link</a>

Secondly, the default behaviour of Offcanvas is that when the overlay is closed the focus is returned to where it was when the overlay was opened. That's why the page isn't scrolling to the correct position when the overlay is closed. To change this, you can pass in restoreFocus={false} prop to Offcanvas. See Offcanvas API docs.

Thirdly, to get the menu to close when a link is clicked, you should track whether the menu is open in state and add a toggle function to the onClick prop of each link, as well as the Navbar.Toggle.

const Navigation = () => {
  const [menuOpen, setMenuOpen] = useState(false)
  const toggleMenu = () => {
    setMenuOpen(!menuOpen)
  }

  const handleClose = () => setMenuOpen(false)

  return(
   /** Omit code */
  
  <Navbar.Toggle
      aria-controls='offcanvasNavbar'
      aria-labelledby='offcanvasNavbarLabel'
      /** Add onClick toggle here */
      onClick={toggleMenu}
    />
    <Navbar.Offcanvas
      id='offcanvasNavbar'
      placement='start'
      /** Add these props */
      restoreFocus={false}
      show={menuOpen}
      onHide={handleClose}
    >
    
    /** omit code */

    <Nav className='justify-content-end flex-grow-1 pe-3'>
        <a href='#services' className='nav-link' onClick={toggleMenu}>
           Services
        </a>
        <a href='#faq' className='nav-link' onClick={toggleMenu}>
           FAQ
        </a>
        {/** more links */}
     </Nav>
    /** omit code */
 )
}
Related