WordPress/WordPress

View on GitHub
wp-content/themes/twentyseventeen/assets/js/global.js

Summary

Maintainability
A
1 hr
Test Coverage
/* global twentyseventeenScreenReaderText */
(function( $ ) {

    // Variables and DOM Caching.
    var $body = $( 'body' ),
        $customHeader = $body.find( '.custom-header' ),
        $branding = $customHeader.find( '.site-branding' ),
        $navigation = $body.find( '.navigation-top' ),
        $navWrap = $navigation.find( '.wrap' ),
        $navMenuItem = $navigation.find( '.menu-item' ),
        $menuToggle = $navigation.find( '.menu-toggle' ),
        $menuScrollDown = $body.find( '.menu-scroll-down' ),
        $sidebar = $body.find( '#secondary' ),
        $entryContent = $body.find( '.entry-content' ),
        $formatQuote = $body.find( '.format-quote blockquote' ),
        isFrontPage = $body.hasClass( 'twentyseventeen-front-page' ) || $body.hasClass( 'home blog' ),
        navigationFixedClass = 'site-navigation-fixed',
        navigationHeight,
        navigationOuterHeight,
        navPadding,
        navMenuItemHeight,
        idealNavHeight,
        navIsNotTooTall,
        headerOffset,
        menuTop = 0,
        resizeTimer;

    // Ensure the sticky navigation doesn't cover current focused links.
    $( 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex], [contenteditable]', '.site-content-contain' ).filter( ':visible' ).on( 'focus', function() {
        if ( $navigation.hasClass( 'site-navigation-fixed' ) ) {
            var windowScrollTop = $( window ).scrollTop(),
                fixedNavHeight = $navigation.height(),
                itemScrollTop = $( this ).offset().top,
                offsetDiff = itemScrollTop - windowScrollTop;

            // Account for Admin bar.
            if ( $( '#wpadminbar' ).length ) {
                offsetDiff -= $( '#wpadminbar' ).height();
            }

            if ( offsetDiff < fixedNavHeight ) {
                $( window ).scrollTo( itemScrollTop - ( fixedNavHeight + 50 ), 0 );
            }
        }
    });

    // Set properties of navigation.
    function setNavProps() {
        navigationHeight      = $navigation.height();
        navigationOuterHeight = $navigation.outerHeight();
        navPadding            = parseFloat( $navWrap.css( 'padding-top' ) ) * 2;
        navMenuItemHeight     = $navMenuItem.outerHeight() * 2;
        idealNavHeight        = navPadding + navMenuItemHeight;
        navIsNotTooTall       = navigationHeight <= idealNavHeight;
    }

    // Make navigation 'stick'.
    function adjustScrollClass() {

        // Make sure we're not on a mobile screen.
        if ( 'none' === $menuToggle.css( 'display' ) ) {

            // Make sure the nav isn't taller than two rows.
            if ( navIsNotTooTall ) {

                // When there's a custom header image or video, the header offset includes the height of the navigation.
                if ( isFrontPage && ( $body.hasClass( 'has-header-image' ) || $body.hasClass( 'has-header-video' ) ) ) {
                    headerOffset = $customHeader.innerHeight() - navigationOuterHeight;
                } else {
                    headerOffset = $customHeader.innerHeight();
                }

                // If the scroll is more than the custom header, set the fixed class.
                if ( $( window ).scrollTop() >= headerOffset ) {
                    $navigation.addClass( navigationFixedClass );
                } else {
                    $navigation.removeClass( navigationFixedClass );
                }

            } else {

                // Remove 'fixed' class if nav is taller than two rows.
                $navigation.removeClass( navigationFixedClass );
            }
        }
    }

    // Set margins of branding in header.
    function adjustHeaderHeight() {
        if ( 'none' === $menuToggle.css( 'display' ) ) {

            // The margin should be applied to different elements on front-page or home vs interior pages.
            if ( isFrontPage ) {
                $branding.css( 'margin-bottom', navigationOuterHeight );
            } else {
                $customHeader.css( 'margin-bottom', navigationOuterHeight );
            }

        } else {
            $customHeader.css( 'margin-bottom', '0' );
            $branding.css( 'margin-bottom', '0' );
        }
    }

    // Set icon for quotes.
    function setQuotesIcon() {
        $( twentyseventeenScreenReaderText.quote ).prependTo( $formatQuote );
    }

    // Add 'below-entry-meta' class to elements.
    function belowEntryMetaClass( param ) {
        var sidebarPos, sidebarPosBottom;

        if ( ! $body.hasClass( 'has-sidebar' ) ||
            typeof $sidebar === 'undefined' ||
            $sidebar.length < 1 || (
            $body.hasClass( 'search' ) ||
            $body.hasClass( 'single-attachment' ) ||
            $body.hasClass( 'error404' ) ||
            $body.hasClass( 'twentyseventeen-front-page' )
        ) ) {
            return;
        }

        sidebarPos       = $sidebar.offset();
        sidebarPosBottom = sidebarPos.top + ( $sidebar.height() + 28 );

        $entryContent.find( param ).each( function() {
            var $element = $( this ),
                elementPos = $element.offset(),
                elementPosTop = elementPos.top;

            // Add 'below-entry-meta' to elements below the entry meta.
            if ( elementPosTop > sidebarPosBottom ) {
                $element.addClass( 'below-entry-meta' );
            } else {
                $element.removeClass( 'below-entry-meta' );
            }
        });
    }

    /*
     * Test if inline SVGs are supported.
     * @link https://github.com/Modernizr/Modernizr/
     */
    function supportsInlineSVG() {
        var div = document.createElement( 'div' );
        div.innerHTML = '<svg/>';
        return 'http://www.w3.org/2000/svg' === ( 'undefined' !== typeof SVGRect && div.firstChild && div.firstChild.namespaceURI );
    }

    /**
     * Test if an iOS device.
    */
    function checkiOS() {
        return /iPad|iPhone|iPod/.test(navigator.userAgent) && ! window.MSStream;
    }

    /*
     * Test if background-attachment: fixed is supported.
     * @link http://stackoverflow.com/questions/14115080/detect-support-for-background-attachment-fixed
     */
    function supportsFixedBackground() {
        var el = document.createElement('div'),
            isSupported;

        try {
            if ( ! ( 'backgroundAttachment' in el.style ) || checkiOS() ) {
                return false;
            }
            el.style.backgroundAttachment = 'fixed';
            isSupported = ( 'fixed' === el.style.backgroundAttachment );
            return isSupported;
        }
        catch (e) {
            return false;
        }
    }

    // Fire on document ready.
    $( function() {

        // If navigation menu is present on page, setNavProps and adjustScrollClass.
        if ( $navigation.length ) {
            setNavProps();
            adjustScrollClass();
        }

        // If 'Scroll Down' arrow in present on page, calculate scroll offset and bind an event handler to the click event.
        if ( $menuScrollDown.length ) {

            if ( $( 'body' ).hasClass( 'admin-bar' ) ) {
                menuTop -= 32;
            }
            if ( $( 'body' ).hasClass( 'blog' ) ) {
                menuTop -= 30; // The div for latest posts has no space above content, add some to account for this.
            }
            if ( ! $navigation.length ) {
                navigationOuterHeight = 0;
            }

            $menuScrollDown.on( 'click', function( e ) {
                e.preventDefault();
                $( window ).scrollTo( '#primary', {
                    duration: 600,
                    offset: { top: menuTop - navigationOuterHeight }
                });
            });
        }

        adjustHeaderHeight();
        setQuotesIcon();
        belowEntryMetaClass( 'blockquote.alignleft, blockquote.alignright' );
        if ( true === supportsInlineSVG() ) {
            document.documentElement.className = document.documentElement.className.replace( /(\s*)no-svg(\s*)/, '$1svg$2' );
        }

        if ( true === supportsFixedBackground() ) {
            document.documentElement.className += ' background-fixed';
        }
    } );

    // If navigation menu is present on page, adjust it on scroll and screen resize.
    if ( $navigation.length ) {

        // On scroll, we want to stick/unstick the navigation.
        $( window ).on( 'scroll', function() {
            adjustScrollClass();
            adjustHeaderHeight();
        });

        // Also want to make sure the navigation is where it should be on resize.
        $( window ).on( 'resize', function() {
            setNavProps();
            setTimeout( adjustScrollClass, 500 );
        });
    }

    $( window ).on( 'resize', function() {
        clearTimeout( resizeTimer );
        resizeTimer = setTimeout( function() {
            belowEntryMetaClass( 'blockquote.alignleft, blockquote.alignright' );
        }, 300 );
        setTimeout( adjustHeaderHeight, 1000 );
    });

    // Add header video class after the video is loaded.
    $( document ).on( 'wp-custom-header-video-loaded', function() {
        $body.addClass( 'has-header-video' );
    });

})( jQuery );