codeuix
codeuix

Reputation: 1396

How to create a fixed/sticky header on scroll with Html and CSS only?

Here is my Html. I want to fix my nav item on top on scrolling using html and css I tried a lot of things but I was not able to fix it. How can I build it without JavaScript or jQuery?

<div class="contain">
  <p>
    Some text to enable scrolling.. Lorem ipsum dolor sit amet, illum definitiones 
    no quo, maluisset concludaturque et eum, altera fabulas ut quo. Atqui causae 
    gloriatur ius te, id agam omnis evertitur eum. Affert laboramus repudiandae nec 
    et. Inciderint efficiantur his ad. Eum no molestiae voluptatibus.
  </p>
</div>
<nav>
  <div class="navWide">
    <div class="wideDiv">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
  <div class="navNarrow">
    <i class="fa fa-bars fa-2x"></i>
    <div class="narrowLinks hidden">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
</nav>
<div class="contain">
      <p>
        Some text to enable scrolling.. Lorem ipsum dolor sit amet, illum definitiones 
        no quo, maluisset concludaturque et eum, altera fabulas ut quo. Atqui causae 
        gloriatur ius te, id agam omnis evertitur eum. Affert laboramus repudiandae nec 
        et. Inciderint efficiantur his ad. Eum no molestiae voluptatibus.
      </p>
    </div>

My custom CSS using this. How can I fix my problem? It will be really helpful for me and others too why I want to complete this using CSS. I am making a small component using html and CSS, this is one more component and I got stuck here.

.contain{
  height:100vh;
  backgroud:#ccc;
}

nav {
    background-color: #CCC;
    overflow: hidden;
    padding: 1em;
    border-bottom: 1px solid #000;
}

nav a {
        color: #000;
}

nav a:visited {
        color: #000;
}

nav .navWide {
    display: none;
    margin: 0 auto;
}

nav .navWide .wideDiv {
    text-align: center;
}

nav .navWide .wideDiv a {
    text-decoration: none;
    display: inline-block;
    padding: 0 2em;
}
    
nav .navNarrow i {
    float: left;
    cursor: pointer;
    color: #000;
}

nav .navNarrow .narrowLinks a {
    text-decoration: none;
    display: block;
    float: left;
    clear: left;
    padding: 0.5em 0;
}

.hidden {
  display: none;
}

/*Adjust breakpoint as desired to select when the "hamburger" menu is
replaced by just the links.*/
@media (min-width: 480px) {
    
    nav .navWide {
        display: block;
    }

    nav .navNarrow {
        display: none;
    }
}

Upvotes: 5

Views: 14995

Answers (3)

Gayane Kabalyan
Gayane Kabalyan

Reputation: 76

You could use position: sticky; on your nav, but note that it will not be supported on all browsers (for example IE 11). Here is the compatibility chart if you wanna take a look: https://caniuse.com/#feat=css-sticky

A safer fix is to set position: fixed; to you nav, and give your content some top padding.

So your css will be as follows:

body {
  margin: 0;
}

.contain{
  height:100vh;
  background:#ccc;
  padding-top: 50px;

}

nav {
    background-color: #CCC;
    overflow: hidden;
    padding: 1em;
    border-bottom: 1px solid #000;
    position: fixed;
    top: 0px;
    display: block;
    width: 100%;
}

nav a {
        color: #000;
}

nav a:visited {
        color: #000;
}

nav .navWide {
    display: none;
    margin: 0 auto;
}

nav .navWide .wideDiv {
    text-align: center;
}

nav .navWide .wideDiv a {
    text-decoration: none;
    display: inline-block;
    padding: 0 2em;
}

nav .navNarrow i {
    float: left;
    cursor: pointer;
    color: #000;
}

nav .navNarrow .narrowLinks a {
    text-decoration: none;
    display: block;
    float: left;
    clear: left;
    padding: 0.5em 0;
}

.hidden {
  display: none;
}

/*Adjust breakpoint as desired to select when the "hamburger" menu is
replaced by just the links.*/
@media (min-width: 480px) {

    nav .navWide {
        display: block;
    }

    nav .navNarrow {
        display: none;
    }
}

Upvotes: 4

Andu Andrici
Andu Andrici

Reputation: 853

As stated, the way to go is:

body {
  height: 100vh;
  overflow-y: auto
}

.nav {
  position: sticky;
  top: 20px;
}

As for 'not working in all browsers' that's a whole different story. If you really want to go through that, here's a good starting point:

You could use an onscroll eventListener to check the position of the nav, in relation to the screen, with getBoundingClientRect() - but it is much more expensive than the sticky implementation.

.makeItFixed {
  position: fixed;
  top: 0; left: 0;
};

var nav = document.getElementsByTgName('nav')[0];
window.addEventListener('scroll', function(){
  if (nav.getBoundingClientRect().top <= 0) {
    nav.classList.add('makeItFixed');
  } else {
    nav.classList.remove('makeItFixed');
  }
});

This could use some proper optimization, including a debouncing interval, and eventually moving the getBoundingClientRect() outside of the check - it is an expensive operation as it causes a page reflow/layout with every call.

In the future, the intersectionObserver API will take over, but as you wanted it to work in 'all' browsers, this is out of the question.

Upvotes: 0

fmsthird
fmsthird

Reputation: 1875

You can actually use a simple

position: sticky;
top: 0;    

Sticky behavior will apply once nav bar hits top position 0.

.contain{
  height:100vh;
  backgroud:#ccc;
}

nav {
    background-color: #CCC;
    overflow: hidden;
    padding: 1em;
    border-bottom: 1px solid #000;
    position: sticky;
    top: 0;
}

nav a {
        color: #000;
}

nav a:visited {
        color: #000;
}

nav .navWide {
    display: none;
    margin: 0 auto;
}

nav .navWide .wideDiv {
    text-align: center;
}

nav .navWide .wideDiv a {
    text-decoration: none;
    display: inline-block;
    padding: 0 2em;
}

nav .navNarrow i {
    float: left;
    cursor: pointer;
    color: #000;
}

nav .navNarrow .narrowLinks a {
    text-decoration: none;
    display: block;
    float: left;
    clear: left;
    padding: 0.5em 0;
}

.hidden {
  display: none;
}

/*Adjust breakpoint as desired to select when the "hamburger" menu is
replaced by just the links.*/
@media (min-width: 480px) {

    nav .navWide {
        display: block;
    
  }

    nav .navNarrow {
        display: none;
    }
}
<div class="contain">
  <p>
    Some text to enable scrolling.. Lorem ipsum dolor sit amet, illum definitiones 
    no quo, maluisset concludaturque et eum, altera fabulas ut quo. Atqui causae 
    gloriatur ius te, id agam omnis evertitur eum. Affert laboramus repudiandae nec 
    et. Inciderint efficiantur his ad. Eum no molestiae voluptatibus.
  </p>
</div>
<nav>
  <div class="navWide">
    <div class="wideDiv">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
  <div class="navNarrow">
    <i class="fa fa-bars fa-2x"></i>
    <div class="narrowLinks hidden">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
</nav>
<div class="contain">
      <p>
        Some text to enable scrolling.. Lorem ipsum dolor sit amet, illum definitiones 
        no quo, maluisset concludaturque et eum, altera fabulas ut quo. Atqui causae 
        gloriatur ius te, id agam omnis evertitur eum. Affert laboramus repudiandae nec 
        et. Inciderint efficiantur his ad. Eum no molestiae voluptatibus.
      </p>
    </div>

Upvotes: 1

Related Questions