Skip to content

Instantly share code, notes, and snippets.

@Dzordzu
Last active June 3, 2018 22:11
Show Gist options
  • Save Dzordzu/106293f57b459e6c42a69697fcb2dbac to your computer and use it in GitHub Desktop.
Save Dzordzu/106293f57b459e6c42a69697fcb2dbac to your computer and use it in GitHub Desktop.
Accessible navbar (VanillaJS + BEM)
// Example of the Trigger usage
function toggle(trigger, target) {
if(target.classList.contains("navbar__dropdown--hidden")){
target.classList.remove("navbar__dropdown--hidden");
target.querySelector("a").focus();
}
else {
target.classList.add("navbar__dropdown--hidden");
}
}
function hide(target) {
target.classList.add("navbar__dropdown--hidden");
}
var menuTrigger = new Trigger({
trigger: document.querySelector(".navbar__trigger--menu"),
target: document.querySelector(".navbar__dropdown--menu"),
toggleFunc: toggle,
hideFunc: hide
});
var langTrigger = new Trigger({
trigger: document.querySelector(".navbar__trigger--languageApp"),
target: document.querySelector(".navbar__dropdown--languageApp"),
toggleFunc: toggle,
hideFunc: hide
});
<!-- Assuming you've implemented trigger.js and code.js -->
<!-- Currently there is no default styling (this is gist, isn't it?) -->
<nav class="navbar">
<!-- Triggers. Use buttons for better accessibility -->
<div class="navbar__triggers navbar__triggers--hidden">
<button class="navbar__trigger navbar__trigger--menu">
<span aria-label="Open menu" aria-role="img" class="noMargin">☰</span>
</button>
<button class="navbar__trigger navbar__trigger--languageApp">
<img class="navbar__lang noMargin" alt="Wybierz język / Choose language" src="src/media/images/Flag_of_Poland.png"/>
</button>
</div>
<!-- Dropdowns. Lists that should be hidden by default -->
<ul class="navbar__dropdown navbar__dropdown--menu">
<li class="navbar__elem"> <a class="navbar__link" href="#1">Lorem ipsum</a></li>
<li class="navbar__elem"> <a class="navbar__link" href="#1">dolor</a></li>
<li class="navbar__elem"> <a class="navbar__link" href="#1">amet</a></li>
</ul>
<!-- No alt's here (for img). This approach guarantees no additional comment to the following language name -->
<ul class="navbar__dropdown navbar__dropdown--languageApp">
<li class="navbar__elem"> <a class="navbar__link" href="?lang=pl"> <img class="navbar__lang" src="src/media/images/Flag_of_Poland.svg"/> Polish </a> </li>
<li class="navbar__elem"> <a class="navbar__link" href="?lang=uk"> <img class="navbar__lang" src="src/media/images/Flag_of_Ukraine.png"/> Ukrainian </a> </li>
<li class="navbar__elem"> <a class="navbar__link" href="?lang=en"> English </a> </li>
</ul>
</nav>
/**
* @class Trigger
* @desc creates bound between trigger and target. Additionally can add the proper events to close dialog
*
* @constructor Trigger(options)
* @desc options should be in the following form:
* {
* @required @prop trigger,
* @required @prop target, // Is activated by trigger
* @required @function toggleFunc(trigger, target), //Is called on click event. Should always take trigger and target
* @function hideFunc(target) // Is called if "Escape" is clicked and/or when target lost its focus
* }
*
*
*/
var Trigger = function(options) {
/*
* @section methods
*/
function isEmpty(obj) {
if(obj === null || obj === undefined || obj === {}) return true;
else return false;
}
// Basic function runs always whenever new object is created
function basic() {
// Prepare options.func
if(isEmpty(options.toggleFunc)) {
console.log("Trigger error: No action to call");
}
// Run options.func
options.trigger.addEventListener("click", function(){
(options.toggleFunc)(options.trigger, options.target);
});
}
// Hiddings function runs if and only if hideFunc is already set (in constructor)
function hiddings() {
var setTimeoutID = null;
// When it loses focus
options.target.addEventListener("blur", function(){
setTimeoutID = setTimeout(function(){
(options.hideFunc)(options.target);
}, 100);
}, 1);
options.target.addEventListener("focus", function(){
clearTimeout(setTimeoutID);
}, 1);
// When Escape is entered
document.addEventListener("keydown", function(event){
if(event.code === "Escape") {
(options.hideFunc)(options.target);
};
});
}
/*
* Constructor
*/
function constructor() {
basic();
if(!isEmpty(options.hideFunc)) hiddings();
}
// Run it!
constructor();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment