-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathclicky-menus.js
168 lines (139 loc) · 4.81 KB
/
clicky-menus.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* Clicky Menus v1.2.0
*/
( function() {
'use strict';
const ClickyMenus = function( menu ) {
// DOM element(s)
const container = menu.parentElement;
let currentMenuItem,
i,
len;
this.init = function() {
menuSetup();
document.addEventListener( 'click', closeIfClickOutsideMenu );
// custom event to allow outside scripts to close submenus
menu.addEventListener( 'clickyMenusClose', closeOpenSubmenu );
};
/*===================================================
= Menu Open / Close Functions =
===================================================*/
function toggleOnMenuClick( e ) {
const button = e.currentTarget;
// close open menu if there is one
if ( currentMenuItem && button !== currentMenuItem ) {
toggleSubmenu( currentMenuItem );
}
toggleSubmenu( button );
}
function toggleSubmenu( button ) {
const submenu = document.getElementById( button.getAttribute( 'aria-controls' ) );
if ( 'true' === button.getAttribute( 'aria-expanded' ) ) {
button.setAttribute( 'aria-expanded', false );
submenu.setAttribute( 'aria-hidden', true );
currentMenuItem = false;
} else {
button.setAttribute( 'aria-expanded', true );
submenu.setAttribute( 'aria-hidden', false );
preventOffScreenSubmenu( submenu );
currentMenuItem = button;
}
}
function preventOffScreenSubmenu( submenu ) {
const screenWidth = window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth,
parent = submenu.offsetParent,
menuLeftEdge = parent.getBoundingClientRect().left,
menuRightEdge = menuLeftEdge + submenu.offsetWidth;
if ( menuRightEdge + 32 > screenWidth ) { // adding 32 so it's not too close
submenu.classList.add( 'sub-menu--right' );
}
}
function closeOnEscKey( e ) {
if ( 27 === e.keyCode ) {
// we're in a submenu item
if ( null !== e.target.closest( 'ul[aria-hidden="false"]' ) ) {
currentMenuItem.focus();
toggleSubmenu( currentMenuItem );
// we're on a parent item
} else if ( 'true' === e.target.getAttribute( 'aria-expanded' ) ) {
toggleSubmenu( currentMenuItem );
}
}
}
function closeIfClickOutsideMenu( e ) {
if ( currentMenuItem && ! e.target.closest( '#' + container.id ) ) {
toggleSubmenu( currentMenuItem );
}
}
function closeOpenSubmenu() {
if( currentMenuItem ) {
toggleSubmenu( currentMenuItem );
}
}
/*===========================================================
= Modify Menu Markup & Bind Listeners =
=============================================================*/
function menuSetup() {
menu.classList.remove( 'no-js' );
const submenuSelector = 'clickySubmenuSelector' in menu.dataset ? menu.dataset.clickySubmenuSelector : 'ul';
menu.querySelectorAll( submenuSelector ).forEach( ( submenu ) => {
const menuItem = submenu.parentElement;
if ( 'undefined' !== typeof submenu ) {
const button = convertLinkToButton( menuItem );
setUpAria( submenu, button );
// bind event listener to button
button.addEventListener( 'click', toggleOnMenuClick );
menu.addEventListener( 'keyup', closeOnEscKey );
}
} );
}
/**
* Why do this? See https://justmarkup.com/articles/2019-01-21-the-link-to-button-enhancement/
*
* @param {HTMLElement} menuItem An element representing a link to be converted to a button
*/
function convertLinkToButton( menuItem ) {
const link = menuItem.getElementsByTagName( 'a' )[ 0 ],
linkHTML = link.innerHTML,
linkAtts = link.attributes,
button = document.createElement( 'button' );
if ( null !== link ) {
// copy button attributes and content from link
button.innerHTML = linkHTML.trim();
for ( i = 0, len = linkAtts.length; i < len; i++ ) {
const attr = linkAtts[ i ];
if ( 'href' !== attr.name ) {
button.setAttribute( attr.name, attr.value );
}
}
menuItem.replaceChild( button, link );
}
return button;
}
function setUpAria( submenu, button ) {
const submenuId = submenu.getAttribute( 'id' );
let id;
if ( null === submenuId ) {
id = button.textContent.trim().replace( /\s+/g, '-' ).toLowerCase() + '-submenu';
} else {
id = submenuId + '-submenu';
}
// set button ARIA
button.setAttribute( 'aria-controls', id );
button.setAttribute( 'aria-expanded', false );
// set submenu ARIA
submenu.setAttribute( 'id', id );
submenu.setAttribute( 'aria-hidden', true );
}
};
/* Create a ClickMenus object and initiate menu for any menu with .clicky-menu class */
document.addEventListener( 'DOMContentLoaded', function() {
const menus = document.querySelectorAll( '.clicky-menu' );
menus.forEach( ( menu ) => {
const clickyMenu = new ClickyMenus( menu );
clickyMenu.init();
} );
} );
}() );