POC Priority navigation w/ dropdown

In this example below you will see how to do a POC Priority navigation w/ dropdown with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by tibomahe, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright tibomahe ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>POC Priority navigation w/ dropdown</title>
  
  
  
      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <header class="c-header">
  
  <div class="l-container">
    
    <!-- The priority nav is made of a nvabar and an overflow -->
    <nav class="c-priority-nav js-priority-nav">
      
      <ul class="c-navbar js-priority-nav__navbar">
        <li>
          <a href="#">Item 1</a>
        </li>
        <li>
          <a href="#">Long item 2</a>
        </li>
        <li>
          <a href="#">Big bigger big item 3</a>
        </li>
        <li>
          <a href="#">Item 4</a>
        </li>
        <li>
          <div class="c-dropdown js-dropdown">
            <a href="#" class="js-dropdown__trigger"> Dropdown item 5 &blacktriangledown;</a>
            <ul class="c-dropdown__menu js-dropdown__menu">
              <li class="c-dropdown__item"><a href="#">Item 1</a></li>
              <li class="c-dropdown__item"><a href="#">Item 2</a></li>
              <li class="c-dropdown__item"><a href="#">Item 3</a></li>
              <li class="c-dropdown__item"><a href="#">Item 4</a></li>
              <li class="c-dropdown__item"><a href="#">Item 5</a></li>
            </ul>
          </div>
        </li>
        <li>
          <a href="#">Item 6</a>
        </li>
        <li>
          <div class="c-dropdown js-dropdown">
            <a href="#" class="js-dropdown__trigger">Long Dropdown item 7 &blacktriangledown;</a>
            <ul class="c-dropdown__menu js-dropdown__menu">
              <li class="c-dropdown__item"><a href="#">Item 1</a></li>
              <li class="c-dropdown__item"><a href="#">Item 2</a></li>
              <li class="c-dropdown__item"><a href="#">Item 3</a></li>
              <li class="c-dropdown__item"><a href="#">Item 4</a></li>
              <li class="c-dropdown__item"><a href="#">Item 5</a></li>
            </ul>
          </div>
        </li>
      </ul>
      
      <div class="c-priority-nav__overflow js-priority-nav__overflow">
				<a href="#" class="c-priority-nav__trigger js-priority-nav__trigger" title="Liens de navigation masqués">Plus &blacktriangledown;</a>
        <ul class="c-priority-nav__hidden-links js-priority-nav__hidden-links u-dropdown-overflow">
        </ul>
      </div>
      
    </nav>
    
  </div>
  
</header>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>

  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/tibomahe/poc-priority-navigation-with-dropdown-BmdgZd */
* {
  box-sizing: border-box;
}

a {
  font-size: 13px;
  font-family: Helvetica;
  text-decoration: none;
}

.l-container {
  margin-right: auto;
  margin-left: auto;
  max-width: 100%;
  border: 1px solid red;
}
@media (min-width: 576px) {
  .l-container {
    width: 540px;
  }
}
@media (min-width: 768px) {
  .l-container {
    width: 720px;
  }
}
@media (min-width: 992px) {
  .l-container {
    width: 960px;
  }
}
@media (min-width: 1200px) {
  .l-container {
    width: 1140px;
  }
}

.c-header {
  position: relative;
  height: 50px;
  background-color: #333537;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  padding: 0 20px;
  font-size: 0;
  z-index: 1001;
}
.c-header .c-priority-nav,
.c-header .c-navbar {
  height: 50px;
  font-size: 13px;
  display: inline-block;
  vertical-align: top;
}
.c-header .c-priority-nav {
  width: 100%;
  text-align: right;
}

