Skip to content

Instantly share code, notes, and snippets.

@maxboeck
Last active February 15, 2018 10:11
Show Gist options
  • Save maxboeck/b15a5c15d929f70010fe6752c83e05cc to your computer and use it in GitHub Desktop.
Save maxboeck/b15a5c15d929f70010fe6752c83e05cc to your computer and use it in GitHub Desktop.
Reusable Focus Trap Example
// Reusable Focus Trap Method
export function createFocusTrap(el, opt = {}) {
let isActive = false
const focusableElementSelectors = [
'a[href]',
'area[href]',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'button:not([disabled])',
'iframe',
'object',
'embed',
'[contenteditable]',
'[tabindex]:not([tabindex^="-"])'
]
const focusableElements = el.querySelectorAll(
focusableElementSelectors.join(', ')
)
const lastFocusableElement = focusableElements[focusableElements.length - 1]
const defaults = {
toggleElement: focusableElements[0],
escape: true,
onEscape: () => {}
}
const options = Object.assign({}, defaults, opt)
const handleKeyPress = e => {
if (!isActive || e.ctrlKey || e.metaKey || e.altKey) {
return
}
switch (e.keyCode) {
case 27: // ESC
if (options.escape) {
options.onEscape()
options.toggleElement.focus()
}
break
case 9: // TAB
if (e.shiftKey) {
if (document.activeElement === options.toggleElement) {
lastFocusableElement.focus()
e.preventDefault()
}
} else if (document.activeElement === lastFocusableElement) {
options.toggleElement.focus()
e.preventDefault()
}
break
default:
break
}
}
function activate() {
isActive = true
el.addEventListener('keydown', handleKeyPress)
}
function deactivate() {
isActive = false
el.removeEventListener('keydown', handleKeyPress)
}
return {
activate,
deactivate
}
}
import { createFocusTrap } from './focustrap'
const SELECTORS = {
nav: '.js-nav',
toggleBtn: '.js-nav-toggle'
}
const CLASSES = {
open: 'nav--open'
}
export default class Navigation {
constructor(el) {
this.isOpen = false
this.nav = el
this.toggleBtn = this.nav.querySelector(SELECTORS.toggleBtn)
this.toggleBtn.addEventListener('click', () => this.toggleMenu())
this.focusTrap = createFocusTrap(this.nav, {
toggleElement: this.toggleBtn,
onEscape: () => this.toggleMenu()
})
}
toggleMenu() {
this.isOpen = !this.isOpen
if (this.isOpen) {
this.nav.classList.add(CLASSES.open)
this.focusTrap.activate()
} else {
this.nav.classList.remove(CLASSES.open)
this.focusTrap.deactivate()
}
this.toggleBtn.setAttribute('aria-expanded', String(this.isOpen))
}
}
const navElement = document.querySelector(SELECTORS.nav)
if (navElement) {
new Navigation(navElement)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment