Adding Headroom with JavaScript

Internet Browser
Image by janjf93 from Pixabay

Fixed navigation bars have an annoying drawback — they take up precious screen real estate. We can add headroom to any web page by automatically retracting and expanding the navigation bar on Of course, retracting and expanding navigation bars can be even more annoying. In the Improvements section we try to ease this pain.

We will create a super simple implementation with vanilla JavaScript and discuss some ways to improve its behavior. First let’s add some hacker style boilerplate to our app.js file.

javascript
/**
 * Boilerplate Functions
 */
function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }
function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
function arem (a, x) { var i = apos(x, a); if (i >= 0) { a.splice(i, 1); } return a; }
function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }
function remClass (el, cl) { if (el) { var a = el.className.split(' '); arem(a, cl); el.className = a.join(' ') } }

Then setup our variables. The variable previousPosition stores the initial page position. Bind to the navigation bar with the navbar selector and grab its height using the offsetHeight.

javascript
/**
 * Setup Variables
 */
var previousPosition = window.pageYOffset;
var navbar = document.getElementById("navbar");
var navbarHeight = navbar.offsetHeight;

Add an onscroll event with a simple check that hides the navigation bar on scroll down and reveals it on scroll up.

javascript
/**
 * Headroom Action
 */
window.onscroll = function() {
  var currentPosition = window.pageYOffset;

  if (previousPosition > currentPosition) {
    remClass(navbar, 'headroom');
  } else if (currentPosition > navbarHeight) {
    addClass(navbar, 'headroom');
  }

  previousPosition = currentPosition;
};

We add headroom to the page only when the current position is greater than the navigation bar’s height.

javascript
else if (currentPosition > navbarHeight) {
  addClass(navbar, 'headroom');
}

For our CSS style sheet, force the navigation bar upwards from its initial fixed position and give it a transition time to make the movement less jarring.

css
.headroom { top: -5em !important; }
.navbar {
  -webkit-transition: top 0.75s;
  -o-transition: top 0.75s;
  transition: top 0.75s;
}

Demonstration and Source

Basic auto hiding navigation bar.

Improvements

How can we improve this simple script? Let’s introduce velocity. We will show the navigation bar only if the user scrolls up faster than normal.

The velocity will be the difference between the previous and current position. The greater the difference, the faster the user scrolls. A scroll downwards produces a negative value and a scroll upwards produces a positive value.

javascript
var velocity = previousPosition - currentPosition;

Now that we know the velocity, we can add resistance by setting a velocity threshold of 55. The user must now overcome this velocity to reveal the navigation bar. The effect of this resistance threshold will vary based on device and browser.

The else if condition is capped to velocity < 0. This handles a situation where the user performs a flick scroll gesture which involves both acceleration and deceleration. In this scenario accidental triggers can happen if we do not consider the direction of the scroll.

javascript
if (velocity > 55 || currentPosition < navbarHeight) {
  remClass(navbar, 'headroom');
} else if (velocity < 0) {
  addClass(navbar, 'headroom');
}

Due to this implementation, add an initial check at run time to handle a refresh anywhere in the middle of the This is an unnecessary check, but the initial auto–hide on page load effect was sort of interesting.

javascript
var previousPosition = window.pageYOffset;
var navbar = document.getElementById("navbar");
var navbarHeight = navbar.offsetHeight;

if (previousPosition > navbarHeight) {
  addClass(navbar, 'headroom');
}

window.onscroll = function() {
  var currentPosition = window.pageYOffset;
  var velocity = previousPosition - currentPosition;

  if (velocity > 55 || currentPosition < navbarHeight) {
    remClass(navbar, 'headroom');
  } else if (velocity < 0) {
    addClass(navbar, 'headroom');
  }

  previousPosition = currentPosition;
};

Demonstration and Source

Improved auto hiding navigation bar.

Conclusion

That’s it. We can make even Predict the user’s intent by adding a few more basic axioms into the mix. improvements but this looks good enough.

