Collapse mobile menu on single page website with anchor links

Unless I’m missing something, it seems like Bootstrap still does not recognize the need to collapse the mobile menu when you have a one page website where the menu items link to anchor links on the same page.

This issue was brought up and addressed in this forum post way back in 2018 where @saj provided an effective (though somewhat laborious) solution involving some JavaScript and the adding of data attributes to all the anchors and links.

I’ve noted that the BSS New Age template is a one-page site with anchor links and it contains the below JavaScript that handles the collapsing of the mobile menu when clicking a menu link, and doesn’t require adding any data attributes.

(function() {
  "use strict"; // Start of use strict

  var mainNav = document.querySelector('#mainNav');

  if (mainNav) {

    var navbarCollapse = mainNav.querySelector('.navbar-collapse');
    
    if (navbarCollapse) {
      
      var collapse = new bootstrap.Collapse(navbarCollapse, {
        toggle: false
      });
      
      var navbarItems = navbarCollapse.querySelectorAll('a');
      
      // Closes responsive menu when a scroll trigger link is clicked
      for (var item of navbarItems) {
        item.addEventListener('click', function (event) {
          collapse.hide();
        });
      }
    }

    // Collapse Navbar
    var collapseNavbar = function() {

      var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

      if (scrollTop > 100) {
        mainNav.classList.add("navbar-shrink");
      } else {
        mainNav.classList.remove("navbar-shrink");
      }
    };
    // Collapse now if page is not at top
    collapseNavbar();
    // Collapse the navbar when page is scrolled
    document.addEventListener("scroll", collapseNavbar);
  }

})(); // End of use strict

The problem though is that this script won’t work on a site built from scratch because the New Age template apparently references a class (.navbar-shrink) that is not present in the standard bootstrap.min.css file (I presume it uses a custom bootstrap.min.css). While I could probably dig through the css and extract the .navbar-shrink class, I’m thinking there must be an easier way.

I found the following JQuery solution online which works perfectly with a mere three lines of code, but of course it requires loading JQuery, which is obviously undesirable in a Bootstrap 5.x site.

$('.navbar-nav>li>a').on('click', function(){
$('.navbar-collapse').collapse('hide');
});

I’m a total JS novice, so I’m wondering if anybody with JS experience would know of a way to do what this JQuery script is doing using plain JavaScript?

UPDATE: I found the following JS on StackOverflow. It seems to do the job.

const navLinks = document.querySelectorAll('.nav-item')
const menuToggle = document.getElementById('navcol-1')
const bsCollapse = new bootstrap.Collapse(menuToggle, {
  toggle: false
})
navLinks.forEach((l) => {
    l.addEventListener('click', () => {
        if(bsCollapse._isShown()){
            bsCollapse.hide()
        }
    })
})

Post has been removed.

If you want smooth scroll to the anchors that not scroll under the navbar.
You can do it like this.
In the dropdown menu is a link to the javascript used in the example

1 Like

I opted to use your code. It works perfectly. THANK YOU! :raised_hands:

I was wondering two things…

Isn’t it redundant using JS to incorporate smooth scrolling when Bootstrap already has scroll-behavior: smooth; in the CSS?

Also, why do you think Bootstrap doesn’t incorporate something like this in their base JS code? Single page websites that use anchor links have been popular for some time now, and I’ve seen people complaining about this problem for well over 5 years. It seems like Bootstrap wants people to figure out how to do it using data attributes (data-bs-toggle="collapse") but they don’t explain it very clearly in their docs.

It seems to me that incorporating a JS solution like yours into the bootstrap.min.js file would make perfect sense.

One other question… your JS solution does not seem to be compatible with the scroll-margin rule. I presume this is because it tracks the height of the navbar and uses that as the distance to which it scrolls the page. Any thoughts?

The data attribute data-bs-toggle="collapse" is the trigger for the mobile menu and the data-bs-target="#THE-ID-OF-THE-MOBILE-MENU" is the menu. You set that in the html then no need for people to write javascript to trigger the mobile menu.

In the example the script calculate the height of the navbar,mobilemenu and set that as offset for the anchor element when it scroll. There is no need for a css-rule scroll-margin. If you have a css-rule scroll-margin-top:80px; then that rule will be added to the offset where to scroll the page.

I have another example how to do it if you have an offcanvas menu