drop nav state functionality
authorDan Allen <dan@opendevise.com>
Fri, 19 Jul 2019 08:31:37 +0000 (02:31 -0600)
committerDan Allen <dan@opendevise.com>
Sun, 21 Jul 2019 07:21:05 +0000 (01:21 -0600)
- don't attempt to remember expanded state or scroll offset
- scroll current page in nav to midpoint
- scroll current page in nav to midpoint when nav is initially hidden
- rename 01-navigation.js to 01-nav.js for consistency

src/css/nav.css
src/js/01-nav.js [new file with mode: 0644]
src/js/01-navigation.js [deleted file]

index 471dfee..9ffa5b4 100644 (file)
@@ -1,11 +1,11 @@
 .nav-container {
-  display: none;
   position: fixed;
   top: var(--navbar-height);
   left: 0;
   width: 100%;
   font-size: 0.9375rem;
   z-index: var(--z-index-nav);
+  visibility: hidden;
 }
 
 @media screen and (min-width: 769px) {
   .nav-container {
     font-size: 0.8125rem;
     flex: none;
-    display: block;
     position: static;
     top: 0;
+    visibility: visible;
   }
 }
 
 .nav-container.is-active {
-  display: block;
+  visibility: visible;
 }
 
 .nav {
diff --git a/src/js/01-nav.js b/src/js/01-nav.js
new file mode 100644 (file)
index 0000000..65481b5
--- /dev/null
@@ -0,0 +1,106 @@
+;(function () {
+  'use strict'
+
+  var navContainer = document.querySelector('.nav-container')
+  var navToggle = document.querySelector('.nav-toggle')
+
+  navToggle.addEventListener('click', showNav)
+  // NOTE don't let click events propagate outside of nav container
+  navContainer.addEventListener('click', concealEvent)
+
+  var menuPanel = navContainer.querySelector('[data-panel=menu]')
+  if (!menuPanel) return
+  var nav = navContainer.querySelector('.nav')
+
+  var currentPageItem = menuPanel.querySelector('.is-current-page')
+  if (currentPageItem) {
+    activateCurrentPath(currentPageItem)
+    scrollItemToMidpoint(menuPanel, currentPageItem.querySelector('.nav-link'))
+  } else {
+    menuPanel.scrollTop = 0
+  }
+
+  find(menuPanel, '.nav-item-toggle').forEach(function (btn) {
+    var li = btn.parentElement
+    btn.addEventListener('click', toggleActive.bind(li))
+    var navItemSpan = findNextElement(btn, '.nav-text')
+    if (navItemSpan) {
+      navItemSpan.style.cursor = 'pointer'
+      navItemSpan.addEventListener('click', toggleActive.bind(li))
+    }
+  })
+
+  nav.querySelector('.context').addEventListener('click', function () {
+    var currentPanel = nav.querySelector('.is-active[data-panel]')
+    var activatePanel = currentPanel.dataset.panel === 'menu' ? 'explore' : 'menu'
+    currentPanel.classList.toggle('is-active')
+    nav.querySelector('[data-panel=' + activatePanel + ']').classList.toggle('is-active')
+  })
+
+  // NOTE prevent text from being selected by double click
+  menuPanel.addEventListener('mousedown', function (e) {
+    if (e.detail > 1) e.preventDefault()
+  })
+
+  function activateCurrentPath (navItem) {
+    var ancestorClasses
+    var ancestor = navItem.parentNode
+    while (!(ancestorClasses = ancestor.classList).contains('nav-menu')) {
+      if (ancestor.tagName === 'LI' && ancestorClasses.contains('nav-item')) {
+        ancestorClasses.add('is-active', 'is-current-path')
+      }
+      ancestor = ancestor.parentNode
+    }
+    navItem.classList.add('is-active')
+  }
+
+  function toggleActive () {
+    this.classList.toggle('is-active')
+  }
+
+  function showNav (e) {
+    if (navToggle.classList.contains('is-active')) return hideNav(e)
+    document.documentElement.classList.add('is-clipped--nav')
+    navToggle.classList.add('is-active')
+    navContainer.classList.add('is-active')
+    window.addEventListener('click', hideNav)
+    concealEvent(e)
+  }
+
+  function hideNav (e) {
+    if (e.which === 3 || e.button === 2) return
+    document.documentElement.classList.remove('is-clipped--nav')
+    navToggle.classList.remove('is-active')
+    navContainer.classList.remove('is-active')
+    window.removeEventListener('click', hideNav)
+    concealEvent(e)
+  }
+
+  // NOTE don't let event get picked up by window click listener
+  function concealEvent (e) {
+    e.stopPropagation()
+  }
+
+  function scrollItemToMidpoint (panel, el) {
+    var rect = panel.getBoundingClientRect()
+    var effectiveHeight = rect.height
+    var navStyle = window.getComputedStyle(nav)
+    if (navStyle.position === 'sticky') effectiveHeight -= (rect.top - parseFloat(navStyle.top))
+    panel.scrollTop = Math.max(0, (el.getBoundingClientRect().height - effectiveHeight) * 0.5 + el.offsetTop)
+  }
+
+  function find (from, selector) {
+    return [].slice.call(from.querySelectorAll(selector))
+  }
+
+  function findNextElement (from, selector) {
+    var el
+    if ('nextElementSibling' in from) {
+      el = from.nextElementSibling
+    } else {
+      el = from
+      while ((el = el.nextSibling) && el.nodeType !== 1);
+    }
+    return el && selector ? el[el.matches ? 'matches' : 'msMatchesSelector'](selector) && el : el
+  }
+})()
diff --git a/src/js/01-navigation.js b/src/js/01-navigation.js
deleted file mode 100644 (file)
index ba25546..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-;(function () {
-  'use strict'
-
-  var navContainer = document.querySelector('.nav-container')
-  var navToggle = document.querySelector('.nav-toggle')
-
-  navToggle.addEventListener('click', toggleNavigation)
-  // don't let click events propagate outside of nav container
-  navContainer.addEventListener('click', concealEvent)
-
-  var menuPanel = navContainer.querySelector('[data-panel=menu]')
-  if (!menuPanel) return
-
-  var navState = getNavState()
-  var menuState = getMenuState(navState, navContainer.dataset.component, navContainer.dataset.version)
-
-  navContainer.querySelector('.context').addEventListener('click', function () {
-    var currentPanel = navContainer.querySelector('.is-active[data-panel]')
-    var activatePanel = currentPanel.dataset.panel === 'menu' ? 'explore' : 'menu'
-    currentPanel.classList.toggle('is-active')
-    navContainer.querySelector('[data-panel=' + activatePanel + ']').classList.toggle('is-active')
-  })
-
-  find('.nav-item-toggle', menuPanel).forEach(function (btn) {
-    var li = btn.parentElement
-    btn.addEventListener('click', function () {
-      li.classList.toggle('is-active')
-      menuState.expandedItems = getExpandedItems()
-      saveNavState()
-    })
-    var navItemSpan = findNextElement(btn, '.nav-text')
-    if (navItemSpan) {
-      navItemSpan.style.cursor = 'pointer'
-      navItemSpan.addEventListener('click', function () {
-        li.classList.toggle('is-active')
-        menuState.expandedItems = getExpandedItems()
-        saveNavState()
-      })
-    }
-  })
-
-  find('.nav-item', menuPanel).forEach(function (item, idx) {
-    item.setAttribute('data-id', 'menu-' + item.dataset.depth + '-' + idx)
-  })
-
-  var expandedItems = menuState.expandedItems || (menuState.expandedItems = [])
-
-  if (expandedItems.length) {
-    find(
-      expandedItems
-        .map(function (itemId) {
-          return '.nav-item[data-id="' + itemId + '"]'
-        })
-        .join(','),
-      menuPanel
-    ).forEach(function (item) {
-      item.classList.add('is-active')
-    })
-  }
-
-  var currentPageItem = menuPanel.querySelector('.is-current-page')
-  if (currentPageItem) {
-    activateCurrentPath(currentPageItem).forEach(function (itemId) {
-      if (expandedItems.indexOf(itemId) < 0) expandedItems.push(itemId)
-    })
-  }
-
-  saveNavState()
-
-  scrollItemIntoView(menuState.scroll || 0, menuPanel, currentPageItem && currentPageItem.querySelector('.nav-link'))
-
-  menuPanel.addEventListener('scroll', function () {
-    menuState.scroll = Math.round(menuPanel.scrollTop)
-    saveNavState()
-  })
-
-  function activateCurrentPath (navItem) {
-    var ids = [navItem.dataset.id]
-    var ancestorClasses
-    var ancestor = navItem.parentNode
-    while (!(ancestorClasses = ancestor.classList).contains('nav-menu')) {
-      if (ancestor.tagName === 'LI' && ancestorClasses.contains('nav-item')) {
-        ancestorClasses.add('is-active', 'is-current-path')
-        ids.push(ancestor.dataset.id)
-      }
-      ancestor = ancestor.parentNode
-    }
-    navItem.classList.add('is-active')
-    return ids
-  }
-
-  function toggleNavigation (e) {
-    if (navToggle.classList.contains('is-active')) return closeNavigation(e)
-    document.documentElement.classList.add('is-clipped--nav')
-    navToggle.classList.add('is-active')
-    navContainer.classList.add('is-active')
-    window.addEventListener('click', closeNavigation)
-    // don't let this event get picked up by window click listener
-    concealEvent(e)
-  }
-
-  function closeNavigation (e) {
-    if (e.which === 3 || e.button === 2) return
-    document.documentElement.classList.remove('is-clipped--nav')
-    navToggle.classList.remove('is-active')
-    navContainer.classList.remove('is-active')
-    window.removeEventListener('click', closeNavigation)
-    // don't let this event get picked up by window click listener
-    concealEvent(e)
-  }
-
-  function concealEvent (e) {
-    e.stopPropagation()
-  }
-
-  function getExpandedItems () {
-    return find('.is-active', menuPanel).map(function (item) {
-      return item.dataset.id
-    })
-  }
-
-  function getNavState () {
-    var data = window.sessionStorage.getItem('nav-state')
-    return data && (data = JSON.parse(data)).__version__ === '1' ? data : { __version__: '1' }
-  }
-
-  function getMenuState (navState, component, version) {
-    var key = version + '@' + component
-    return navState[key] || (navState[key] = {})
-  }
-
-  function saveNavState () {
-    window.sessionStorage.setItem('nav-state', JSON.stringify(navState))
-  }
-
-  function scrollItemIntoView (scrollPosition, parent, el) {
-    if (!el) return (parent.scrollTop = scrollPosition)
-
-    var margin = 10
-    //var y = el.getBoundingClientRect().top - parent.getBoundingClientRect().top
-    var y = el.offsetTop
-
-    if (y < scrollPosition) {
-      parent.scrollTop = y - margin
-    } else if (y - parent.offsetHeight + el.offsetHeight > scrollPosition) {
-      parent.scrollTop = y - parent.offsetHeight + el.offsetHeight + margin
-    } else {
-      parent.scrollTop = scrollPosition
-    }
-  }
-
-  function find (selector, from) {
-    return [].slice.call((from || document).querySelectorAll(selector))
-  }
-
-  function findNextElement (from, selector) {
-    var el
-    if ('nextElementSibling' in from) {
-      el = from.nextElementSibling
-    } else {
-      el = from
-      while ((el = el.nextSibling) && el.nodeType !== 1);
-    }
-    return el && selector ? el[el.matches ? 'matches' : 'msMatchesSelector'](selector) && el : el
-  }
-})()