339 lines
9.2 KiB
JavaScript
339 lines
9.2 KiB
JavaScript
|
/**
|
||
|
* File navigation.js.
|
||
|
*
|
||
|
* Handles toggling the navigation menu for small screens and enables TAB key
|
||
|
* navigation support for dropdown menus.
|
||
|
*
|
||
|
* @package Zakra
|
||
|
*/
|
||
|
|
||
|
var ZakraNavHelper = {
|
||
|
|
||
|
// Returns first children of a node.
|
||
|
getChildNodes : function ( node ) {
|
||
|
var children = [], child;
|
||
|
|
||
|
for ( child in node.childNodes ) {
|
||
|
if ( node.childNodes.hasOwnProperty( child ) && 1 === node.childNodes[child].nodeType ) {
|
||
|
children.push( node.childNodes[child] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return children;
|
||
|
},
|
||
|
|
||
|
offset: function ( el ) {
|
||
|
var rect = el.getBoundingClientRect(),
|
||
|
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
|
||
|
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||
|
|
||
|
return {top: rect.top + scrollTop, left: rect.left + scrollLeft}
|
||
|
},
|
||
|
|
||
|
|
||
|
// Calculate the dimension of an element with margin, padding and content.
|
||
|
dimension : function ( el ) {
|
||
|
return parseInt( document.defaultView.getComputedStyle( el, '' ).getPropertyValue( 'width' ) ) + parseInt( document.defaultView.getComputedStyle( el, '' ).getPropertyValue( 'margin-left' ) ) + parseInt( document.defaultView.getComputedStyle( el, '' ).getPropertyValue( 'padding-left' ) ) + parseInt( document.defaultView.getComputedStyle( el, '' ).getPropertyValue( 'padding-right' ) ) + parseInt( document.defaultView.getComputedStyle( el, '' ).getPropertyValue( 'margin-right' ) );
|
||
|
},
|
||
|
|
||
|
getOverflowItems : function ( navLi ) {
|
||
|
|
||
|
|
||
|
navigation.style.flex = '0 0 ' + navUlTempWidth + 'px';
|
||
|
|
||
|
var extraLi = [];
|
||
|
|
||
|
for ( var liCount = 0; liCount < navLi.length; liCount++ ) {
|
||
|
var initialPos, li, posTop;
|
||
|
|
||
|
li = navLi[liCount];
|
||
|
posTop = this.offset( li ).top;
|
||
|
|
||
|
if ( 0 === liCount ) {
|
||
|
initialPos = posTop;
|
||
|
}
|
||
|
|
||
|
if ( posTop > initialPos ) {
|
||
|
if ( ! li.classList.contains( 'tg-menu-item-search' ) && ! li.classList.contains( 'tg-menu-item-cart' ) &&
|
||
|
! li.classList.contains( 'tg-header-button-wrap' ) && ! li.classList.contains( 'tg-menu-extras-wrap' )
|
||
|
) {
|
||
|
extraLi.push( li );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return extraLi;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
window.zakraNavHelper = ZakraNavHelper;
|
||
|
|
||
|
(
|
||
|
function () {
|
||
|
var container, menu, links, i, len;
|
||
|
|
||
|
container = document.getElementById( 'site-navigation' );
|
||
|
|
||
|
if ( ! container ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
menu = container.getElementsByTagName( 'ul' )[0];
|
||
|
|
||
|
menu.setAttribute( 'aria-expanded', 'false' );
|
||
|
|
||
|
if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
|
||
|
menu.className += ' nav-menu';
|
||
|
}
|
||
|
|
||
|
// Get all the link elements within the menu.
|
||
|
links = menu.getElementsByTagName( 'a' );
|
||
|
|
||
|
// Each time a menu link is focused or blurred, toggle focus.
|
||
|
for ( i = 0, len = links.length; i < len; i++ ) {
|
||
|
links[i].addEventListener( 'focus', toggleFocus, true );
|
||
|
links[i].addEventListener( 'blur', toggleFocus, true );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets or removes .focus class on an element.
|
||
|
*/
|
||
|
function toggleFocus() {
|
||
|
var self = this;
|
||
|
|
||
|
// Move up through the ancestors of the current link until we hit .nav-menu.
|
||
|
while ( -1 === self.className.indexOf( 'nav-menu' ) ) {
|
||
|
|
||
|
// On li elements toggle the class .focus.
|
||
|
if ( 'li' === self.tagName.toLowerCase() ) {
|
||
|
if ( -1 !== self.className.indexOf( 'focus' ) ) {
|
||
|
self.className = self.className.replace( ' focus', '' );
|
||
|
} else {
|
||
|
self.className += ' focus';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self = self.parentElement;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggles `focus` class to allow submenu access on tablets.
|
||
|
*/
|
||
|
(
|
||
|
function ( container ) {
|
||
|
var touchStartFn, i,
|
||
|
parentLink = container.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' ),
|
||
|
supportsPassive = false;;
|
||
|
try {
|
||
|
var opts = Object.defineProperty( {}, 'passive', {
|
||
|
get: function() {
|
||
|
supportsPassive = true;
|
||
|
}
|
||
|
} );
|
||
|
window.addEventListener( "testPassive", null, opts );
|
||
|
window.removeEventListener( "testPassive", null, opts );
|
||
|
} catch (e) {}
|
||
|
|
||
|
if ( 'ontouchstart' in window ) {
|
||
|
touchStartFn = function ( e ) {
|
||
|
var i,
|
||
|
menuItem = this.parentNode;
|
||
|
|
||
|
if ( ! menuItem.classList.contains( 'focus' ) ) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
for ( i = 0; i < menuItem.parentNode.children.length; ++i ) {
|
||
|
if ( menuItem === menuItem.parentNode.children[i] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
menuItem.parentNode.children[i].classList.remove( 'focus' );
|
||
|
}
|
||
|
menuItem.classList.add( 'focus' );
|
||
|
} else {
|
||
|
menuItem.classList.remove( 'focus' );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
for ( i = 0; i < parentLink.length; ++i ) {
|
||
|
parentLink[i].addEventListener( 'touchstart', touchStartFn, supportsPassive ? { passive: true } : false );
|
||
|
}
|
||
|
}
|
||
|
}( container )
|
||
|
);
|
||
|
}()
|
||
|
);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fixes menu out of viewport
|
||
|
*/
|
||
|
(
|
||
|
function () {
|
||
|
var i, elEvent;
|
||
|
|
||
|
var elWithChildren = document.querySelectorAll( '.tg-primary-menu li.menu-item-has-children, .tg-primary-menu li.page_item_has_children' ),
|
||
|
elCount = elWithChildren.length,
|
||
|
elEvent = isTouchDevice() ? 'touchstart' : 'mouseenter';
|
||
|
|
||
|
/**
|
||
|
* @see https://stackoverflow.com/questions/123999/how-can-i-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
|
||
|
*/
|
||
|
function isElementInViewport( el ) {
|
||
|
var rect = el.getBoundingClientRect();
|
||
|
|
||
|
return (
|
||
|
0 <= rect.left &&
|
||
|
rect.right <= (
|
||
|
( window.innerWidth - 10 ) || ( document.documentElement.clientWidth - 10 )
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Checks whether user uses touch device or not.
|
||
|
function isTouchDevice() {
|
||
|
return 'ontouchstart' in window || navigator.maxTouchPoints;
|
||
|
};
|
||
|
|
||
|
// Loop through li having sub menu.
|
||
|
for ( i = 0; i < elCount; i++ ) {
|
||
|
|
||
|
// On mouse enter.
|
||
|
elWithChildren[i].addEventListener( elEvent, function ( ev ) {
|
||
|
var li = ev.currentTarget,
|
||
|
subMenu;
|
||
|
|
||
|
if ( li ) {
|
||
|
|
||
|
subMenu = li.querySelectorAll( '.sub-menu, .children' )[0];
|
||
|
|
||
|
if ( subMenu ) {
|
||
|
if ( ! isElementInViewport( subMenu ) ) {
|
||
|
subMenu.classList.add( 'tg-edge' );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}, false );
|
||
|
|
||
|
// On mouse leave.
|
||
|
elWithChildren[i].addEventListener( 'mouseleave', function ( ev ) {
|
||
|
var li = ev.currentTarget,
|
||
|
sub;
|
||
|
|
||
|
if ( li ) {
|
||
|
sub = li.querySelectorAll( '.sub-menu, .children' )[0];
|
||
|
|
||
|
sub.classList.remove( 'tg-edge' );
|
||
|
|
||
|
if ( sub.classList.contains( 'tg-edge' ) ) {
|
||
|
sub.classList.remove( 'tg-edge' );
|
||
|
}
|
||
|
}
|
||
|
}, false );
|
||
|
} // End: for ( i in elWithChildren ) {
|
||
|
|
||
|
}
|
||
|
)();
|
||
|
|
||
|
/**
|
||
|
* Keep menu items on one line.
|
||
|
*/
|
||
|
(
|
||
|
function () {
|
||
|
|
||
|
// Get required elements.
|
||
|
var more, search, cart, button, button2, searchWidth, cartWidth, buttonWidth, moreWidth, navUl, navLi,
|
||
|
navLiWidth, extraWrap, navWrapWidth, overflowItems;
|
||
|
|
||
|
navigation = document.getElementById( 'site-navigation' );
|
||
|
|
||
|
// Return if no navigation markup.
|
||
|
if ( null === navigation ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Return if `Keep Menu Items on One Line` customizer option is not enabled
|
||
|
if ( ! navigation.classList.contains( 'tg-extra-menus' ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Extra ellipsis icon added via PHP.
|
||
|
more = navigation.getElementsByClassName( 'tg-menu-extras-wrap' )[0];
|
||
|
|
||
|
// Ul to append extra menu items.
|
||
|
extraWrap = document.getElementById( 'tg-menu-extras' );
|
||
|
|
||
|
// No primary menu assigned.
|
||
|
if ( null === extraWrap ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
navUl = navigation.getElementsByClassName( 'nav-menu' )[0];
|
||
|
|
||
|
navLi = ZakraNavHelper.getChildNodes( navUl );
|
||
|
|
||
|
navWrapWidth = navigation.offsetWidth;
|
||
|
|
||
|
search = navigation.getElementsByClassName( 'tg-menu-item-search' )[0];
|
||
|
cart = navigation.getElementsByClassName( 'tg-menu-item-cart' )[0];
|
||
|
button = navigation.getElementsByClassName( 'tg-header-button-wrap' )[0];
|
||
|
button2 = navigation.getElementsByClassName( 'tg-header-button-wrap' )[1];
|
||
|
|
||
|
searchWidth = search ? ZakraNavHelper.dimension( search ) : 0;
|
||
|
cartWidth = cart ? ZakraNavHelper.dimension( cart ) : 0;
|
||
|
buttonWidth = button ? ZakraNavHelper.dimension( button ) : 0;
|
||
|
buttonWidth += button2 ? ZakraNavHelper.dimension( button2 ) : 0;
|
||
|
moreWidth = more ? ZakraNavHelper.dimension( more ) : 0;
|
||
|
|
||
|
navUlTempWidth = navWrapWidth - ( searchWidth + cartWidth + buttonWidth + moreWidth );
|
||
|
|
||
|
navLiWidth = 0;
|
||
|
|
||
|
navLi.forEach( function ( menuItem, index ) {
|
||
|
navLiWidth += ZakraNavHelper.dimension( menuItem );
|
||
|
} );
|
||
|
|
||
|
// If overflow.
|
||
|
if ( navLiWidth > navWrapWidth ) {
|
||
|
|
||
|
overflowItems = ZakraNavHelper.getOverflowItems( navLi );
|
||
|
|
||
|
overflowItems.forEach( function ( item ) {
|
||
|
extraWrap.appendChild( item );
|
||
|
});
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// Remove ellipsis icon for more.
|
||
|
more.parentNode.removeChild( more );
|
||
|
}
|
||
|
|
||
|
navigation.style.flex = '';
|
||
|
}()
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* Close mobile menu on clicking menu items.
|
||
|
*/
|
||
|
(
|
||
|
function () {
|
||
|
var mobMenuItems = document.querySelectorAll( '#mobile-navigation li a' ),
|
||
|
toggleButton = document.querySelector( '.tg-mobile-toggle' ),
|
||
|
mobMenuItemsCount = mobMenuItems.length,
|
||
|
item;
|
||
|
|
||
|
for ( var i = 0; i < mobMenuItemsCount; i++ ) {
|
||
|
item = mobMenuItems[i];
|
||
|
|
||
|
item.addEventListener(
|
||
|
'click',
|
||
|
function () {
|
||
|
toggleButton.click();
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
}()
|
||
|
);
|
||
|
|