├── 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 | 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 | --------------------------------------------------------------------------------