.c-priority-nav {
  position: relative;
}
.c-priority-nav__overflow {
  height: 50px;
  position: absolute;
  right: 30px;
  top: 0;
  visibility: hidden;
}
.c-priority-nav__trigger {
  transition: color 0.4s;
  display: block;
  line-height: 50px;
  color: #fff;
}
.c-priority-nav__trigger:focus, .c-priority-nav__trigger:hover {
  color: #CCC;
  text-decoration: none;
  cursor: pointer;
}
.c-priority-nav__hidden-links {
  margin: 0;
  padding: 0;
  list-style: none;
  display: none;
  position: absolute;
  top: 100%;
  right: -30px;
  padding: 0 0 15px;
  width: 170px;
  background-color: #333537;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2);
}
.c-priority-nav__hidden-links li a {
  transition: color 0.4s;
  display: block;
  line-height: 50px;
  color: #fff;
  line-height: 3;
  padding: 0 10px;
}
.c-priority-nav__hidden-links li a:focus, .c-priority-nav__hidden-links li a:hover {
  color: #CCC;
  text-decoration: none;
  cursor: pointer;
}
.c-priority-nav .c-navbar {
  position: absolute;
  top: 0;
  right: 0;
  transition: right 0.4s;
}
.c-priority-nav.is-prioritized .c-priority-nav__overflow {
  visibility: visible;
}
.c-priority-nav.is-prioritized .c-priority-nav__hidden-links.is-open {
  display: block;
}
.c-priority-nav.is-prioritized .c-navbar {
  right: 80px;
}

.c-navbar {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid green;
}
.c-navbar:before, .c-navbar:after {
  content: " ";
  display: table;
}
.c-navbar:after {
  clear: both;
}
.c-navbar > li {
  height: 100%;
  float: left;
  margin-right: 30px;
}
.c-navbar > li + li {
  margin-left: 10px;
}
.c-navbar li a:not(.c-btn),
.c-navbar li button:not(.c-btn) {
  transition: color 0.4s;
  display: block;
  line-height: 50px;
  color: #fff;
}
.c-navbar li a:not(.c-btn):focus, .c-navbar li a:not(.c-btn):hover,
.c-navbar li button:not(.c-btn):focus,
.c-navbar li button:not(.c-btn):hover {
  color: #CCC;
  text-decoration: none;
  cursor: pointer;
}
.c-navbar > li .c-btn {
  margin-top: 10px;
}

.c-dropdown {
  position: relative;
}
.c-dropdown__menu {
  position: absolute;
  top: 100%;
  width: 100%;
  margin: 0;
  padding: 10px;
  display: none;
  background-color: #444;
  color: #fff;
}
.c-dropdown__menu li a:not(.c-btn) {
  line-height: 2.5;
}
.c-priority-nav__overflow .c-dropdown__menu {
  position: relative;
}
.c-dropdown__menu.is-open {
  display: block;
}


/*Downloaded from https://www.codeseek.co/tibomahe/poc-priority-navigation-with-dropdown-BmdgZd */
// debounce (from underscore.js)
function debounce(func, wait, immediate) {
	var timeout = null;
	return function () {
		var args = arguments,
		    context = this;
		function later() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		}
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
}

// dropdown component
function dropdown() {

	var $allDropdown = $('.js-dropdown');
	var $allDropdownMenu = $('.js-dropdown__menu');
	var closingTime = 500;
	var timer;

	$allDropdown.each(function (index, element) {

		var $element = $(element);
		var $dropdownTrigger = $element.find('.js-dropdown__trigger');
		var $dropdownMenu = $element.find('.js-dropdown__menu');

		$dropdownTrigger.on('click', function (event) {
			event.preventDefault();

			if ($dropdownMenu.hasClass('is-open')) {
				$allDropdownMenu.removeClass('is-open');
			} else {
				$allDropdownMenu.removeClass('is-open');
				$dropdownMenu.addClass('is-open');
			}

			clearTimeout(timer);
		});

		$dropdownMenu.on('mouseleave', function () {
			timer = setTimeout(function () {
				$dropdownMenu.removeClass('is-open');
			}, closingTime);
		}).on('mouseenter', function () {
			clearTimeout(timer);
		});
	});
}

