Skip to content

Instantly share code, notes, and snippets.

@web-crab
Created July 4, 2019 13:53
Show Gist options
  • Save web-crab/e71eb28d04b2c8511c3ee73a8934ca51 to your computer and use it in GitHub Desktop.
Save web-crab/e71eb28d04b2c8511c3ee73a8934ca51 to your computer and use it in GitHub Desktop.
const FOCUSABLE = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"]), [contenteditable]'
export default class Modal {
  constructor (options) {
    const focusableChildren = options.el.querySelectorAll(FOCUSABLE)
    this.el = options.el
    this.activeClass = options.activeClass
    this.firstFocusableChild = focusableChildren[0]
    this.lastFocusableChild = focusableChildren[focusableChildren.length - 1]
    this.elBeforeOpen = null
    this.scrollBeforeOpen = null
    this._onKeydown = this._onKeydown.bind(this)
  }
  show () {
    this.scrollBeforeOpen = document.documentElement.scrollTop
    this.elBeforeOpen = document.activeElement
    document.body.setAttribute('style', [
      'position: fixed',
      'width: 100%',
      'top: ' + -this.scrollBeforeOpen + 'px'
    ].join(';'))
    document.addEventListener('keydown', this._onKeydown)
    this.el.classList.add(this.activeClass)
    this.firstFocusableEL.focus()
  }
  hide () {
    document.body.removeAttribute('style')
    document.documentElement.scrollTop = this.scrollBeforeOpen
    document.removeEventListener('keydown', this._onKeydown)
    this.el.classList.remove(this.activeClass)
    this.elBeforeOpen.focus()
  }
  _onKeydown (e) {
    if (e.key === 'Tab') {
      if (e.shiftKey) {
        if (document.activeElement === this.firstFocusableChild) {
          e.preventDefault()
          this.lastFocusableChild.focus()
        }
      } else {
        if (document.activeElement === this.lastFocusableChild) {
          e.preventDefault()
          this.firstFocusableChild.focus()
        }
      }
    }
    if (e.key === 'Escape') {
      e.preventDefault()
      this.hide()
    }
  }
}
const modal = new Modal({
  el: document.querySelector('.modal'),
  activeClass: '.modal--active'
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment