All Projects → jayfreestone → priority-plus

jayfreestone / priority-plus

Licence: other
A modern implementation of the priority plus navigation pattern.

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language
HTML
75241 projects
CSS
56736 projects

Projects that are alternatives of or similar to priority-plus

Sidr
Sidr is a jQuery plugin for creating side menus and the easiest way for doing your menu responsive.
Stars: ✭ 2,924 (+9646.67%)
Mutual labels:  navigation, responsive, menu
ml-stack-nav
Customizable, responsive, accessible, easy-to-use multi-level stack navigation menu with slide effect.
Stars: ✭ 20 (-33.33%)
Mutual labels:  navigation, responsive, menu
Quickmenu
The new era of mobile navigation for the web, we're out of hamburgers.
Stars: ✭ 119 (+296.67%)
Mutual labels:  navigation, responsive, menu
React Responsive Navbar
Nothing crazy, nothing flashy, just a simple, flexible & completely customisable responsive navigation bar component.
Stars: ✭ 42 (+40%)
Mutual labels:  navigation, responsive, menu
React Command Palette
An accessible browser compatible javascript command palette
Stars: ✭ 140 (+366.67%)
Mutual labels:  navigation, menu
Draggablemenu
A draggable menu that shows a thumbnail preview of an image grid
Stars: ✭ 117 (+290%)
Mutual labels:  navigation, menu
Layerjs
layerJS: Javascript UI composition framework
Stars: ✭ 1,825 (+5983.33%)
Mutual labels:  navigation, menu
Mmenu Js
The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp.
Stars: ✭ 2,535 (+8350%)
Mutual labels:  navigation, menu
Promotion Menu
RubyMotion gem allowing you to easily setup a facebook or Path style hidden slide menu easily with the ProMotion gem.
Stars: ✭ 78 (+160%)
Mutual labels:  navigation, menu
React Site Nav
A kick ass site menu powered by styled components inspired by Stripe.
Stars: ✭ 155 (+416.67%)
Mutual labels:  navigation, menu
Dropdownmenukit
UIKit drop down menu, simple yet flexible and written in Swift
Stars: ✭ 246 (+720%)
Mutual labels:  navigation, menu
Pushy
Pushy is a responsive off-canvas navigation menu using CSS transforms & transitions.
Stars: ✭ 1,488 (+4860%)
Mutual labels:  navigation, menu
Bootstrap Dropdown Hover
Bootstrap based responsive mulltilevel dropdown navigation menu with fascinating animations
Stars: ✭ 115 (+283.33%)
Mutual labels:  navigation, menu
Responsive Sidebar Navigation
An easy-to-integrate side, vertical navigation, ideal for dashboards and admin areas.
Stars: ✭ 111 (+270%)
Mutual labels:  navigation, responsive
vue-bottom-navigation
Vue bottom navigation
Stars: ✭ 56 (+86.67%)
Mutual labels:  navigation, menu
Hc Offcanvas Nav
JavaScript library for creating toggled off-canvas multi-level navigations, allowing endless nesting of submenu elements, supporting swipe gestures, keyboard interactions and ARIA attributes.
Stars: ✭ 201 (+570%)
Mutual labels:  navigation, menu
curved-menu
VanillaJS fully configurable curved menu (circular navigation)
Stars: ✭ 30 (+0%)
Mutual labels:  vanilla-javascript, menu
SidebarOverlay
Yet another implementation of sidebar menu, but here your menu appears over the top view controller.
Stars: ✭ 66 (+120%)
Mutual labels:  navigation, menu
Accordion
Silky-smooth accordion widgets with no external dependencies.
Stars: ✭ 32 (+6.67%)
Mutual labels:  navigation, menu
Eeh Navigation
An AngularJS menu module.
Stars: ✭ 74 (+146.67%)
Mutual labels:  navigation, menu

priorityPlus

Github build status npm npm bundle size semantic-release

A modern implementation of the priority plus navigation pattern.

Animation showing nav items moving to and from the overflow nav.

You can see a demo on the landing page.

There's also a Glitch pen available here with a different, alternatively styled example. Check out the source.

The short stuff:

  • Vanilla JS, dependency free. Available as an ES6 module, or a drop-in IIFE assigned to the global priorityPlus.
  • Uses the IntersectionObserver API instead of width-based calculations.
  • Toggles the appropriate WAI-ARIA attributes to remain accessible.
  • Provides a class hook to style the menu differently when all items are in the overflow/hidden.
  • Provides a way to update the overflow toggle button with the hidden item count.