// priority nav
function priorityNav() {

	// Inspired by https://css-tricks.com/the-priority-navigation-pattern/
	// Inspired by https://aws.amazon.com/fr/
	// Inspired by https://github.com/lukejacksonn/GreedyNav

	// Selectors
	// ------------------

	var priorityNav = '.js-priority-nav';
	var priorityNavNavbar = '.js-priority-nav__navbar';
	var priorityNavItem = '.js-priority-nav__navbar > li';
	var priorityNavOverflow = '.js-priority-nav__overflow';
	var priorityNavHiddenLinks = '.js-priority-nav__hidden-links';
	// Temp
	var priorityNavTrigger = '.js-priority-nav__trigger';

	// Elements
	// ------------------

	var $target = $(priorityNav);
	var $navbar = $target.find(priorityNavNavbar);

	// Init var
	// ------------------

	// array with all the links
	var priorityItem = [];
	var priorityItemLength = 0;
	// array with all the overflown links
	var priorityItemInOverflow = [];
	// array with all the links widths
	var itemWidths = [];
	var priorityNavOverflowWidth = 80;
	// empty string that will contain the nav
	var duplicatedNav = '';
	// temp
	var closingTime = 1000;
	var timer;

	// Utility functions
	// ------------------

	// show item in navbar and hide in drodpown
	function showItem(index) {
		$(priorityItem).eq(index).show();
		setTimeout(function () {
			$(priorityItemInOverflow).eq(index).hide();
		}, 0);
	}

	// hide item in navbar and show in drodpown
	function hideItem(index) {
		$(priorityItem).eq(index).hide();
		setTimeout(function () {
			$(priorityItemInOverflow).eq(index).show();
		}, 0);
	}

	// Duplicate the links
	function copyAllLinksToDropdown() {
		// empty the dropdown
		$target.find(priorityNavHiddenLinks).empty();
		// set the priorityItem array
		priorityItem = $navbar.children('li');
		priorityItemLength = priorityItem.length;

		// loop for writing all links in dropdown
		for (var i = 0, len = priorityItemLength; i < len; i++) {

			var $priorityItem = $(priorityItem[i]);
			var priorityItemHtml = $priorityItem.html();

			duplicatedNav += '<li>' + priorityItemHtml + '</li>';
		}

		$target.find(priorityNavHiddenLinks).append(duplicatedNav);

		// reassign the array with all the links in dropdown
		priorityItemInOverflow = $target.find(priorityNavHiddenLinks + ' > li');
	}

	// Get dimensions
	function getItemsDimensions() {
		itemWidths.length = 0;
		// Set the array
		priorityItem = $target.find(priorityNavItem);
		priorityItemLength = priorityItem.length;
		for (var i = 0, len = priorityItemLength; i < len; i++) {
			var $priorityItem = $(priorityItem[i]);
			// Write the widths
			itemWidths[itemWidths.length] = $priorityItem.outerWidth(true);
		}
	}

	// Main function to update position
	// ------------------

	function updatePositions() {
		// available width (priority nav width - "more" button width)
		var availableWidth = $target.width() - priorityNavOverflowWidth;
		// init the total with
		var totalWidth = 0;
		// boolean to know if overflow has started
		var hasOverflowed = false;
		// boolean to know the index of overflow
		var overflowedAtIndex = -1;

		priorityItemLength = priorityItem.length;

		for (var i = 0, len = priorityItemLength; i < len; i++) {
			totalWidth += itemWidths[i];
			// if available width is not enough
			if (totalWidth > availableWidth) {
				hideItem(i);
				// overflow !
				hasOverflowed = true;
				// which index is overflown ?
				if (overflowedAtIndex === -1) {
					overflowedAtIndex = i;
				}
			}
			// if there is enough space
			else {
					showItem(i);
				}
		}

		/*
  	// Debug
  	console.log('availableWidth : ', availableWidth);
  	console.log('totalWidth : ', totalWidth);
  	console.log('hasOverflowed ? : ', hasOverflowed);
  	console.log('overflowedAtIndex at : ', overflowedAtIndex);
  */

		// Management of the 'more' button
		// 'is-prioritzed' state shows/hides the overflow btn
		// and move the navbar consequently
		if (hasOverflowed) {
			$target.addClass('is-prioritized');
		} else {
			$target.removeClass('is-prioritized');
		}
	}

	// Init function
	// ------------------

	function init() {
		copyAllLinksToDropdown();
		getItemsDimensions();
		updatePositions();
	}

	// TEMP : Manage dropdown
	// ------------------

	// 'is-open' state shows/hides the hidden links
	$target.find(priorityNavTrigger).on('click', function (event) {
		event.preventDefault();
		$target.find(priorityNavHiddenLinks).toggleClass('is-open');
		clearTimeout(timer);
	});

	$target.find(priorityNavHiddenLinks).on('mouseleave', function () {
		timer = setTimeout(function () {
			$target.find(priorityNavHiddenLinks).removeClass('is-open');
		}, closingTime);
	}).on('mouseenter', function () {
		clearTimeout(timer);
	});

	// Call
	// ------------------

	if ($navbar.length) {
		init();
		$(window).on('resize', debounce(updatePositions, 250));
	}
}

// export default priorityNav;
priorityNav();
dropdown();

Comments