Skip to content

Commit 64032d5

Browse files
authored
Merge pull request #25 from mrwweb/v1.3.0
V1.3.0
2 parents c967cbc + 5e25f63 commit 64032d5

File tree

5 files changed

+56
-19
lines changed

5 files changed

+56
-19
lines changed

clicky-menus.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Clicky Menus v1.2.0
2+
* Clicky Menus v1.3.0
33
*/
44

55
/**

clicky-menus.js

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Clicky Menus v1.2.0
2+
* Clicky Menus v1.3.0
33
*/
44

55
( function() {
@@ -9,7 +9,7 @@
99
// DOM element(s)
1010
const container = menu.parentElement;
1111
let currentMenuItem,
12-
i,
12+
i = 0,
1313
len;
1414

1515
this.init = function() {
@@ -62,15 +62,16 @@
6262
}
6363

6464
function closeOnEscKey( e ) {
65-
if ( 27 === e.keyCode ) {
65+
if ( 27 === e.keyCode ) {
6666
// we're in a submenu item
6767
if ( null !== e.target.closest( 'ul[aria-hidden="false"]' ) ) {
6868
currentMenuItem.focus();
6969
toggleSubmenu( currentMenuItem );
70-
70+
e.stopPropagation();
7171
// we're on a parent item
7272
} else if ( 'true' === e.target.getAttribute( 'aria-expanded' ) ) {
7373
toggleSubmenu( currentMenuItem );
74+
e.stopPropagation();
7475
}
7576
}
7677
}
@@ -116,14 +117,14 @@
116117
*/
117118
function convertLinkToButton( menuItem ) {
118119
const link = menuItem.getElementsByTagName( 'a' )[ 0 ],
119-
linkHTML = link.innerHTML,
120-
linkAtts = link.attributes,
121-
button = document.createElement( 'button' );
120+
linkHTML = link.innerHTML,
121+
linkAtts = link.attributes,
122+
button = document.createElement( 'button' );
122123

123124
if ( null !== link ) {
124125
// copy button attributes and content from link
125126
button.innerHTML = linkHTML.trim();
126-
for ( i = 0, len = linkAtts.length; i < len; i++ ) {
127+
for ( len = linkAtts.length; i < len; i++ ) {
127128
const attr = linkAtts[ i ];
128129
if ( 'href' !== attr.name ) {
129130
button.setAttribute( attr.name, attr.value );
@@ -141,9 +142,11 @@
141142

142143
let id;
143144
if ( null === submenuId ) {
144-
id = button.textContent.trim().replace( /\s+/g, '-' ).toLowerCase() + '-submenu';
145+
id = button.textContent.trim().replace( /\s+/g, '-' ).replace(/^[^a-zA-Z]+|[^\w:.-]+/g, "").toLowerCase() + `-submenu-${i}`;
146+
i++;
145147
} else {
146-
id = submenuId + '-submenu';
148+
id = `${submenuId}-submenu-${i}`;
149+
i++;
147150
}
148151

149152
// set button ARIA
@@ -165,4 +168,19 @@
165168
clickyMenu.init();
166169
} );
167170
} );
171+
172+
function dispatchMenuClose(e) {
173+
const menuId = e.currentTarget.getAttribute('data-clicky-menus-close');
174+
const menu = document.getElementById( menuId );
175+
if( menu ) {
176+
menu.dispatchEvent( new Event( 'clickyMenusClose' ) );
177+
}
178+
}
179+
180+
const menuClosers = document.querySelectorAll( '[data-clicky-menus-close]' );
181+
if( menuClosers ) {
182+
menuClosers.forEach( ( menuCloser ) => {
183+
menuCloser.addEventListener( 'click', dispatchMenuClose );
184+
} );
185+
}
168186
}() );

demo/demo.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
<!DOCTYPE html>
22
<html lang="en">
3+
34
<head>
45
<meta charset="UTF-8">
56
<title>Click Menus Demo</title>
67
<script src="../clicky-menus.js"></script>
78
<link rel="stylesheet" href="demo.css" />
89
<link rel="stylesheet" href="../clicky-menus.css" />
10+
<meta name="viewport" content="width=device-width, initial-scale=1" />
911
</head>
12+
1013
<body>
1114
<!-- for use with <use> -->
12-
<svg xmlns="http://www.w3.org/2000/svg" hidden>
13-
<symbol id="arrow" viewbox="0 0 16 16" >
15+
<svg xmlns="http://www.w3.org/2000/svg" hidden>
16+
<symbol id="arrow" viewbox="0 0 16 16">
1417
<polyline points="4 6, 8 10, 12 6" stroke="#000" stroke-width="2" fill="transparent" stroke-linecap="round" />
1518
</symbol>
1619
</svg>
1720

1821
<!-- In the real world, all hrefs would have go to real, unique URLs, not a "#" -->
1922
<nav id="site-navigation" class="site-navigation" aria-label="Clickable Menu Demonstration">
20-
<ul class="main-menu clicky-menu no-js">
23+
<ul id="main-menu" class="main-menu clicky-menu no-js">
2124
<li>
2225
<a href="#">Home</a>
2326
</li>
@@ -61,9 +64,11 @@
6164
<li><a href="#">Mission</a></li>
6265
<li><a href="#">History</a></li>
6366
<li><a href="#">Contact</a></li>
67+
<li><button data-clicky-menus-close="main-menu">Close Submenu</button></li>
6468
</ul>
6569
</li>
6670
</ul>
6771
</nav>
6872
</body>
73+
6974
</html>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "clicky-menus",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"homepage": "https://github.com/mrwweb/clicky-menus#readme",
55
"description": "Simple click-triggered navigation submenus. Accessible and progressively enhanced.",
66
"author": {

readme.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Clicky Menus
22

3-
Version 1.2.0
3+
Version 1.3.0
44

55
Jump to: [About](#about), [Features](#features), [Setup & Configuration](#setup--configuration), [Browser Support](#browser-support) [Changelog](#changelog)
66

@@ -25,7 +25,7 @@ Why should you want menus that work this way? Read the accompanying article on C
2525
- Close open submenu with click outside of open menu
2626
- Basic offscreen-menu prevention
2727
- [Configure custom submenu selector](#custom-submenu-selector)
28-
- [Programmatically close open submenus](#closing-submenus-with-js)
28+
- [Close open submenus with a button](#closing-open-submenus-with-data-attribute-or-custom-javascript-event)
2929

3030
### Why only one level of submenu?
3131

@@ -74,13 +74,21 @@ For example, if you only want to only select the first level of nested `<ul>` el
7474
</ul>
7575
```
7676

77-
### Closing submenus with JS
77+
### Closing open submenus with data attribute or custom JavaScript event
7878

7979
There are a variety of situations where you might want to force submenus to close based on interactions elsewhere on the page. For example, maybe an adjacent search toggle overlaps with submenus when expanded.
8080

8181
To close all open submenus, dispatch the custom event `clickyMenusClose` to the `.clicky-menu` DOM node (usually the `<ul>` containing menu items).
8282

83-
Example:
83+
#### With data attribute
84+
85+
Where `my-menu` is the ID of the menu you want to close and is the element with the `clicky-menu` class:
86+
87+
```html
88+
<button data-clicky-menus-close="my-menu">Close Open Submenus</button>
89+
```
90+
91+
#### With custom JavaScript event
8492

8593
```html
8694
<button id="close-open-submenus">Close Open Submenus</button>
@@ -149,6 +157,12 @@ Internet Explorer 11 support is possible if you include polyfills for [`closest`
149157
150158
## Changelog
151159
160+
### 1.3.0 (April 25, 2025)
161+
162+
- NEW! Use the `data-clicky-menus-close` attribute on any element to automatically close the open submenu. It should be set to the ID of the element with the `clicky-menu` class. Fixes #21.
163+
- Don't let `ESC` propogate when pressed on an item inside a submenu. This should generally prevent dialogs and other toggle features from closing when they contain a menu. Fixes #22
164+
- Strip special characters from automatically generated submenu IDs and add an increment (e.g. `-1`) to the end of each ID to get much closer to guaranteeing it is unique. Fixes #12
165+
152166
### 1.2.0 (May 21, 2024)
153167
154168
- You can now close open submenus from 3rd-party JS with the `clickyMenusClose` event

0 commit comments

Comments
 (0)