Comes in at under 2.5kb after gzip.

What is it

As Brad explains:

The Priority+ pattern...exposes what’s deemed to be the most important navigation elements and tucks away less important items behind a “more” link. The less important items are revealed when the user clicks the “more” link.

Diagram overview

This library implements the pattern by fitting as many navigation items as possible into the 'primary' navigation, and then automatically moving the rest into a dropdown. If more space becomes available, the links are gradually re-instated into the primary navigation.

There are already examples of libraries that follow this behaviour, such as PriorityNav.js. However most of these were written before the advent of modern browser APIs such as the IntersectionObserver, operating by measuring the parent and child elements, then calculating how many items can (and cannot) fit.

This library, however, uses an IntersectionObserver to avoid costly measurements, instead relying on the browser to tell us when an element 'intersects' with the edge of the viewport. The result is faster - and generally snazzier.

How it works

When initiated, the library creates a new version of your navigation with the required markup, including a toggle button:

<div data-main class="p-plus">
  <div class="p-plus__primary-wrapper">
    <ul data-primary-nav class="p-plus__primary" aria-hidden="false">
      <li data-nav-item>
        <a href="#">Home</a>
      </li>
      <!-- etc -->
    </ul>
  </div>
  <button data-toggle-btn class="p-plus__toggle-btn" aria-expanded="false">
    <span aria-label="More">+ (0)</span>
  </button>
  <ul data-overflow-nav class="p-plus__overflow" aria-hidden="true"></ul>
</div>

It also clones this version, so there are actually two versions of the new navigation living on the page. One is the visible navigation that the library will add and remove elements from, and the other is an invisible copy that always retains the full set of nav items (which are forced to overflow horizontally).

Diagram showing how the clone navigation overflows the wrapper.

As the items overflow, they trigger the parent's IntersectionObserver. This means we can easily detect when (and in which direction) a new nav item clashes with the outer boundary of the navigation.

Once we detect a collision, we store which navigation it should now belong to (primary or overflow) and update both in the DOM.

Browser support

This library is designed to work with modern browsers. Both JavaScript bundles are transpiled down to ES6, and include syntax such as template-literals and the spread operator. If you'd like priorityPlus to work in (for instance) Internet Explorer, you'll need to bring your own transpilation down to ES5. When using Webpack, this would usually involve changing your exclude to be non-inclusive of the library:

exclude: /node_modules\/(?!priority-plus)/,

You will need to bring your own support for the IntersectionObserver API through a polyfill.

Installation

Install from NPM:

npm install priority-plus

Or use a CDN if you're feeling old-school:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/priority-plus/dist/priority-plus.css">
<!-- Will be available globally as priorityPlus -->
<script defer src="https://cdn.jsdelivr.net/npm/priority-plus/dist/priority-plus.js"></script>

Setup

You can create a new instance by passing in an HTMLElement that is the direct parent of the navigation items:

<nav>
  <ul class="js-p-target">
    <li><a href="/">Home</a></li>
    <li><a href="/">About</a></li>
    <li><a href="/">Work</a></li>
    <li><a href="/">Services longer nav title</a></li>
    <li><a href="/">Contact</a></li>
  </ul>
</nav>
// Doesn't have to be SASS, just ensure the CSS is included.
@import "node_modules/priority-plus/dist/priority-plus";
import priorityPlus from 'priority-plus';
priorityPlus(document.querySelector('.js-p-target'));

It's important that the element is the immediate parent, since internally the library iterates over the children as the basis for the new navigation items.

Methods

The following methods are available on a new instance, e.g.:

const inst = priorityPlus(document.querySelector('.js-p-target'));
console.log(inst.getNavElements());

getNavElements(): { [key: string]?: HTMLElement|HTMLElement[] }

Retrieves an object containing references to each element in the primary generated navigation.

on(eventType: string, cb: Function)

Sets up an event listener on the instance (not the target element). See events for a list of the events that are triggered.

Example:

inst.on('itemsChanged', () => console.log('Items changed'));

off(eventType: string, cb: Function)

Destroys an event listener.

Example:

const callback = () => console.log('Items changed');
inst.on('itemsChanged', callback);
// etc
inst.off('itemsChanged', callback);

setOverflowNavOpen(open: boolean)

Opens or closes the overflow navigation programatically.

Example:

inst.setOverflowNavOpen(true);

toggleOverflowNav()

Opens the overflow nav if closed, closes it if open.

Example:

inst.toggleOverflowNav();

