Kenny Bones
Kenny Bones

Reputation: 5129

Javascript - Efficiency for scroll event, run code only once

These if statements are always making me dizzy. If have a page with a header, and a hidden header above that in the markup.

<div id="headerfloat" style="display:none;">
    <p>Floated header</p>
</div>
<div id="header">
    <p>Header</p>
</div>

The idea is that whenever the page is scrolled down more than 225px, the #headerfloat should appear, and dissapear when topScroll is less than 225px. And I managed to get this working with javascript and jQuery, but when I test it on iPhone, it's very sluggish. And I'm pretty sure it's because the code is run at each scroll event. And even if #headerfloat is visible, the code still executes. Even though it doesn't have to at that point.

So, I need to make sure the code only run once, when it's needed. My first thought was to add and remove classes like .open and .closed to #headerfloat. And run if statements on those during the scroll event. But is that the most efficient way of doing it?

My so far, ugly snippet:

jQuery(window).scroll(function () {
    var headerfloat = $("#header_float");
    var top = jQuery(window).scrollTop();
    if (top > 225) // height of float header
    {
        if (!$(headerfloat).hasClass("closed")) {
            $(headerfloat).addClass("boxshadow", "", 100, "easeInOutQuad").slideDown().addClass("open").removeClass("closed");
        }
    } else {
        $(headerfloat).removeClass("boxshadow", "", 100, "easeInOutQuad").removeClass("closed").slideUp();
    }
});

Edit: So after laconbass's awesome response, this is the code I ended up with:

var mainHeader = $('#header')
          , top_limit = mainHeader.outerHeight()
          , $window = $(window)
        ;

        var header_float = $('#header_float')

        bindEvent();

        function bindEvent() {
            $window.scroll( scrollEvent );
        }

        function scrollEvent() {
            var top = $window.scrollTop();
            // avoid any logic if nothing must be done
            if ( top < top_limit && !header_float.is(':visible')
                || top > top_limit && header_float.is(':visible')
            ) return;
            // unbind the scroll event to avoid its execution
            // until slide animation is complete
            $window.unbind( 'scroll' );
            // show/hide the header
            if ( top > top_limit ) {
                header_float.slideDown( 400, bindEvent );
            } else {
                header_float.slideUp( 400, bindEvent );
            }
        };

Upvotes: 0

Views: 2690

Answers (2)

laconbass
laconbass

Reputation: 17815

The snippet you started from seems a bit ugly.

I've made one on jsfiddle for your pleasure and reference

I've assumed the following:

  • you want a fixed positioned header when the page scrolls down (aka fixed header).
  • fixed headed is a clone of the page main header, with the class fixed.
  • fixed header is shown when the page scrolls down more than the header height.
  • fixed header is hidden when the page scrolls up enough to show the main page header.

Performance tips:

  • cache the jQuery objects to avoid making a new query each time the event handler is executed.
  • unbind the event handler before the show/hide animations, rebind it after.
  • on the event handler, return as soon as posible to avoid unnecesary logic. Remember while JavaScript is executed the browser render process is blocked.
var mainHeader = $('header')
  , header = mainHeader.clone().addClass('fixed').appendTo('body')
  , top_limit = header.outerHeight()
;

bindEvents();

function bindEvents() {
    $(window).scroll( scrollEvent );
}

function scrollEvent() {
    var top = $(window).scrollTop();
    // avoid any logic if nothing must be done
    if ( top < top_limit && !header.is(':visible')
        || top > top_limit && header.is(':visible')
    ) return;
    // unbind the scroll event to avoid its execution
    // until slide animation is complete
    $(window).unbind( 'scroll' );
    // show/hide the header
    if ( top > top_limit ) {
        header.slideDown( 400, bindEvents );
    } else {
        header.slideUp( 400, bindEvents );
    }
};
<header>
    <h1>Awesome header</h1>
</header>
<div>
    <!-- the page content -->
</div>
/* the real code needed */
header.fixed {
    display: none;
    position: fixed;
    top: 0;
}

Upvotes: 2

LIXer
LIXer

Reputation: 362

two way:

1,on scroll,and if you have done your want, remove the scroll event.

2,var a variable,default is false, on scroll, if the variable is false,to do you want,and set the variable to true; if the variable is true already, do nothing(or others you want)

Upvotes: 0

Related Questions