8 April 2019 — Written
8 April 2019 — Updated
Thedro Neely — Creator
adding-headroom-with-javascript.md — Article

More Content

Openring

Web Ring

Comments

References

  1. https://thedroneely.com/git/
  2. https://thedroneely.com/
  3. https://thedroneely.com/posts/
  4. https://thedroneely.com/projects/
  5. https://thedroneely.com/about/
  6. https://thedroneely.com/contact/
  7. https://thedroneely.com/abstracts/
  8. https://ko-fi.com/thedroneely
  9. https://thedroneely.com/tags/javascript/
  10. https://thedroneely.com/tags/webdev/
  11. https://thedroneely.com/posts/adding-headroom-with-javascript/#isso-thread
  12. https://thedroneely.com/posts/rss.xml
  13. https://thedroneely.com/images/adding-headroom-with-javascript.png
  14. https://thedroneely.com/posts/adding-headroom-with-javascript/#improvements
  15. https://news.ycombinator.com/hn.js
  16. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-232bc8f
  17. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-62ac8ca
  18. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-941ceed
  19. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-5a439b0
  20. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-f282cf9
  21. https://jsbin.com/merekas/edit?js,output
  22. https://thedroneely.com/videos/headroom-normal-scroll.mp4
  23. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-0af6185
  24. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-564bd88
  25. https://thedroneely.com/posts/adding-headroom-with-javascript/#code-block-49ef2df
  26. https://jsbin.com/cidaxut/edit?js,output
  27. https://thedroneely.com/videos/headroom-scroll-resistance.mp4
  28. https://thedroneely.com/posts/adding-headroom-with-javascript/#conclusion
  29. https://www.thedroneely.com/posts/adding-headroom-with-javascript.md
  30. https://thedroneely.com/posts/tailwind-css-and-beyond/
  31. https://thedroneely.com/posts/adding-headroom-with-javascript/
  32. https://thedroneely.com/posts/hugo-is-good/
  33. https://git.sr.ht/~sircmpwn/openring
  34. https://drewdevault.com/2022/11/12/In-praise-of-Plan-9.html
  35. https://drewdevault.com/
  36. https://mxb.dev/blog/the-indieweb-for-everyone/
  37. https://mxb.dev/
  38. https://www.taniarascia.com/simplifying-drag-and-drop/
  39. https://www.taniarascia.com/
  40. https://thedroneely.com/posts/adding-headroom-with-javascript#isso-thread
  41. https://thedroneely.com/posts/adding-headroom-with-javascript#improvements
  42. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-232bc8f
  43. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-62ac8ca
  44. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-941ceed
  45. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-5a439b0
  46. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-f282cf9
  47. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-0af6185
  48. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-564bd88
  49. https://thedroneely.com/posts/adding-headroom-with-javascript#code-block-49ef2df
  50. https://thedroneely.com/posts/adding-headroom-with-javascript#conclusion
  51. https://thedroneely.com/posts/programming-sans-internet/
  52. https://thedroneely.com/posts/extreme-ssh-hardening/
  53. https://thedroneely.com/posts/tweaking-goaccess-for-analytics/
  54. https://thedroneely.com/posts/lets-customize-gitea/
  55. https://thedroneely.com/posts/making-web-pages/
  56. https://drewdevault.com/2022/09/16/Open-source-matters.html
  57. https://mxb.dev/blog/make-free-stuff/
  58. https://thedroneely.com/sitemap.xml
  59. https://thedroneely.com/index.json
  60. https://thedroneely.com/resume/
  61. https://gitlab.com/tdro
  62. https://github.com/tdro
  63. https://codeberg.org/tdro
  64. https://thedroneely.com/analytics
  65. https://thedroneely.com/posts/adding-headroom-with-javascript#
  66. https://creativecommons.org/licenses/by-sa/2.0/
  67. https://thedroneely.com/git/thedroneely/thedroneely.com
  68. https://opensource.org/licenses/GPL-3.0
  69. https://www.thedroneely.com/
  70. https://thedroneely.com/posts/adding-headroom-with-javascript/#