1// Code for Acessible Main menu 2 3jQuery(document).ready(function($) { 4//adding outline on menu if meniitems focused 5 $('.menuWrapper nav') 6 .on('focusin', function () { 7 $(this).addClass('menu-focused'); 8 }) 9 .on('focusout', function () { 10 let $this = $(this); 11 setTimeout(function () { 12 if (!$this.find(':focus').length) { 13 $this.removeClass('menu-focused'); 14 } 15 }, 10); 16 }); 17 18 19 $('.menuWrapper nav ul ul.sub-menu').each(function () { 20 const $submenu = $(this); 21 const $parentLi = $submenu.closest('li'); // parent <li> with sub-menu 22 const $parentLink = $parentLi.children('a').first(); 23 24 if ($parentLink.length) { 25 const linkText = $.trim($parentLink.clone().children().remove().end().text()); 26 if (linkText !== '') { 27 $submenu.attr('aria-label', linkText + ' submenu'); 28 } 29 } 30 }); 31 32 function updateAriaExpanded($li, expanded) { 33 const $link = $li.children("a"); 34 if ($li.children("ul.sub-menu").length) { 35 $link.attr("aria-expanded", expanded ? "true" : "false"); 36 } else { 37 $link.removeAttr("aria-expanded"); 38 } 39 } 40 41 function openMenu() { 42 if ($("body").hasClass("rtl")) { 43 $("#overflowMenu").animate({ left: "0px" }); 44 } else { 45 $("#overflowMenu").animate({ right: "0px" }); 46 } 47 $('#overflowMenu ul').find(' > li > a').attr('tabindex', 0); 48 $('#overflowMenu a.closeMenu').attr('tabindex', 0); 49 } 50 51 52 function applyAriaRoles() { 53 const $mainMenu = $(".menuWrapper nav > ul"); 54 55 $mainMenu.attr({ "role": "menubar"}); 56 57 const $topLevelLinks = $mainMenu.find("> li > a"); 58 59 $topLevelLinks.each(function(index) { 60 const $link = $(this); 61 $link.attr("role", "menuitem"); 62 $link.attr("tabindex", index === 0 ? "0" : "-1"); 63 64 const $parentLi = $link.closest("li"); 65 $parentLi.attr("role", "none"); 66 67 if ($parentLi.children("ul.sub-menu").length) { 68 $link.attr("aria-haspopup", "true"); 69 updateAriaExpanded($parentLi, false); 70 } else { 71 $link.removeAttr("aria-haspopup aria-expanded"); 72 } 73 }); 74 75 $(".menuWrapper nav ul ul.sub-menu").attr("role", "menu"); 76 $(".menuWrapper nav ul ul.sub-menu > li").attr("role", "none"); 77 $(".menuWrapper nav ul ul.sub-menu > li > a").attr("role", "menuitem").attr("tabindex", "-1"); // set all submenu items to -1 initially 78 } 79 80 applyAriaRoles(); 81 82 83 84 function closeSubMenu($li) { 85 $li.find("> ul.sub-menu").slideUp().attr("aria-hidden", "true"); 86 updateAriaExpanded($li, false); 87 $li.find("> ul.sub-menu > li > a").attr("tabIndex", -1); 88 $li.find("> ul.sub-menu > li > ul.sub-menu").slideUp().attr("aria-hidden", "true"); 89 $li.find("> ul.sub-menu > li > a").each(function() { 90 updateAriaExpanded($(this).parent(), false); 91 }); 92 } 93 94 function openSubMenu($li, focusLast = false, focusFirst = false) { 95 $li.find("> ul.sub-menu").slideDown().attr("aria-hidden", "false"); 96 updateAriaExpanded($li, true); 97 $li.find("> ul.sub-menu > li > a").attr("tabIndex", 0); 98 if ($li.closest("ul.sub-menu").length > 0) { 99 $li.find("> ul.sub-menu").css({ display: "block", right: "-250px" }); 100 } 101 if (focusLast) { 102 $li.find("> ul.sub-menu > li:last > a").focus(); 103 } else if (focusFirst) { 104 $li.find("> ul.sub-menu > li:first > a").focus(); 105 } 106 } 107 108 function onMenuItemClick(event) { 109 const $submenu = $(this).children("ul.sub-menu"); 110 const $link = $(this).children("a"); 111 if ($submenu.length) { 112 $(this).siblings("li").find("ul.sub-menu").slideUp().attr("aria-hidden", "true") 113 .siblings("a").each(function() { 114 updateAriaExpanded($(this).parent(), false); 115 }); 116 117 const isVisible = $submenu.is(":visible"); 118 // $submenu.slideToggle(); 119 $submenu.attr("aria-hidden", isVisible ? "true" : "false"); 120 updateAriaExpanded($(this), !isVisible); 121 $submenu.find("> li > a").attr("tabIndex", isVisible ? -1 : 0); 122 if ($(this).hasClass("has-sub-child")) { 123 $submenu.css('left', '100%'); 124 } 125 event.stopPropagation(); 126 } 127 } 128 129 $(".menuWrapper nav ul li").each(function () { 130 if ($(this).children("ul.sub-menu").length) { 131 $(this).on("click", onMenuItemClick); 132 } 133 }); 134 135 136 //Keyboard Navigation Handler for Main Menu, Overflow Menu and Submenus 137 138 $(document).on("keydown", function(event) { 139 const $currentElement = $(document.activeElement); 140 141 const $menuWrapper = $(".menuWrapper nav, .moreNav"); 142 if ($menuWrapper.has($currentElement).length === 0) { 143 return; 144 } 145 146 const isMainMenuItem = 147 $currentElement.closest(".menuWrapper nav > ul > li").length > 0 || 148 $currentElement.closest(".moreNav").length > 0; 149 150 const isInSubmenu = $currentElement.closest("ul.sub-menu").length > 0; 151 152 const $mainMenuItems = $(".menuWrapper nav > ul > li > a:visible, .moreNav > a:visible").filter(function() { 153 return $(this).closest("li").parents("ul").length === 1; 154 }); 155 156 const currentIndex = $mainMenuItems.get().indexOf($currentElement[0]); 157 158 const $parentLi = $currentElement.closest("li"); 159 const $submenuItems = isInSubmenu ? $currentElement.closest("ul.sub-menu").find("> li > a") : []; 160 const submenuIndex = isInSubmenu ? $submenuItems.index($currentElement) : -1; 161 162 // ENTER / SPACE 163 if (event.which === 13 || event.which === 32) { 164 event.preventDefault(); 165 if ($parentLi.children("ul.sub-menu").length) { 166 openSubMenu($parentLi, false, true); 167 // $parentLi.children("a").attr("aria-expanded", "true"); 168 } else { 169 window.location.href = $currentElement.attr("href"); 170 // $parentLi.children("a").attr("aria-expanded", "false"); 171 } 172 } 173 174 175 // LEFT / RIGHT / UP / DOWN / HOME / END / ALPHABETICAL KEYS 176 else if ([37, 38, 39, 40, 36, 35].includes(event.which) || (event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key))) { 177 event.preventDefault(); 178 179 if (event.which === 37) { // LEFT 180 if (!isInSubmenu && isMainMenuItem) { 181 const prevIndex = currentIndex > 0 ? currentIndex - 1 : $mainMenuItems.length - 1; 182 $mainMenuItems.eq(prevIndex).focus(); 183 } else if (isInSubmenu) { 184 const $parentMenuItem = $currentElement.closest("ul.sub-menu").parent("li"); 185 closeSubMenu($parentMenuItem); 186 $parentMenuItem.find("> a").focus().attr("aria-expanded", "false"); 187 } 188 } 189 190 191 else if (event.which === 39) { // RIGHT 192 let $currentLi = $currentElement.closest("li"); 193 194 // Case 1: On a submenu item that has its own nested submenu 195 if (isInSubmenu) { 196 let $nestedSubmenu = $currentLi.children("ul.sub-menu"); 197 if ($nestedSubmenu.length > 0) { 198 // Open nested submenu 199 $nestedSubmenu.attr("aria-hidden", "false").slideDown().css({ display: "block", right: "-250px" }); 200 $currentLi.children("a").attr("aria-expanded", "true"); 201 202 // Focus first item in nested submenu 203 $nestedSubmenu.find('a[role="menuitem"]:visible').first().focus(); 204 } else { 205 // If no nested submenu, jump to next top-level menu item 206 let $topParentLi = $currentLi; 207 while ($topParentLi.closest("ul.sub-menu").length) { 208 $topParentLi = $topParentLi.closest("ul.sub-menu").parent("li"); 209 } 210 211 // Close all open submenus of this top-level item 212 $topParentLi.find("ul.sub-menu").slideUp().attr("aria-hidden", "true"); 213 $topParentLi.find("a[aria-expanded='true']").attr("aria-expanded", "false"); 214 215 const parentIndex = $mainMenuItems.index($topParentLi.find("> a")); 216 const nextIndex = parentIndex < $mainMenuItems.length - 1 ? parentIndex + 1 : 0; 217 $mainMenuItems.eq(nextIndex).focus(); 218 } 219 } 220 221 // Case 2: On a main menu item (not in submenu) 222 else if (!isInSubmenu && isMainMenuItem) { 223 const nextIndex = currentIndex < $mainMenuItems.length - 1 ? currentIndex + 1 : 0; 224 $mainMenuItems.eq(nextIndex).focus(); 225 } 226 } 227 228 229 230 231 else if (event.which === 38) { // UP 232 233 if (isInSubmenu) { 234 const newIndex = submenuIndex > 0 ? submenuIndex - 1 : $submenuItems.length - 1; 235 $submenuItems.eq(newIndex).focus(); 236 if ($parentLi.children("ul.sub-menu").length) { 237 $parentLi.children("a").attr("aria-expanded", "true"); 238 } 239 } else if ($parentLi.children("ul.sub-menu").length) { 240 openSubMenu($parentLi, true); 241 } 242 243 } 244 245 else if (event.which === 40) { // DOWN 246 247 if (isInSubmenu) { 248 const newIndex = submenuIndex < $submenuItems.length - 1 ? submenuIndex + 1 : 0; 249 $submenuItems.eq(newIndex).focus(); 250 if ($parentLi.children("ul.sub-menu").length) { 251 $parentLi.children("a").attr("aria-expanded", "true"); 252 } 253 } else if ($parentLi.children("ul.sub-menu").length) { 254 openSubMenu($parentLi, false, true); 255 } 256 257 } 258 259 260 else if (event.which === 36) { // HOME 261 if (isInSubmenu) { 262 $submenuItems.eq(0).focus(); 263 } else { 264 $mainMenuItems.eq(0).focus(); 265 } 266 } else if (event.which === 35) { // END 267 if (isInSubmenu) { 268 $submenuItems.eq($submenuItems.length - 1).focus(); 269 } else { 270 $mainMenuItems.eq($mainMenuItems.length - 1).focus(); 271 } 272 } 273 274 else if (event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) { // First-char nav 275 const searchChar = event.key.toLowerCase(); 276 const items = isInSubmenu ? $submenuItems : $mainMenuItems; 277 let startIndex = isInSubmenu ? submenuIndex + 1 : currentIndex + 1; 278 if (startIndex >= items.length) startIndex = 0; 279 for (let i = 0; i < items.length; i++) {
280 const index = (startIndex + i) % items.length; 281 const itemText = items.eq(index).text().trim().toLowerCase(); 282 if (itemText.startsWith(searchChar)) { 283 items.eq(index).focus(); 284 break; 285 } 286 } 287 } 288 } 289 290 291 }); 292 293 294 $('html').on('click', function(event) { 295 // If clicked outside both main menu and overflow menu 296 if (!$(event.target).closest('.menuWrapper, #overflowMenu').length) { 297 $(".menuWrapper nav ul li ul.sub-menu").each(function () { 298 $(this) 299 .slideUp() 300 .attr("aria-hidden", "true") 301 .find("> li > a") 302 .attr("tabindex", -1); 303 }); 304 $(".menuWrapper nav ul li:has(ul.sub-menu) > a").attr("aria-expanded", "false"); 305 $("#overflowMenu .ofMenu ul li ul.sub-menu").each(function () { 306 $(this) 307 .slideUp() 308 .attr("aria-hidden", "true") 309 .find("> li > a") 310 .attr("tabindex", -1); 311 }); 312 $("#overflowMenu .ofMenu ul li:has(ul.sub-menu) > a").attr("aria-expanded", "false"); 313 } 314 }); 315 316 317 $('.menu').on('click', function(event) { 318 event.stopPropagation(); 319 }); 320 321 322 $(".menuWrapper nav ul li").each(function () { 323 if ($(this).children("ul.sub-menu").length) { 324 $(this).on("click", onMenuItemClick); 325 } 326}); 327 328 $("nav li li").mouseover(function() { 329 if ($(this).children("ul.sub-menu").length == 1) { 330 let parent = $(this); 331 let child_menu = $(this).children("ul.sub-menu"); 332 if ( 333 $(parent).offset().left + $(parent).width() + $(child_menu).width() > 334 $(window).width() 335 ) { 336 $(child_menu).css("left", "-" + $(parent).width() + "px"); 337 } else { 338 $(child_menu).css("right", "-" + $(parent).width() + "px"); 339 } 340 } 341 }); 342 343 /***** Overflow Navigation Setup *****/ 344 345 if ($(window).innerWidth() >= 640) { 346 let menuContanerWidth = $(".menuWrapper .container").width(), 347 itemsTotalWith = 0; 348 $(".menu > ul > li").each(function(s, a) { 349 let itemClone = $(this).clone(); 350 itemClone.find('a').attr("tabindex", -1); 351 if ( 352 ((itemsTotalWith += $(this).width()), 353 itemsTotalWith > menuContanerWidth - 100 && 354 ($(this).hasClass("moreNav") || 355 ($(this).remove(), 356 $("#overflowMenu .ofMenu > ul").append(itemClone)), 357 !($(".menu ul .moreNav").length > 0))) 358 ) { 359 let n = $( 360 '<li class="moreNav" role="none">' + 361 '<a href="javascript:void(0)" role="menuitem" aria-label="More Menu" title="More Menu"><span class="icon-menu" aria-hidden="true"></span><span class="hide">' + 362 $(".menuMoreText").html() + 363 "</span></a></li>" 364 ); 365 $(".menuMoreText").html(""); 366 $(".menu>ul").append(n); 367 n.click(openMenu); 368 } 369 }); 370 } 371 372 // // More Button 373 $(".moreNav a").on("click", function(event) { 374 event.preventDefault(), event.stopPropagation(); 375 openMenu(); 376 }); 377 378 379 $('#overflowMenu').click(function(event) { 380 event.stopPropagation(); 381 }); 382 383 384 $(".ofMenu ul li").on("click", onMenuItemClick); 385 386 $('.ofMenu ul li').hover(function(e) { 387 $(this).siblings().children('ul.sub-menu').slideUp(); 388 $(this).children('ul.sub-menu').slideDown(); 389 }, function() { 390 $(this).parent().siblings().children('ul.sub-menu').slideUp(); 391 }); 392 393 /***** Responsive Navigation Setup *****/ 394 395 $(".menuToggle").click(function(event) { 396 event.preventDefault(); 397 $(".menuWrapper").stop().slideToggle(); 398 }); 399 400 if ($(window).width() <= 639) { 401 $(".menu ul li").each(function(index, element) { 402 $(this).off('click'); 403 if ($(this).children("ul").length) { 404 $(".indicator").hide(); 405 $("<a href='javascript:void(0)' aria-label='Click to expand submenu' class='menu-toggle' role='button'> </a>") 406 .insertAfter($(element).find(" > a ")); 407 } 408 }); 409 410 } 411 412 413 $(function() { 414 $(".menu-toggle").each(function() { 415 var menuTitle = $(this).closest("li").find("> a").text().trim(); 416 var expandText = S3WaaSAccessibilityParams.clickToExpand; 417 var submenuWord = S3WaaSAccessibilityParams.submenu; 418 419 $(this) 420 .attr("aria-label", expandText + " " + menuTitle + " " + submenuWord) 421 .attr("aria-expanded", "false"); 422 }); 423 }); 424 425 // On click on menutoggle update aria-labels
426 $(".menu-toggle").click(function(event) { 427 event.preventDefault(); 428 $(this).toggleClass("open").parents('li').siblings().children("ul.sub-menu").stop().slideUp('fast'); 429 $(this).next("ul.sub-menu").slideToggle('fast'); 430 431 $(this).parents('li').siblings().children("a").removeClass("open"); 432 $(this).parents('li').siblings().children("a.menu-select").removeClass("menu-select"); 433 $(this).prev().toggleClass("menu-select"); 434 435 var menuTitle = $(this).closest("li").find("> a").text().trim(); 436 var collapseText = S3WaaSAccessibilityParams.clickToCollapse; 437 var expandText = S3WaaSAccessibilityParams.clickToExpand; 438 var submenuWord = S3WaaSAccessibilityParams.submenu; 439 440 if ($(this).hasClass("open")) { 441 $(this).attr("aria-label", collapseText + " " + menuTitle + " " + submenuWord).attr("aria-expanded", "true"); 442 } else { 443 $(this).attr("aria-label", expandText + " " + menuTitle + " " + submenuWord).attr("aria-expanded", "false"); 444 } 445 }); 446 447 jQuery(".menuWrapper nav ul li ul.sub-menu > li > a, #overflowMenu .ofMenu ul li ul.submenu > li > a") 448 .on("keydown", function(event) { 449 const $submenu = $(this).closest("ul.sub-menu"); 450 const $submenuItems = $submenu.find("> li > a"); 451 const currentIndex = $submenuItems.index(this); 452 453 // Trap focus within submenu 454 if (event.which === 9) { // Tab key 455 if (!event.shiftKey && currentIndex === $submenuItems.length - 1) { 456 event.preventDefault(); 457 $submenuItems.eq(0).focus(); 458 } else if (event.shiftKey && currentIndex === 0) { 459 event.preventDefault(); 460 $submenuItems.eq($submenuItems.length - 1).focus(); 461 } 462 } 463 464 if (event.which === 27) { // Escape key 465 const $parentLi = $submenu.parent("li"); 466 closeSubMenu($parentLi); 467 $parentLi.find("> a").focus().attr('aria-expanded', 'false'); 468 event.preventDefault(); 469 470 } 471 472 }); 473 474 //if no submenu is open and esc key pressed move focus to first focusable element in main 475 jQuery(".menuWrapper nav > ul > li > a").on("keydown", function(event) { 476 if (event.which === 27) { 477 const $levelFirstMenu = jQuery(this).closest("ul.sub-menu"); 478 479 if ($levelFirstMenu.length === 0) { 480 const $firstMainLink = jQuery("main a:visible").first(); 481 if ($firstMainLink.length) { 482 $firstMainLink.focus(); 483 } 484 event.preventDefault(); 485 } 486 } 487 }); 488}); 489// js to close submenu on ESC key press if submenu is already opened using mouse 490jQuery(function ($) { 491 492 $(document).on('keydown', function (e) { 493 if (e.key === 'Escape') { 494 495 const $nav = $('.menuWrapper nav, #accessibilityMenu').addClass('esc-off'); 496 setTimeout(() => $nav.removeClass('esc-off'), 200); 497 } 498 }); 499 500}); 501 502 503//overflow menu extra js for accessibility 504 505jQuery(document).ready(function($) { 506 const $ov = $('#overflowMenu'); 507 const ovWidth = $ov.width(); 508 509 // Ensure menu is collapsed on load 510 $('.moreNav > a').attr('aria-expanded', 'false'); 511 $ov.hide().css('right', -ovWidth); 512 $ov.find('.sub-menu').hide().attr('aria-hidden', 'true'); 513 $ov.find('a').attr('tabindex', -1); 514 515 const $toggleButton = $('.moreNav > a'); 516 $toggleButton.attr({ 517 'aria-haspopup': 'true', 518 'aria-expanded': 'false', 519 'aria-controls': 'overflowMenu' // connects to the popup menu 520 }); 521 $ov.attr({ 522 'role': 'dialog',
523 'aria-modal': 'true' 524 }); 525 526 function applyAttributes() { 527 $ov.find('.ofMenu > ul').attr('role', 'menu'); 528 $ov.find('li').attr('role', 'none'); 529 $ov.find('a').attr('role', 'menuitem'); 530 $ov.find('li:has(ul.sub-menu) > a') 531 .attr('aria-haspopup', 'true') 532 .attr('aria-expanded', 'false') 533 .attr('tabindex', -1); 534 $ov.find('ul.sub-menu') 535 .attr('role', 'menu') 536 .attr('aria-hidden', 'true') 537 .hide(); 538 } 539 applyAttributes(); 540 541 function trapWithin($container) { 542 const $focusables = $container.find('a:visible, button:visible').filter('[tabindex!="-1"]'); 543 const first = $focusables.first(); 544 const last = $focusables.last(); 545 546 $focusables.off('keydown.trap').on('keydown.trap', function(e) { 547 const $items = $focusables; 548 const currentIndex = $items.index(this); 549 550 if (e.key === 'Tab') { 551 if (e.shiftKey && this === first[0]) { 552 e.preventDefault(); 553 last.focus(); 554 } else if (!e.shiftKey && this === last[0]) { 555 e.preventDefault(); 556 first.focus(); 557 } 558 } 559 560 if (e.key === 'ArrowDown') { 561 e.preventDefault(); 562 const nextIndex = (currentIndex + 1) % $items.length; 563 $items.eq(nextIndex).focus(); 564 } 565 566 if (e.key === 'ArrowUp') { 567 e.preventDefault(); 568 const prevIndex = (currentIndex - 1 + $items.length) % $items.length; 569 $items.eq(prevIndex).focus(); 570 } 571 }); 572 } 573 574 function openOverflow() { 575 if ($ov.is(':visible')) return; 576 577 $toggleButton.attr('aria-expanded', 'true'); 578 579 $ov.css({ 580 right: -ovWidth, 581 display: 'block' 582 }).animate({ 583 right: 0 584 }, 400, function () { 585 $ov.find('a').attr('tabindex', 0); 586 // const $firstItem = $ov.find('.ofMenu > ul > li > a:visible').first(); 587 // $firstItem.focus(); 588 $ov.focus().css('outline','2px solid black') 589 trapWithin($ov); 590 591 announceMenuStatus('More menu expanded'); 592 }); 593 } 594 595 596 function closeOverflow() { 597 $ov.animate({ 598 right: -ovWidth 599 }, 400, function () { 600 $ov.css('display', 'none'); 601 $ov.find('a').attr('tabindex', -1); 602 $ov.find('[aria-expanded="true"]').each(function () { 603 $(this) 604 .attr('aria-expanded', 'false') 605 .next('ul.sub-menu') 606 .hide() 607 .attr('aria-hidden', 'true'); 608 }); 609 $ov.find('*').off('keydown.trap'); 610 $toggleButton.attr('aria-expanded', 'false').focus(); 611 612 announceMenuStatus('More menu collapsed'); 613 }); 614 } 615 616 function toggleSubmenu($link, open) { 617 const $sub = $link.next('.sub-menu'); 618 $link.attr('aria-expanded', open); 619 $sub.attr('aria-hidden', !open)[open ? 'slideDown' : 'slideUp'](); 620 if (open) { 621 const $first = $sub.find('a:visible').first().focus(); 622 trapWithin($sub); 623 } else { 624 $sub.find('*').off('keydown.trap'); 625 $link.focus(); 626 } 627 } 628 629 $('.moreNav > a').on('click keydown', function(e) { 630 if (e.type === 'click' || e.key === 'Enter' || e.key === ' ') { 631 e.preventDefault(); 632 openOverflow(); 633 } 634 }); 635 636 $ov.find('.closeMenu').on('click keydown', function(e) { 637 if (e.type === 'click' || e.key === 'Enter' || e.key === ' ') { 638 e.preventDefault(); 639 closeOverflow(); 640 } 641 }); 642 643 $ov.on('keydown', 'a', function(e) { 644 const key = e.which; 645 const $link = $(this); 646 const $li = $link.parent('li'); 647 const isParent = $li.children('ul.sub-menu').length > 0; 648 let $all = $ov.find('a:visible'); 649 let idx = $all.index(this); 650 651 if ([13, 32].includes(key)) { 652 e.preventDefault(); 653 if (isParent) { 654 toggleSubmenu($link, $link.attr('aria-expanded') == 'false'); 655 } else { 656 location.href = $link.attr('href'); 657 } 658 } else if (key === 27) { 659 const $visibleSubmenus = $ov.find('ul.sub-menu:visible').filter(function() { 660 return $(this).css('display') !== 'none'; 661 }); 662 663 if ($visibleSubmenus.length > 0) { 664 const $deepestSubmenu = $visibleSubmenus.sort((a, b) => { 665 return $(a).parents('ul.sub-menu').length - $(b).parents('ul.sub-menu').length; 666 }
666).last(); 667 668 const $parentLi = $deepestSubmenu.parent('li'); 669 const $parentLink = $parentLi.children('a'); 670 671 $deepestSubmenu.slideUp().attr('aria-hidden', 'true'); 672 $parentLink.attr('aria-expanded', 'false'); 673 $deepestSubmenu.find('*').off('keydown.trap'); 674 $parentLink.focus(); 675 } else { 676 closeOverflow(); 677 } 678 e.preventDefault(); 679 } else if (key === 37) { 680 e.preventDefault(); 681 const $submenu = $link.closest('ul.sub-menu'); 682 const $allMain = $ov.find('.ofMenu > ul > li > a:visible'); 683 684 if ($submenu.length > 0) { 685 const $parentLi = $submenu.parent('li'); 686 const $parentLink = $parentLi.children('a'); 687 $submenu.slideUp().attr('aria-hidden', 'true'); 688 $parentLink.attr('aria-expanded', 'false'); 689 $parentLink.focus(); 690 } else { 691 const prev = (idx - 1 + $all.length) % $all.length; 692 $all.eq(prev).focus(); 693 } 694 } else if (key === 39) { 695 e.preventDefault(); 696 const $submenu = $link.closest('ul.sub-menu'); 697 const $allMain = $ov.find('.ofMenu > ul > li > a:visible'); 698 699 if ($submenu.length > 0) { 700 const $parentLi = $submenu.parent('li'); 701 const $parentLink = $parentLi.children('a'); 702 $submenu.slideUp().attr('aria-hidden', 'true'); 703 $parentLink.attr('aria-expanded', 'false'); 704 const mainIdx = $allMain.index($parentLink); 705 const nextIdx = (mainIdx + 1) % $allMain.length; 706 $allMain.eq(nextIdx).focus(); 707 } else { 708 const next = (idx + 1) % $all.length; 709 $all.eq(next).focus(); 710 } 711 } else if (key === 36) { 712 e.preventDefault(); 713 $all.first().focus(); 714 } else if (key === 35) { 715 e.preventDefault(); 716 $all.last().focus(); 717 } 718 }); 719 720 $(document).on('click', function(e) { 721 if ($ov.is(':visible') && !$(e.target).closest('#overflowMenu, .moreNav').length) { 722 closeOverflow(); 723 } 724 }); 725 726 $(window).on('unload', function () { 727 $ov.hide().css('right', -ovWidth); 728 $ov.find('.sub-menu').hide().attr('aria-hidden', 'true'); 729 $ov.find('a').attr('tabindex', -1); 730 $ov.find('[aria-expanded="true"]').attr('aria-expanded', 'false'); 731 }); 732 733 // Append HTML in body for screen reader purpose only 734 if ($('#menu-status').length === 0) { 735 $('body').append('<div id="menu-status" role="status" aria-live="polite" style="position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;"></div>'); 736 } 737 738 // overflow menu expand/collapse message for screen reader 739 function announceMenuStatus(message) { 740 const $status = $('#menu-status'); 741 $status.text(''); // Clear first to force re-announcement 742 setTimeout(() => $status.text(message), 10); 743 } 744 745 746}); 747 748//Add aria-modal and role dialog attr on mobile menu wrapper 749jQuery(document).ready(function($) { 750 function updateMenuWrapperAttrs() { 751 if($(window).width()< 640){ 752 753 const mobileMenuOpen = $(".menuToggle").hasClass("clicked"); 754 755 $(".menuWrapper") 756 .attr("aria-hidden", mobileMenuOpen ? "false": "true") 757 } 758 else{ 759 $(".menuWrapper") 760 .removeAttr("aria-hidden") 761 } 762 763 } 764 765 updateMenuWrapperAttrs(); 766 $(window).on("resize", updateMenuWrapperAttrs ); 767 $(".menuToggle").on("click", function(e) { 768 setTimeout (updateMenuWrapperAttrs, 10) 769 }); 770 771 }); 772 773 774 775 776// code to handle menulink having link & submenu both for desktop and keyboard Navigation start 777 778 jQuery(document).ready(function ($) { 779 780 $(".menuWrapper nav li.menu-item-has-children > a").on("keydown.realLinkFix", function (e) { 781 782 const $link = $(this); 783 const href = $link.attr("href") || ""; 784 const isRealLink = href !== "#" && !href.startsWith("javascript"); 785 786 if (isRealLink && (e.key === "Enter" || e.key === " ")) { 787 e.stopImmediatePropagation(); 788 return true; 789 } 790 791 }); 792 //Menu hints text announcement for screen reader code start 793 if ($("#menu-hint-global").length === 0) { 794 $("body").append( 795 '<div id="menu-hint-global" aria-live="polite" role="status" ' + 796 'style="position:absolute;
796left:-9999px;top:-9999px;width:1px;height:1px;overflow:hidden;"></div>' 797 ); 798 } 799 800 const MENU_HINT_TEXT = "Press Enter to open the link. Press Down Arrow to open the submenu."; 801 802 $(".menuWrapper nav li.menu-item-has-children > a") 803 .on("focus.realHint", function () { 804 805 const $link = $(this); 806 const href = $link.attr("href") || ""; 807 const isRealLink = href !== "#" && !href.startsWith("javascript"); 808 809 if (!isRealLink) return; 810 811 $link.find(".menu-real-link-hint-wrapper").remove(); 812 813 setTimeout(function () { 814 815 const inlineHintText = $( 816 '<span class="menu-real-link-hint-wrapper" aria-live="polite" ' + 'style="position:absolute;left:-9999px;top:-9999px;">' + 817 '<span class="menu-real-link-hint">' + MENU_HINT_TEXT + '</span>' + 818 '</span>' 819 ); 820 $link.append(inlineHintText); 821 const $globalMenuHint = $("#menu-hint-global"); 822 $globalMenuHint.text(""); 823 setTimeout(function () { 824 $globalMenuHint.text(MENU_HINT_TEXT); 825 }, 50); 826 827 }, 300); 828 829 }) 830 .on("blur.realHint", function () { 831 const $link = $(this); 832 $link.find(".menu-real-link-hint-wrapper").remove(); 833 834 $("#menu-hint-global").text(""); 835 }); 836 837 838 839 // Handelling menu links with link as well as submenu in mobile device js start 840 841 842 // let lastInputType = "mouse"; 843 // let lastTouchedLink = null; 844 845 // $(document) 846 // .on("keydown.inputMode", function () { 847 // lastInputType = "keyboard"; 848 // }) 849 // .on("touchstart.inputMode pointerdown.inputMode", function () { 850 // lastInputType = "touch"; 851 // }) 852 // .on("mousedown.inputMode", function () { 853 // lastInputType = "mouse"; 854 // }); 855 856 // /* TOUCH-ONLY MENU LOGIC */ 857 // $(".menuWrapper nav li.menu-item-has-children > a") 858 // .on("click.touchMenu", function (e) { 859 860 // // If keyboard is being used â behave like desktop 861 // if (lastInputType === "keyboard") return; 862 863 // const $link = $(this); 864 // const href = $link.attr("href") || ""; 865 // const isRealLink = href !== "#" && !href.startsWith("javascript"); 866 // const $parentLi = $link.parent("li"); 867 868 // if (!isRealLink) return; 869 870 // // First touch â open submenu 871 // if (!$parentLi.hasClass("submenu-open")) { 872 // e.preventDefault(); 873 874 // $(".menuWrapper nav li.submenu-open") 875 // .removeClass("submenu-open"); 876 877 // $parentLi.addClass("submenu-open"); 878 // lastTouchedLink = this; 879 // return; 880 // } 881 882 // // Second touch on same link â allow navigation 883 // if (lastTouchedLink === this) { 884 // lastTouchedLink = null; 885 // return true; 886 // } 887 888 // }); 889 890 // /* TOUCHED OUTSIDE CLOSE SUBMENU */ 891 // $(document).on("touchstart", function (e) { 892 // if (!$(e.target).closest(".menuWrapper").length) { 893 // $(".menuWrapper nav li.submenu-open").removeClass("submenu-open"); 894 // lastTouchedLink = null; 895 // } 896 // }); 897 898 // Handelling menu links with link as well as submenu in mobile device js ends 899 }); 900 901// code to handle menulink having link & submenu both for desktop and ke
901yboard Navigation end 902 903 904//handle esc key functionality js start 905 906jQuery(document).ready(function ($) { 907 908 $(document).on('keydown.overflowEscHandler', function (e) { 909 910 if (e.key === "Escape" || e.keyCode === 27) { 911 912 const $ov = $('#overflowMenu'); 913 914 // run only if overflow menu is open 915 if ($ov.is(':visible')) { 916 917 // if focus is inside overflow menu â let existing logic handle it 918 if ($(document.activeElement).closest('#overflowMenu').length) { 919 return; 920 } 921 922 // otherwise close overflow 923 e.preventDefault(); 924 925 const ovWidth = $ov.width(); 926 927 $ov.animate({ right: -ovWidth }, 400, function () { 928 $ov.css('display', 'none'); 929 $ov.find('a').attr('tabindex', -1); 930 $('.moreNav > a').attr('aria-expanded', 'false').focus(); 931 }); 932 933 } 934 935 } 936 937 }); 938 939}); 940 941//handle esc key functionality js ends 942
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.