On this site this site, I have some code which you can see below which figures out the offset for each post from the top of the document and assigns a colour from each post to the header when it scrolls into view. I am having to wait for the window load event until the code works which really is not very elegant. Can anyone see how I can improve this code?

$(window).load(function() {
    var $header = $("header");
    var numberOfSections = $("section").length;   
    var sectionOffsets = [];

    for(var i = 0; i < numberOfSections; i++) {

    $(window).scroll(function () {
         $("section").each(function () {
            if ($(window).scrollTop() > $(this).offset().top - 180) {
                $("header").css('color', $(this).data("colour"));
  • \$\begingroup\$ What's the point of $header, numberOfSections, and sectionOffsets? \$\endgroup\$ May 24, 2015 at 10:50
  • \$\begingroup\$ What's wrong with load event for you? \$\endgroup\$
    – smt
    May 24, 2015 at 21:00
  • \$\begingroup\$ The load event is not very good as there are a lot of images on the page and the function does not work until all of the images are loaded \$\endgroup\$
    – 2ne
    May 25, 2015 at 15:24

1 Answer 1


jsBin demo

Instead of storing section positions on window onload time, which can be always kind of inaccurate since your images might still load...

get dynamically the current position / size data of an element using:


using that JS method you don't even need to calculate the $(window).scrollTop()
cause the returned value is the element's position respective to the client top (viewport top edge).

To retrieve only the one element that matches a top criteria you can use the jQuery's .filter() method and return the element which...

    var r = this.getBoundingClientRect();
    return  r.top + r.height - 100  > 0; // ...matches this

(100 is the header height in this demo; set as desired or calculate dynamically)

Now, since jQuery filtered more than one element that matches that criteria of (gbcr.top + gbcr.height - headeroffser) > 0 by going directly to chain another method to it like .data(), the value will be respective to the first of elements returned in the .filter() collection:

  color : $section.filter(function(){
    var r = this.getBoundingClientRect();
    return  r.top + r.height - 100 > 0;
  }).data().colour // is the colour data of the first of filtered elements

Store that colouring stuff inside a setScrollColors function and use like:

$(function() { // DOM ready shorthand

  var $header  = $("header");  // Cache selectors
  var $section = $("section");

  function setScrollColors() {
      color : $section.filter(function(){
        var r = this.getBoundingClientRect();
        return  r.top + r.height - 100 > 0;

  setScrollColors();                            // Call inside dom ready
  $(window).on("load scroll", setScrollColors); // call also on load and scroll

  border-bottom:1px solid currentColor;
  min-height: 1000px;
  min-height: calc(100vh - 100px);
  border-top:1px solid #000;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="content">
  <section data-colour="black">black</section>
  <section data-colour="red">red</section>
  <section data-colour="blue">blue</section>
  <section data-colour="green">green</section>
  <section data-colour="fuchsia">fuchsia</section>
  <section data-colour="orange">orange</section>

  • 1
    \$\begingroup\$ This seems to be a more elegant solution than my approach. I have never used "getBoundingClientRect" before. It seems very useful. \$\endgroup\$
    – 2ne
    May 25, 2015 at 15:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.