30

So, I have this single page that consists of a few sections. Users can go to these sections by scrolling themselves or clicking in the navbar (a href with anchor). Due to the Bootstrap 4 navbar being fixed to the top, the content gets placed under it. Is there a way I could offset anchors by -54px, so that whenever I click on an anchor link, it would show the content below the navbar (X:54px) and not under the navbar (X:0px).

Made this codepen to show the problem I'm facing:
https://codepen.io/anon/pen/XEjaKv
Whenever you click an anchor link, it will take you to the section, however, the navbar is covering the text..

All sections are 100 viewheight.

SCSS used:

.container{
  section{
    height: 100vh;
    &#section1{
      margin-top: 54px; // we need to offset the first section by 54px because of the navbar..
    }
    &#section1, &#section3{
      background-color: #ddd;
    }
    &#section2, &#section4{
      background-color:#ccc;
    }
  }
}
html{
  scroll-behavior:smooth;
}
Kevin
  • 929
  • 1
  • 8
  • 13

6 Answers6

20

There are a few different ways to solve it, but I think the best way is to put a hidden pseudo element ::before each section. This is a CSS only solution, no JS or jQuery...

section:before {
    height: 54px;
    content: "";
    display:block;
}

https://www.codeply.com/go/J7ryJWF5fr

That will put the space needed to account for the fixed-top Navbar. You'll also want to remove the margin-top offset for #section1 since this method will work consistently for all sections and allow the scrollspy to work.


Related
How do I add a data-offset to a Bootstrap 4 fixed-top responsive navbar?
Href Jump with Bootstrap Sticky Navbar

Carol Skelly
  • 351,302
  • 90
  • 710
  • 624
  • 13
    It adds space so i.e. when you have colored section, you get unwanted mass of color on the start of the blocks – Fanky Jan 08 '19 at 15:45
18

you can use jQuery to override the default behavior so you don't have to change the layout ( margin, padding .. etc.)

  var divId;

  $('.nav-link').click(function(){    
    divId = $(this).attr('href');
     $('html, body').animate({
      scrollTop: $(divId).offset().top - 54
    }, 100);
  });

https://codepen.io/anon/pen/NYRvaL

Taki
  • 17,320
  • 4
  • 26
  • 47
  • 1
    This works great, but not out of the box if you used slim build. See https://stackoverflow.com/questions/38647190/jquery-animate-is-not-a-function – Dr. Paul Kenneth Shreeman Sep 10 '18 at 21:39
  • 1
    This should be considered the best answer since the CSS approach can potentially change the HTML layout on page load. – Aday Jul 24 '19 at 15:54
  • To work for links outside of the navbar, `.nav-link` should be changed to whatever class the links one is using belong to; for example, if you have links to anchors in the body, add `class=my-link` to these links, and change the function accordingly. – Jonathan Y. Jul 23 '20 at 13:10
  • @Taki, can this approach be modified to work with links from one page to an anchor on a different page? – Jonathan Y. Jul 23 '20 at 13:24
17

This elegant CSS 1-liner solution worked nicely for me:

html { scroll-padding-top: 125px; }

My navbar height is 125px

  • 1
    This is the best answer I've found, works great for BootStrap 5.x. Then can add `data-bs-offset="125"` as needed to ensure scrollspy works correctly – Chris Nov 22 '21 at 20:13
10

This solution adds a padding and removes the gap by a negative margin at the top. This worked best for me.

section {
    padding-top: 56px;
    margin-top: -56px;
}

.offset {
    height: 54px;
}

/* START SNIPPET */
section {
    padding-top: 56px;
    margin-top: -56px;
}
/* END SNIPPET */

#section1 p, #section3 p {
    background-color: #ddd;
}
#section2 p, #section4 p {
    background-color: #ccc;
}
p {
    min-height: 15rem;
}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

  </head>
  <body>
 <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
      <a class="navbar-brand" href="#">Navbar</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarsExampleDefault">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item">
            <a class="nav-link" href="#section1">Section 1</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#section2">Section 2</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#section3">Section 3</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#section4">Section 4</a>
          </li>
        </ul>
      </div>
    </nav>
 
  <div class="container">
    <div class="offset col-12"></div>
    <section id="section1" class="row">
      <p class="col-12">
        Section 1
      </p>
    </section>
    <section id="section2" class="row">
      <p class="col-12">
        Section 2
      </p>
    </section>
    <section id="section3" class="row">
      <p class="col-12">
        Section 3
      </p>
    </section>
    <section id="section4" class="row">
      <p class="col-12">
        Section 4
      </p>
    </section>
  </div>
 
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
roland
  • 900
  • 12
  • 25
-1

I like to dynamically offset the height header (or navbar) like this:

$('.nav-link').click(function(e){
    let divCoords = $(e.target.hash).offset();
    let height = $('header').height();
    e.preventDefault();
    window.scrollTo({
    left: divCoords.left, 
    top: divCoords.top - height,
    behavior: 'smooth'
    });
});
Jacob Shore
  • 115
  • 9
-3

Usually when using a sticky header like that you’ll want to find the height of your navbar and offset the entire view accordingly. Something like

body {
  margin-top:2rem;
}

Your other option would be to use JavaScript.

scramlo
  • 105
  • 6