navigation.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /**
  2. * File navigation.js
  3. *
  4. * Handles toggling the navigation menu for small screens and enables tab
  5. * support for dropdown menus.
  6. *
  7. * @package Astra
  8. */
  9. /**
  10. * Get all of an element's parent elements up the DOM tree
  11. *
  12. * @param {Node} elem The element.
  13. * @param {String} selector Selector to match against [optional].
  14. * @return {Array} The parent elements.
  15. */
  16. var astraGetParents = function ( elem, selector ) {
  17. // Element.matches() polyfill.
  18. if ( ! Element.prototype.matches) {
  19. Element.prototype.matches =
  20. Element.prototype.matchesSelector ||
  21. Element.prototype.mozMatchesSelector ||
  22. Element.prototype.msMatchesSelector ||
  23. Element.prototype.oMatchesSelector ||
  24. Element.prototype.webkitMatchesSelector ||
  25. function(s) {
  26. var matches = (this.document || this.ownerDocument).querySelectorAll( s ),
  27. i = matches.length;
  28. while (--i >= 0 && matches.item( i ) !== this) {}
  29. return i > -1;
  30. };
  31. }
  32. // Setup parents array.
  33. var parents = [];
  34. // Get matching parent elements.
  35. for ( ; elem && elem !== document; elem = elem.parentNode ) {
  36. // Add matching parents to array.
  37. if ( selector ) {
  38. if ( elem.matches( selector ) ) {
  39. parents.push( elem );
  40. }
  41. } else {
  42. parents.push( elem );
  43. }
  44. }
  45. return parents;
  46. };
  47. /**
  48. * Deprecated: Get all of an element's parent elements up the DOM tree
  49. *
  50. * @param {Node} elem The element.
  51. * @param {String} selector Selector to match against [optional].
  52. * @return {Array} The parent elements.
  53. */
  54. var getParents = function ( elem, selector ) {
  55. console.warn( 'getParents() function has been deprecated since version 2.5.0 or above of Astra Theme and will be removed in the future. Use astraGetParents() instead.' );
  56. astraGetParents( elem, selector );
  57. }
  58. /**
  59. * Toggle Class funtion
  60. *
  61. * @param {Node} elem The element.
  62. * @param {String} selector Selector to match against [optional].
  63. * @return {Array} The parent elements.
  64. */
  65. var astraToggleClass = function ( el, className ) {
  66. if ( el.classList.contains( className ) ) {
  67. el.classList.remove( className );
  68. } else {
  69. el.classList.add( className );
  70. }
  71. };
  72. /**
  73. * Deprecated: Toggle Class funtion
  74. *
  75. * @param {Node} elem The element.
  76. * @param {String} selector Selector to match against [optional].
  77. * @return {Array} The parent elements.
  78. */
  79. var toggleClass = function ( el, className ) {
  80. console.warn( 'toggleClass() function has been deprecated since version 2.5.0 or above of Astra Theme and will be removed in the future. Use astraToggleClass() instead.' );
  81. astraToggleClass( el, className );
  82. };
  83. // CustomEvent() constructor functionality in Internet Explorer 9 and higher.
  84. (function () {
  85. if (typeof window.CustomEvent === "function") return false;
  86. function CustomEvent(event, params) {
  87. params = params || { bubbles: false, cancelable: false, detail: undefined };
  88. var evt = document.createEvent('CustomEvent');
  89. evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
  90. return evt;
  91. }
  92. CustomEvent.prototype = window.Event.prototype;
  93. window.CustomEvent = CustomEvent;
  94. })();
  95. /**
  96. * Trigget custom JS Event.
  97. *
  98. * @since 1.4.6
  99. *
  100. * @link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
  101. * @param {Node} el Dom Node element on which the event is to be triggered.
  102. * @param {Node} typeArg A DOMString representing the name of the event.
  103. * @param {String} A CustomEventInit dictionary, having the following fields:
  104. * "detail", optional and defaulting to null, of type any, that is an event-dependent value associated with the event.
  105. */
  106. var astraTriggerEvent = function astraTriggerEvent( el, typeArg ) {
  107. var customEventInit =
  108. arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  109. var event = new CustomEvent(typeArg, customEventInit);
  110. el.dispatchEvent(event);
  111. };
  112. ( function() {
  113. var menu_toggle_all = document.querySelectorAll( '.main-header-menu-toggle' );
  114. var menu_click_listeners = {};
  115. /* Add break point Class and related trigger */
  116. var updateHeaderBreakPoint = function () {
  117. // Content overrflowing out of screen can give incorrect window.innerWidth.
  118. // Adding overflow hidden and then calculating the window.innerWidth fixes the problem.
  119. var originalOverflow = document.querySelector('body').style.overflow;
  120. document.querySelector('body').style.overflow = 'hidden';
  121. var ww = window.innerWidth;
  122. document.querySelector('body').style.overflow = originalOverflow;
  123. var break_point = astra.break_point,
  124. headerWrap = document.querySelectorAll('.main-header-bar-wrap');
  125. if (headerWrap.length > 0) {
  126. for (var i = 0; i < headerWrap.length; i++) {
  127. if (headerWrap[i].tagName == 'DIV' && headerWrap[i].classList.contains('main-header-bar-wrap')) {
  128. if (ww > break_point) {
  129. //remove menu toggled class.
  130. if (null != menu_toggle_all[i]) {
  131. menu_toggle_all[i].classList.remove('toggled');
  132. }
  133. document.body.classList.remove("ast-header-break-point");
  134. document.body.classList.add("ast-desktop");
  135. astraTriggerEvent(document.body, "astra-header-responsive-enabled");
  136. } else {
  137. document.body.classList.add("ast-header-break-point");
  138. document.body.classList.remove("ast-desktop");
  139. astraTriggerEvent(document.body, "astra-header-responsive-disabled")
  140. }
  141. }
  142. }
  143. }
  144. }
  145. updateHeaderBreakPoint();
  146. AstraToggleSubMenu = function() {
  147. var parent_li = this.parentNode;
  148. if (parent_li.classList.contains('ast-submenu-expanded') && document.querySelector("header.site-header").classList.contains("ast-menu-toggle-link")) {
  149. if (!this.classList.contains('ast-menu-toggle')) {
  150. var link = parent_li.querySelector('a').getAttribute('href');
  151. if ('' !== link || '#' !== link) {
  152. window.location = link;
  153. }
  154. }
  155. }
  156. var parent_li_child = parent_li.querySelectorAll('.menu-item-has-children');
  157. for (var j = 0; j < parent_li_child.length; j++) {
  158. parent_li_child[j].classList.remove('ast-submenu-expanded');
  159. var parent_li_child_sub_menu = parent_li_child[j].querySelector('.sub-menu, .children');
  160. if( null !== parent_li_child_sub_menu ) {
  161. parent_li_child_sub_menu.style.display = 'none';
  162. }
  163. }
  164. var parent_li_sibling = parent_li.parentNode.querySelectorAll('.menu-item-has-children');
  165. for (var j = 0; j < parent_li_sibling.length; j++) {
  166. if (parent_li_sibling[j] != parent_li) {
  167. parent_li_sibling[j].classList.remove('ast-submenu-expanded');
  168. var all_sub_menu = parent_li_sibling[j].querySelectorAll('.sub-menu');
  169. for (var k = 0; k < all_sub_menu.length; k++) {
  170. all_sub_menu[k].style.display = 'none';
  171. }
  172. }
  173. }
  174. if (parent_li.classList.contains('menu-item-has-children') ) {
  175. astraToggleClass(parent_li, 'ast-submenu-expanded');
  176. if (parent_li.classList.contains('ast-submenu-expanded')) {
  177. parent_li.querySelector('.sub-menu').style.display = 'block';
  178. } else {
  179. parent_li.querySelector('.sub-menu').style.display = 'none';
  180. }
  181. }
  182. };
  183. AstraNavigationMenu = function( parentList ) {
  184. console.warn( 'AstraNavigationMenu() function has been deprecated since version 1.6.5 or above of Astra Theme and will be removed in the future.' );
  185. };
  186. AstraToggleMenu = function( astra_menu_toggle ) {
  187. console.warn('AstraToggleMenu() function has been deprecated since version 1.6.5 or above of Astra Theme and will be removed in the future. Use AstraToggleSubMenu() instead.');
  188. // Add Eventlisteners for Submenu.
  189. if (astra_menu_toggle.length > 0) {
  190. for (var i = 0; i < astra_menu_toggle.length; i++) {
  191. astra_menu_toggle[i].addEventListener('click', AstraToggleSubMenu, false);
  192. }
  193. }
  194. };
  195. AstraToggleSetup = function () {
  196. var __main_header_all = document.querySelectorAll('.main-header-bar-navigation');
  197. if (menu_toggle_all.length > 0) {
  198. for (var i = 0; i < menu_toggle_all.length; i++) {
  199. menu_toggle_all[i].setAttribute('data-index', i);
  200. if ( ! menu_click_listeners[i] ) {
  201. menu_click_listeners[i] = menu_toggle_all[i];
  202. menu_toggle_all[i].addEventListener('click', astraNavMenuToggle, false);
  203. }
  204. if ('undefined' !== typeof __main_header_all[i]) {
  205. if (document.querySelector("header.site-header").classList.contains("ast-menu-toggle-link")) {
  206. var astra_menu_toggle = __main_header_all[i].querySelectorAll('.ast-header-break-point .main-header-menu .menu-item-has-children > .menu-link, .ast-header-break-point ul.main-header-menu .ast-menu-toggle');
  207. } else {
  208. var astra_menu_toggle = __main_header_all[i].querySelectorAll('ul.main-header-menu .ast-menu-toggle');
  209. }
  210. // Add Eventlisteners for Submenu.
  211. if (astra_menu_toggle.length > 0) {
  212. for (var j = 0; j < astra_menu_toggle.length; j++) {
  213. astra_menu_toggle[j].addEventListener('click', AstraToggleSubMenu, false);
  214. }
  215. }
  216. }
  217. }
  218. }
  219. };
  220. astraNavMenuToggle = function ( event ) {
  221. event.preventDefault();
  222. var __main_header_all = document.querySelectorAll('.main-header-bar-navigation');
  223. var event_index = this.getAttribute('data-index');
  224. if ('undefined' === typeof __main_header_all[event_index]) {
  225. return false;
  226. }
  227. var menuHasChildren = __main_header_all[event_index].querySelectorAll('.menu-item-has-children');
  228. for (var i = 0; i < menuHasChildren.length; i++) {
  229. menuHasChildren[i].classList.remove('ast-submenu-expanded');
  230. var menuHasChildrenSubMenu = menuHasChildren[i].querySelectorAll('.sub-menu');
  231. for (var j = 0; j < menuHasChildrenSubMenu.length; j++) {
  232. menuHasChildrenSubMenu[j].style.display = 'none';
  233. }
  234. }
  235. var menu_class = this.getAttribute('class') || '';
  236. if ( menu_class.indexOf('main-header-menu-toggle') !== -1 ) {
  237. astraToggleClass(__main_header_all[event_index], 'toggle-on');
  238. astraToggleClass(menu_toggle_all[event_index], 'toggled');
  239. if (__main_header_all[event_index].classList.contains('toggle-on')) {
  240. __main_header_all[event_index].style.display = 'block';
  241. document.body.classList.add("ast-main-header-nav-open");
  242. } else {
  243. __main_header_all[event_index].style.display = '';
  244. document.body.classList.remove("ast-main-header-nav-open");
  245. }
  246. }
  247. };
  248. document.body.addEventListener("astra-header-responsive-enabled", function () {
  249. var __main_header_all = document.querySelectorAll('.main-header-bar-navigation');
  250. if (__main_header_all.length > 0) {
  251. for (var i = 0; i < __main_header_all.length; i++) {
  252. if (null != __main_header_all[i]) {
  253. __main_header_all[i].classList.remove('toggle-on');
  254. __main_header_all[i].style.display = '';
  255. }
  256. var sub_menu = __main_header_all[i].getElementsByClassName('sub-menu');
  257. for (var j = 0; j < sub_menu.length; j++) {
  258. sub_menu[j].style.display = '';
  259. }
  260. var child_menu = __main_header_all[i].getElementsByClassName('children');
  261. for (var k = 0; k < child_menu.length; k++) {
  262. child_menu[k].style.display = '';
  263. }
  264. var searchIcons = __main_header_all[i].getElementsByClassName('ast-search-menu-icon');
  265. for (var l = 0; l < searchIcons.length; l++) {
  266. searchIcons[l].classList.remove('ast-dropdown-active');
  267. searchIcons[l].style.display = '';
  268. }
  269. }
  270. }
  271. }, false);
  272. window.addEventListener('resize', function () {
  273. // Skip resize event when keyboard display event triggers on devices.
  274. if( 'INPUT' !== document.activeElement.tagName ) {
  275. updateHeaderBreakPoint();
  276. AstraToggleSetup();
  277. }
  278. });
  279. document.addEventListener('DOMContentLoaded', function () {
  280. AstraToggleSetup();
  281. /**
  282. * Navigation Keyboard Navigation.
  283. */
  284. var container, count;
  285. container = document.querySelectorAll( '.navigation-accessibility' );
  286. for ( count = 0; count <= container.length - 1; count++ ) {
  287. if ( container[count] ) {
  288. navigation_accessibility( container[count] );
  289. }
  290. }
  291. });
  292. var get_browser = function () {
  293. var ua = navigator.userAgent,tem,M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
  294. if(/trident/i.test(M[1])) {
  295. tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
  296. return;
  297. }
  298. if( 'Chrome' === M[1] ) {
  299. tem = ua.match(/\bOPR|Edge\/(\d+)/)
  300. if(tem != null) {
  301. return;
  302. }
  303. }
  304. M = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
  305. if((tem = ua.match(/version\/(\d+)/i)) != null) {
  306. M.splice(1,1,tem[1]);
  307. }
  308. var bodyElement = document.body;
  309. if( 'Safari' === M[0] && M[1] < 11 ) {
  310. bodyElement.classList.add( "ast-safari-browser-less-than-11" );
  311. }
  312. }
  313. get_browser();
  314. /* Search Script */
  315. var SearchIcons = document.getElementsByClassName( 'astra-search-icon' );
  316. for (var i = 0; i < SearchIcons.length; i++) {
  317. SearchIcons[i].onclick = function(event) {
  318. if ( this.classList.contains( 'slide-search' ) ) {
  319. event.preventDefault();
  320. var sibling = this.parentNode.parentNode.parentNode.querySelector( '.ast-search-menu-icon' );
  321. if ( ! sibling.classList.contains( 'ast-dropdown-active' ) ) {
  322. sibling.classList.add( 'ast-dropdown-active' );
  323. sibling.querySelector( '.search-field' ).setAttribute('autocomplete','off');
  324. setTimeout(function() {
  325. sibling.querySelector( '.search-field' ).focus();
  326. },200);
  327. } else {
  328. var searchTerm = sibling.querySelector( '.search-field' ).value || '';
  329. if( '' !== searchTerm ) {
  330. sibling.querySelector( '.search-form' ).submit();
  331. }
  332. sibling.classList.remove( 'ast-dropdown-active' );
  333. }
  334. }
  335. }
  336. }
  337. /* Hide Dropdown on body click*/
  338. document.body.onclick = function( event ) {
  339. if ( typeof event.target.classList !== 'undefined' ) {
  340. if ( ! event.target.classList.contains( 'ast-search-menu-icon' ) && astraGetParents( event.target, '.ast-search-menu-icon' ).length === 0 && astraGetParents( event.target, '.ast-search-icon' ).length === 0 ) {
  341. var dropdownSearchWrap = document.getElementsByClassName( 'ast-search-menu-icon' );
  342. for (var i = 0; i < dropdownSearchWrap.length; i++) {
  343. dropdownSearchWrap[i].classList.remove( 'ast-dropdown-active' );
  344. }
  345. }
  346. }
  347. }
  348. /**
  349. * Navigation Keyboard Navigation.
  350. */
  351. function navigation_accessibility( container ) {
  352. if ( ! container ) {
  353. return;
  354. }
  355. var button = container.getElementsByTagName( 'button' )[0];
  356. if ( 'undefined' === typeof button ) {
  357. button = container.getElementsByTagName( 'a' )[0];
  358. if ( 'undefined' === typeof button ) {
  359. return;
  360. }
  361. }
  362. var menu = container.getElementsByTagName( 'ul' )[0];
  363. // Hide menu toggle button if menu is empty and return early.
  364. if ( 'undefined' === typeof menu ) {
  365. button.style.display = 'none';
  366. return;
  367. }
  368. menu.setAttribute( 'aria-expanded', 'false' );
  369. if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
  370. menu.className += ' nav-menu';
  371. }
  372. button.onclick = function() {
  373. if ( -1 !== container.className.indexOf( 'toggled' ) ) {
  374. container.className = container.className.replace( ' toggled', '' );
  375. button.setAttribute( 'aria-expanded', 'false' );
  376. menu.setAttribute( 'aria-expanded', 'false' );
  377. } else {
  378. container.className += ' toggled';
  379. button.setAttribute( 'aria-expanded', 'true' );
  380. menu.setAttribute( 'aria-expanded', 'true' );
  381. }
  382. };
  383. // Get all the link elements within the menu.
  384. var links = menu.getElementsByTagName( 'a' );
  385. var subMenus = menu.getElementsByTagName( 'ul' );
  386. // Set menu items with submenus to aria-haspopup="true".
  387. for ( var i = 0, len = subMenus.length; i < len; i++ ) {
  388. subMenus[i].parentNode.setAttribute( 'aria-haspopup', 'true' );
  389. }
  390. // Each time a menu link is focused or blurred, toggle focus.
  391. for ( i = 0, len = links.length; i < len; i++ ) {
  392. links[i].addEventListener( 'focus', toggleFocus, true );
  393. links[i].addEventListener( 'blur', toggleBlurFocus, true );
  394. links[i].addEventListener( 'click', toggleClose, true );
  395. }
  396. }
  397. /**
  398. * Close the Toggle Menu on Click on hash (#) link.
  399. *
  400. * @since 1.3.2
  401. * @return void
  402. */
  403. function toggleClose( )
  404. {
  405. var self = this || '',
  406. hash = '#';
  407. if( self && ! self.classList.contains('astra-search-icon') ) {
  408. var link = new String( self );
  409. if( link.indexOf( hash ) !== -1 ) {
  410. var link_parent = self.parentNode;
  411. if ( document.body.classList.contains('ast-header-break-point') && ! ( document.querySelector("header.site-header").classList.contains("ast-menu-toggle-link") && link_parent.classList.contains("menu-item-has-children") ) ) {
  412. /* Close Main Header Menu */
  413. var main_header_menu_toggle = document.querySelector( '.main-header-menu-toggle' );
  414. main_header_menu_toggle.classList.remove( 'toggled' );
  415. var main_header_bar_navigation = document.querySelector( '.main-header-bar-navigation' );
  416. main_header_bar_navigation.classList.remove( 'toggle-on' );
  417. main_header_bar_navigation.style.display = 'none';
  418. /* Close Below Header Menu */
  419. var before_header_menu_toggle = document.querySelector( '.menu-below-header-toggle' );
  420. var before_header_bar_navigation = document.querySelector( '.ast-below-header' );
  421. var before_header_bar = document.querySelector( '.ast-below-header-actual-nav' );
  422. if ( before_header_menu_toggle && before_header_bar_navigation && before_header_bar ) {
  423. before_header_menu_toggle.classList.remove( 'toggled' );
  424. before_header_bar_navigation.classList.remove( 'toggle-on' );
  425. before_header_bar.style.display = 'none';
  426. }
  427. /* Close After Header Menu */
  428. var after_header_menu_toggle = document.querySelector( '.menu-above-header-toggle' );
  429. var after_header_bar_navigation = document.querySelector( '.ast-above-header' );
  430. var after_header_bar = document.querySelector( '.ast-above-header-navigation' );
  431. if ( after_header_menu_toggle && after_header_bar_navigation && after_header_bar ) {
  432. after_header_menu_toggle.classList.remove( 'toggled' );
  433. after_header_bar_navigation.classList.remove( 'toggle-on' );
  434. after_header_bar.style.display = 'none';
  435. }
  436. astraTriggerEvent( document.querySelector('body'), 'astraMenuHashLinkClicked' );
  437. } else {
  438. while ( -1 === self.className.indexOf( 'nav-menu' ) ) {
  439. // On li elements toggle the class .focus.
  440. if ( 'li' === self.tagName.toLowerCase() ) {
  441. if ( -1 !== self.className.indexOf( 'focus' ) ) {
  442. self.className = self.className.replace( ' focus', '' );
  443. }
  444. }
  445. self = self.parentElement;
  446. }
  447. }
  448. }
  449. }
  450. }
  451. /**
  452. * Sets or removes .focus class on an element on focus.
  453. */
  454. function toggleFocus() {
  455. var self = this;
  456. // Move up through the ancestors of the current link until we hit .nav-menu.
  457. while ( -1 === self.className.indexOf( 'nav-menu' ) ) {
  458. // On li elements toggle the class .focus.
  459. if ( 'li' === self.tagName.toLowerCase() ) {
  460. if ( -1 !== self.className.indexOf( 'focus' ) ) {
  461. self.className = self.className.replace( ' focus', '' );
  462. } else {
  463. self.className += ' focus';
  464. }
  465. }
  466. self = self.parentElement;
  467. }
  468. }
  469. /**
  470. * Sets or removes .focus class on an element on blur.
  471. */
  472. function toggleBlurFocus() {
  473. var self = this || '',
  474. hash = '#';
  475. var link = new String( self );
  476. if( link.indexOf( hash ) !== -1 && document.body.classList.contains('ast-mouse-clicked') ) {
  477. return;
  478. }
  479. // Move up through the ancestors of the current link until we hit .nav-menu.
  480. while ( -1 === self.className.indexOf( 'nav-menu' ) ) {
  481. // On li elements toggle the class .focus.
  482. if ( 'li' === self.tagName.toLowerCase() ) {
  483. if ( -1 !== self.className.indexOf( 'focus' ) ) {
  484. self.className = self.className.replace( ' focus', '' );
  485. } else {
  486. self.className += ' focus';
  487. }
  488. }
  489. self = self.parentElement;
  490. }
  491. }
  492. /* Add class if mouse clicked and remove if tab pressed */
  493. if ( 'querySelector' in document && 'addEventListener' in window ) {
  494. var body = document.body;
  495. body.addEventListener( 'mousedown', function() {
  496. body.classList.add( 'ast-mouse-clicked' );
  497. } );
  498. body.addEventListener( 'keydown', function() {
  499. body.classList.remove( 'ast-mouse-clicked' );
  500. } );
  501. }
  502. } )();