Skip to Content

scrollTo()

Animate the scroll to an element on the same page (automatically or manually).

JavaScript July 10, 2020

Usage

JavaScript
nebula.scrollTo(element, milliseconds, offset, onlyWhenBelow, callback)

Parameters

element
(Required) (Object) The jQuery object of the element to scroll to
Default: None

milliseconds
(Optional) (Integer) How long the animated scroll should take
Default: 500

offset
(Optional) (Integer) Pixel offset of the element
Default: None

onlyWhenBelow
(Optional) (Boolean) Only scroll if the viewport is below the element (with offset)
Default: false

callback
(Optional) (Function) A function to run after the scroll animation has completed
Default: None

Parameter Notes

offset is automatically subtracted from the element top, so positive numbers are above the element and negative numbers are below it.

Note: if a  scroll-padding-top CSS property is applied to the <body> element, it will be accounted for as well and added to the offset automatically.

Request or provide clarification »

Examples

Scroll to a specific element.

JavaScript
nebula.scrollTo(jQuery('.some-element'));

Scroll to 100px above an element only when the viewport is below that point (triggered on a Bootstrap carousel slide).

JavaScript
jQuery('#example-bootstrap-carousel').on('slide.bs.carousel', function(){
    nebula.scrollTo(jQuery('#signup-form'), 500, 100, true);
});

Automatically scroll to an element.

HTML
<a href="#lower-element">Click here to scroll</a>

Automatically scroll with an offset

HTML
<a href="#lower-element" offset="200">Click here to scroll</a>

Alternate way to automatically scroll to an element.

HTML
<a class="nebula-scrollto" href="#" scroll-to="#lower-element" offset="200">Click here to scroll</a>

Demo


Click here to scroll (via href) Click here to scroll (via JS method)

Additional Notes

This function can be called specifically to scroll to an element, but it also runs automatically on any links where the href is an ID that starts with # (unless the class no-scroll is added to the element) or when the class nebula-scrollto is present.

After scrolling, the element will be “focused” on to maintain keyboard navigability. If you’re seeing borders on elements after scrolling, this is what is happening. You can modify your CSS to change this.

It is highly recommended to use scroll-padding-top in CSS to ensure the browser accounts for the fixed header on the page if used.

Source File

Located in /assets/js/nebula.js on line 3641.

JavaScript
nebula.scrollTo = function(element, scrollSpeed, offset, onlyWhenBelow, callback){
    if ( nebula.dom.html.css('scroll-behavior') !== 'smooth' ){ //If the html has smooth scroll-behavior, use that instead of this.
        if ( !offset ){
            var offset = nebula.scroll.offset || 0; //Note: This selector should be the height of the fixed header, or a hard-coded offset.
        }

        //Account for the scroll-padding-top CSS property on the body element
        var scrollPaddingTop = parseInt(nebula.dom.body.css('scroll-padding-top'), 10); //Parse the CSS value as a base-10 integer
        if ( !isNaN(scrollPaddingTop) ){
            offset = offset + scrollPaddingTop;
        }

        //Call this function with a jQuery object to trigger scroll to an element (not just a selector string).
        if ( element ){
            if ( typeof element === 'string' ){
                element = jQuery(element);
            }

            if ( element.length ){
                var willScroll = true;
                if ( onlyWhenBelow ){
                    var elementTop = element.offset().top-offset;
                    var viewportTop = nebula.dom.document.scrollTop();
                    if ( viewportTop-elementTop <= 0 ){
                        willScroll = false;
                    }
                }

                if ( willScroll ){
                    if ( !scrollSpeed ){
                        var scrollSpeed = nebula.scroll.speed || 500;
                    }

                    jQuery('html, body').animate({
                        scrollTop: element.offset().top-offset
                    }, scrollSpeed, function(){
                        nebula.focusOnElement(element);

                        if ( callback ){
                            callback();
                        }
                    });
                }
            }

            return false;
        }

        nebula.dom.document.on('click keyup', 'a[href*="#"]:not([href="#"])', function(e){ //An href contains a hash ID but is not only a hash ("#content" but not "#")
            if ( e.type === 'click' || (e.type === 'keyup' && (e.keyCode === 32 || e.keyCode === 13)) ){ //Spacebar or Enter
                var avoid = '.no-scroll, .mm-menu, .carousel, .tab-content, .modal, [data-toggle], #wpadminbar, #query-monitor';
                if ( !jQuery(this).is(avoid) && !jQuery(this).parents(avoid).length ){
                    if ( location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') && location.hostname === this.hostname ){ //Ensure the link does not have a protocol and is internal
                        var thisHash = this.hash;
                        var target = jQuery(thisHash) || jQuery('[name=' + thisHash.slice(1) +']'); //Determine the target
                        if ( target.length ){ //If target exists
                            var pOffset = ( jQuery(this).attr('data-offset') )? parseFloat(jQuery(this).attr('data-offset')) : nebula.scroll.offset; //Determine the offset
                            var nOffset = Math.floor(target.offset().top-offset+pOffset) + jQuery('body').scrollTop();
                            scrollSpeed = nebula.scroll.speed || 500;

                            //Eventually replace with target.scrollIntoView({behavior: 'smooth'});

                            jQuery('html, body').animate({
                                scrollTop: nOffset
                            }, scrollSpeed, function(){
                                nebula.focusOnElement(target);
                                history.replaceState({}, '', thisHash); //Add the hash to the URL so it can be refreshed, copied, links, etc. ReplaceState does this without affecting the back button.
                            }); //Speed is hard-coded, but could look for an HTML attribute if desired
                            return false;
                        }
                    }
                }
            }
        });

        nebula.dom.document.on('click keyup', '.nebula-scrollto', function(e){ //Using the nebula-scrollto class with scrollto attribute.
            if ( e.type === 'click' || (e.type === 'keyup' && (e.keyCode === 32 || e.keyCode === 13)) ){ //Spacebar or Enter
                var pOffset = ( jQuery(this).attr('offset') )? parseFloat(jQuery(this).attr('offset')) : nebula.scroll.offset;

                if ( jQuery(this).attr('scrollto') ){
                    var scrollElement = jQuery(this).attr('scroll-to');
                    if ( scrollElement !== '' ){
                        scrollSpeed = nebula.scroll.speed || 500;

                        //Eventually replace with scrollElement.scrollIntoView({behavior: 'smooth'});

                        jQuery('html, body').animate({
                            scrollTop: Math.floor(jQuery(scrollElement).offset().top-offset+pOffset)
                        }, scrollSpeed, function(){
                            nebula.focusOnElement(scrollElement);
                        });
                    }
                }

                return false;
            }
        });
    }
};

Override

To override or disable this JavaScript function, simply redeclare it with the exact same function name.

JavaScript
nebula.scrollTo = function(element, milliseconds, offset, onlyWhenBelow, callback){
    //Write your own code here, leave it blank, or return false.
}