Options

openOnToggle

You can disable the default behaviour of automatically opening the overflow when the toggle is clicked by passing false. If you wanted to re-implement your own toggle behaviour, you could do so by listening for the toggleClicked event:

const inst = priorityPlus(document.querySelector('.js-p-target'), {
  openOnToggle: false,
})

inst.on('toggleClicked', () => {
  // Re-implement existing behaviour
  inst.toggleOverflowNav();
})

collapseAtCount

If you'd like to collapse into the overflow when the primary navigation becomes depleted, you can do with the collapseAtCount option:

priorityPlus(document.querySelector('.js-p-target'), {
  collapseAtCount: 2,
});

The above will move all menu items into the overflow if only two can 'fit' into the primary. This is essentially a way to avoid orphan nav items.

Classes

If you'd like to override the default classes, you can pass in a classNames object like so:

priorityPlus(document.querySelector('.js-p-target'), {
  classNames: {
    // Will override the p-plus class.
    // Other classes will be un-touched.
    wrapper: ['my-p-plus'],
  },
});

Each class override must be passed as an array.

Option Default Explanation
container
p-plus-container
This is the wrapper that collects both 'clones' of the navigation. Its purpose is to provide a way to obscure the clone.
main
p-plus
The class applied to each of the top-level navigation wrappers. Be aware it applies to both the clone and the visible copy.
primary-nav-wrapper
p-plus__primary-wrapper
Outer wrapper for the 'primary' (non-overflow) navigation.
primary-nav
p-plus__primary
Inner wrapper for the 'primary' (non-overflow) navigation.
overflow-nav
p-plus__overflow
Wrapper for the overflow navigation.
toggle-btn
p-plus__toggle-btn
Applied to the dropdown menu toggle button.

Templates

innerToggleTemplate(String|Function)

Default: 'More'

Overrides the inner contents of the 'view more' button. If you pass a string, then it will only render once, but if you pass it a function it will re-render every time the navigation is updated.

The function receives an object containing two parameters, toggleCount (the number of items in the overflow) and totalCount (which is the total number of navigation items).

Example:

priorityPlus(document.querySelector('.js-p-target'), {
  innerToggleTemplate: ({ toggleCount, totalCount }) => `
    Menu${toggleCount && toggleCount !== totalCount ? ` (${toggleCount})` : ''}
  `,
});

Be aware that if you alter the width of the element by changing its content, you could create a loop wherein the button updates, triggering a new intersection, which causes the button to update (and so on). Therefore it's probably a good idea to apply a width to the button so it remains consistent.

Events

Arguments are provided via the details property.

Name Arguments Description
showOverflow None Triggered when the overflow nav becomes visible.
hideOverflow None Triggered when the overflow nav becomes invisible.
itemsChanged overflowCount (The number of items in the overflow nav) Triggered when the navigation items are updated (either added/removed).
toggleClicked original (The original click event) Triggered when the overflow toggle button is clicked.

Defining a 'mobile' breakpoint

You should never have to base the amount of visible navigation items visible on the viewport size.

However, if you would like to break (early) to the 'mobile' view at a pre-defined point, you can do so with just CSS.

Simply add a rule that causes the first item in the navigation to expand beyond the viewport, like so:

@media (max-width: 40em) {
  .p-plus__primary > li:first-child {
    width: 100%;
  }
}

Troubleshooting

Flex nav collapsing

If your menu is part of an auto-sized flex-child, it will probably need a positive flex-grow value to prevent it reverting to its smallest form. For instance:

<header class="site-header">
  <h1 class="site-header__title">My great site title</h1>
  <nav class="site-header__nav">
    <ul class="site-nav js-site-nav">
      <li><a href="#">Services</a></li>
      <li><a href="#">Thinking</a></li>
      <li><a href="#">Events</a></li>
    </ul>
  </nav>
</header>
.site-header {
  display: flex;
  align-items: center;
}

/**
 * Prevents nav from collapsing.
 */
.site-header__nav {
  flex-grow: 1;
}

Navigation event listeners

priorityPlus makes a copy of your menu, rather than reusing the original. Classes and attributes are carried over, but not event listeners. This means that any additional libraries or JavaScript which operate on the menu and its children needs to be run (or re-run) after initialization:

priorityPlus(document.querySelector('.js-p-target'));
// .js-p-target is *not* the same element, but has been cloned and replaced
loadLibrary(document.querySelector('.js-p-target'));

If your use-case is not covered by this, please raise an issue.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].