├── README.md
├── index.html
├── script.js
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | # A More Accessible Multi-Level Dropdown Navigation
2 |
3 | My attempt at creating a more accessible multi-level dropdown navigation menu, which responds to the follow user interactions at any screen size -
4 |
5 | - Using a mouse to **hover** over the element
6 | - **Clicking** on the element with a mouse
7 | - **Tapping** on the element on a touchscreen
8 | - **Tabbing** into and out of the element with a keyboard
9 |
10 | ## [Read the blog post](http://bitsofco.de/2015/accessible-multi-level-dropdown-navigation/)
11 |
12 |
13 | ### Credits
14 |
15 | Although I started this myself, I received a lot of valuable input from people in the comments section of the blog post and have updated the final menu. [Check out the comments](http://bitsofco.de/2015/accessible-multi-level-dropdown-navigation/#disqus_thread)
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Accessible Multi-Level Dropdown Navigation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Accessible Multi-Level Dropdown Navigation
17 |
18 |
19 |
20 |
21 |
22 |
33 |
34 |
35 | Level 1
36 |
37 |
38 |
39 | Level 2
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 | Level 3
51 |
64 |
65 |
66 |
67 | Level 1
68 |
69 |
70 | Level 3
71 |
72 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 |
4 | // Remove no-js class
5 | $('html').removeClass('no-js');
6 |
7 | $('#toggleMenu').on('click', function() {
8 |
9 | if ( $(this).hasClass('js-open') ) {
10 |
11 | $('#nav > ul > li:not(#toggleMenu)').removeClass('js-showElement');
12 | $(this).removeClass('js-open');
13 |
14 | $(this).attr('aria-expanded', false);
15 |
16 | } else {
17 |
18 | $('#nav > ul > li:not(#toggleMenu)').addClass('js-showElement');
19 | $(this).addClass('js-open');
20 |
21 | $(this).attr('aria-expanded', true);
22 |
23 | }
24 |
25 | return false;
26 | })
27 |
28 | // Add plus mark to li that have a sub menu
29 | $('li:has("ul") > a').append('+ ');
30 |
31 |
32 |
33 |
34 | // sub menu
35 | // ------------------------
36 |
37 | // When interacting with a li that has a sub menu
38 | $('li:has("ul")').on('mouseover keyup click mouseleave', function(e) {
39 |
40 | console.log("test")
41 |
42 | // If either -
43 | // tabbing into the li that has a sub menu
44 | // hovering over the li that has a sub menu
45 | if ( e.keyCode === 9 | e.type === 'mouseover' ) {
46 |
47 | // Show sub menu
48 | $(this).children('ul').removeClass('js-hideElement');
49 | $(this).children('ul').addClass('js-showElement');
50 | }
51 |
52 | // If mouse leaves li that has sub menu
53 | if ( e.type === 'mouseleave' ) {
54 |
55 | // hide sub menu
56 | $(this).children('ul').removeClass('js-showElement');
57 | $(this).children('ul').addClass('js-hideElement');
58 | }
59 |
60 |
61 | // If clicking on li that has a sub menu
62 | if ( e.type === 'click' ) {
63 |
64 | // If sub menu is already open
65 | if ( $(this).children('a').hasClass('js-openSubMenu') ) {
66 |
67 | // remove Open class
68 | $(this).children('a').removeClass('js-openSubMenu');
69 |
70 | // Hide sub menu
71 | $(this).children('ul').removeClass('js-showElement');
72 | $(this).children('ul').addClass('js-hideElement');
73 |
74 |
75 | // If sub menu is closed
76 | } else {
77 |
78 | // add Open class
79 | $(this).children('a').addClass('js-openSubMenu');
80 |
81 | // Show sub menu
82 | $(this).children('ul').removeClass('js-hideElement');
83 | $(this).children('ul').addClass('js-showElement');
84 |
85 | }
86 |
87 | } // end click event
88 |
89 | });
90 |
91 |
92 | // Tabbing through Levels of sub menu
93 | // ------------------------
94 |
95 | // If key is pressed while on the last link in a sub menu
96 | $('li > ul > li:last-child > a').on('keydown', function(e) {
97 |
98 |
99 | // If tabbing out of the last link in a sub menu AND not tabbing into another sub menu
100 | if ( (e.keyCode == 9) && $(this).parent('li').children('ul').length == 0 ) {
101 |
102 | // Close this sub menu
103 | $(this).parent('li').parent('ul').removeClass('js-showElement');
104 | $(this).parent('li').parent('ul').addClass('js-hideElement');
105 |
106 |
107 | // If tabbing out of a third level sub menu and there are no other links in the parent (level 2) sub menu
108 | if ( $(this).parent('li').parent('ul').parent('li').parent('ul').parent('li').children('ul').length > 0
109 | && $(this).parent('li').parent('ul').parent('li').is(':last-child') ) {
110 |
111 | // Close the parent sub menu (level 2) as well
112 | $(this).parent('li').parent('ul').parent('li').parent('ul').removeClass('js-showElement');
113 | $(this).parent('li').parent('ul').parent('li').parent('ul').addClass('js-hideElement');
114 | }
115 |
116 | }
117 |
118 | })
119 |
120 |
121 |
122 |
123 | })
124 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: 'Proxima Nova', 'Helvetica Neue', 'Arial', sans-serif; }
3 |
4 | .container {
5 | width: 90%;
6 | margin: 10vh auto; }
7 |
8 | a {
9 | color: inherit; }
10 |
11 | /*
12 | * Accessible Multi-Level Dropdown Navigation Menu
13 | * Created by Ire Aderinokun
14 | *
15 | */
16 | /* Resets */
17 | #nav, #nav ul, #nav li {
18 | margin: 0;
19 | padding: 0;
20 | border: 0;
21 | list-style: none;
22 | box-sizing: border-box; }
23 |
24 | #nav {
25 | position: relative;
26 | min-height: 60px;
27 | max-width: 100%;
28 | background-color: #ffdb3a;
29 | color: #000;
30 | border: 1px solid #D9BA31; }
31 | @media screen and (min-width: 650px) {
32 | #nav {
33 | display: inline-block; } }
34 | @media screen and (max-width: 650px) {
35 | #nav {
36 | display: block; } }
37 |
38 | #nav li {
39 | position: relative; }
40 |
41 | #nav a {
42 | text-decoration: none;
43 | height: 100%;
44 | display: block;
45 | padding: 0 20px; }
46 |
47 | @media screen and (min-width: 650px) {
48 | #nav a:focus {
49 | outline: none; } }
50 |
51 | .plusMark {
52 | margin-left: 10px;
53 | font-size: 20px;
54 | font-weight: 700; }
55 |
56 | @media screen and (min-width: 650px) {
57 | #nav li {
58 | text-align: left;
59 | width: 200px; } }
60 | @media screen and (max-width: 650px) {
61 | #nav li {
62 | text-align: center;
63 | width: 100%; } }
64 |
65 | /* Any sub menu */
66 | @media screen and (min-width: 650px) {
67 | a + ul {
68 | position: absolute; }
69 | a + ul:not(.js-showElement) {
70 | display: none; } }
71 | @media screen and (max-width: 650px) {
72 | a + ul {
73 | position: relative; }
74 | a + ul:not(.js-hideElement) {
75 | display: block; } }
76 |
77 | /* The Main Navigation Bar - Navigation Level One */
78 | #nav > ul, .fa {
79 | height: 100%;
80 | line-height: 60px; }
81 |
82 | #nav > ul > li {
83 | position: relative;
84 | text-align: center; }
85 | @media screen and (min-width: 650px) {
86 | #nav > ul > li {
87 | float: left;
88 | width: auto; } }
89 | @media screen and (max-width: 650px) {
90 | #nav > ul > li {
91 | float: none;
92 | display: block;
93 | width: 100%; } }
94 |
95 | #nav > ul > li > a {
96 | background-color: #ffdb3a; }
97 | #nav > ul > li > a:hover, #nav > ul > li > a:focus, #nav > ul > li > a.js-openSubMenu {
98 | background-color: #D9BA31; }
99 |
100 | #nav > ul > li:hover > a, #nav > ul > li:focus > a {
101 | background-color: #D9BA31; }
102 |
103 | @media screen and (min-width: 650px) {
104 | #nav > ul > li:not(:last-child) {
105 | border-right: 1px solid #D9BA31;
106 | border-bottom: none; } }
107 | @media screen and (max-width: 650px) {
108 | #nav > ul > li:not(:last-child) {
109 | border-right: none; }
110 | #nav > ul > li:not(:last-child):not(:first-child) {
111 | border-bottom: 1px solid #D9BA31; } }
112 |
113 | #nav > ul > li:not(#toggleMenu):not(.js-showElement) {
114 | /* first level nav li except toggleMenu icon */ }
115 | @media screen and (min-width: 650px) {
116 | #nav > ul > li:not(#toggleMenu):not(.js-showElement) {
117 | display: inline-block; } }
118 | @media screen and (max-width: 650px) {
119 | #nav > ul > li:not(#toggleMenu):not(.js-showElement) {
120 | display: none; } }
121 |
122 | @media screen and (min-width: 650px) {
123 | #nav #toggleMenu {
124 | display: none; } }
125 | @media screen and (max-width: 650px) {
126 | #nav #toggleMenu {
127 | display: block;
128 | width: 100%; }
129 | #nav #toggleMenu.js-open {
130 | border-bottom: 1px solid #D9BA31; }
131 | #nav #toggleMenu.js-open .fa-times {
132 | display: block; }
133 | #nav #toggleMenu.js-open .fa-bars {
134 | display: none; }
135 | #nav #toggleMenu.js-open a {
136 | background-color: #D9BA31; }
137 | #nav #toggleMenu:not(.js-open) .fa-times {
138 | display: none; }
139 | #nav #toggleMenu:not(.js-open) .fa-bars {
140 | display: block; } }
141 |
142 | span#toggleMenu-text {
143 | position: absolute;
144 | opacity: 0; }
145 |
146 | /* Second Level Dropdown */
147 | #nav > ul > li > ul {
148 | background-color: #D9BA31; }
149 | @media screen and (min-width: 650px) {
150 | #nav > ul > li > ul {
151 | top: 60px;
152 | left: 0; } }
153 | @media screen and (max-width: 650px) {
154 | #nav > ul > li > ul {
155 | width: 100%;
156 | position: relative; }
157 | #nav > ul > li > ul:not(.js-showElement) {
158 | display: none; } }
159 |
160 | #nav > ul > li > ul > li > a {
161 | background-color: #D9BA31; }
162 | #nav > ul > li > ul > li > a:hover, #nav > ul > li > ul > li > a:focus {
163 | background-color: #ffdb3a; }
164 |
165 | #nav > ul > li > ul > li:not(:last-child) a {
166 | border-bottom: 1px solid #ffdb3a; }
167 |
168 | /* Third Level Dropdown */
169 | @media screen and (min-width: 650px) {
170 | #nav > ul > li > ul > li > ul {
171 | top: 0;
172 | left: 200px;
173 | /* width of ul */ } }
174 | @media screen and (max-width: 650px) {
175 | #nav > ul > li > ul > li > ul {
176 | width: 100%;
177 | position: relative; }
178 | #nav > ul > li > ul > li > ul:not(.js-showElement) {
179 | display: none; } }
180 |
181 | #nav > ul > li > ul > li > ul > li > a {
182 | background-color: #ffdb3a; }
183 | #nav > ul > li > ul > li > ul > li > a:hover, #nav > ul > li > ul > li > ul > li > a:focus {
184 | background-color: #D9BA31; }
185 |
186 | #nav > ul > li > ul > li > ul > li:not(:last-child) > a {
187 | border-bottom: 1px solid #D9BA31; }
188 |
189 | /* Javascript classes */
190 | #nav .js-hideElement {
191 | display: none; }
192 |
193 | #nav .js-showElement {
194 | display: block; }
195 |
196 | /* Fallback for users without javascript */
197 | html.no-js li:hover > a + ul, html.no-js li:focus > a + ul {
198 | display: block; }
199 | @media screen and (max-width: 650px) {
200 | html.no-js #nav:hover > ul > li:not(#toggleMenu), html.no-js #nav:focus > ul > li:not(#toggleMenu) {
201 | display: block; }
202 | html.no-js #nav:hover li:hover > a + ul, html.no-js #nav:hover li:focus > a + ul, html.no-js #nav:focus li:hover > a + ul, html.no-js #nav:focus li:focus > a + ul {
203 | display: block; } }
204 |
--------------------------------------------------------------------------------