npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@meom/navigation

v1.3.0

Published

MEOM navigation

Downloads

1,107

Readme

MEOM navigation

MEOM navigation gives head start for two types of navigations:

  1. "Click" navigation where sub menus open only on click.
  2. "Hover" navigation where sub menus open on hover also.

It's designed for WordPress navigation markup but can be tweaked for different use cases.

Demos

Usage

If you’re using a bundler (such as Webpack or Rollup), you can install @meom/navigation through npm like any other dependency:

npm install @meom/navigation

Then you can import the library in your JavaScript and instantiate your navigation.

import { Navigation } from '@meom/navigation';

// Mandatory elements.
const navElement = document.querySelector('.my-nav-wrapper');
const navToggle = document.querySelector('.my-nav-toggle');

// Bail if there is no nav nor toggle button.
if (!navElement || !navToggle) {
    return;
}

new Navigation(navElement, navToggle);

You can also import helper functions and use them in your Javascript.

import { Navigation, animate, updateAria } from '@meom/navigation';

Mandatory elements

There are two mandatory elements:

  1. navElement - Element which is hidden and releaved on smaller screens.
  2. navToggle - <button> element which trickers the navElement.

Markup

Recommended markup is using landmark nav with list of links.

<nav class="site-nav js-site-nav">
    <button
    class="site-nav__toggle js-site-nav-toggle" aria-controls="site-nav__items"
    aria-expanded="false"
    >
    Menu
    </button>
    <ul class="site-nav__items js-site-nav-items" id="site-nav__items">
        <li><a href="#">Home</a></li>
        <li class="menu-item-has-children">
            <a href="#">About</a>
            <ul class="sub-menu">
                <li><a href="#">Contact us</a></li>
                <li><a href="#">Company values</a></li>
                <li><a href="#">Staff</a></li>
                <li><a href="#">More info</a></li>
            </ul>
        </li>
        <li class="menu-item-has-children">
            <a href="#">Services</a>
            <ul class="sub-menu">
                <li><a href="#">Design</a></li>
                <li><a href="#">WordPress</a></li>
                <li class="menu-item-has-children"><a href="#">Hosting</a>
                    <ul class="sub-menu">
                        <li><a href="#">Premium</a></li>
                        <li><a href="#">Even better</a></li>
                        <li><a href="#">The fastest</a>
                    </ul>
                </li>
                <li><a href="#">Helping</a></li>
            </ul>
        </li>
        <li><a href="#">Blog</a></li>
    </ul>
</nav>

With this markup our JS would look like this:

import { Navigation } from '@meom/navigation';

// Mandatory elements.
const navElement = document.querySelector('.js-site-nav-items');
const navToggle = document.querySelector('.js-site-nav-toggle');

// Bail if there is no nav nor toggle button.
if (!navElement || !navToggle) {
    return;
}

new Navigation(navElement, navToggle);

Markup with your own <button> elements

You can use <button data-meom-nav="sub-toggle" aria-expanded="false" type="button"> for dropdowns elements directly in the markup without top level anchors. Note the mandatory attributes:

  • data-meom-nav="sub-toggle"
  • aria-expanded="false"
  • type="button"

Markup would look like this:

<nav class="site-nav js-site-nav">
    <button
    class="site-nav__toggle js-site-nav-toggle" aria-controls="site-nav__items"
    aria-expanded="false"
    >
    Menu
    </button>
    <ul class="site-nav__items js-site-nav-items" id="site-nav__items">
        <li><a href="#">Home</a></li>
        <li class="menu-item-has-children">
            <button class="site-nav__sub-toggle" data-meom-nav="sub-toggle" aria-expanded="false" type="button">About</button>
            <ul class="sub-menu">
                <li><a href="#">Contact us</a></li>
                <li><a href="#">Company values</a></li>
                <li><a href="#">Staff</a></li>
                <li><a href="#">More info</a></li>
            </ul>
        </li>
        <li class="menu-item-has-children">
            <button class="site-nav__sub-toggle" data-meom-nav="sub-toggle" aria-expanded="false" type="button">Services</button>
            <ul class="sub-menu">
                <li><a href="#">Design</a></li>
                <li><a href="#">WordPress</a></li>
                <li class="menu-item-has-children"><a href="#">Hosting</a>
                    <ul class="sub-menu">
                        <li><a href="#">Premium</a></li>
                        <li><a href="#">Even better</a></li>
                        <li><a href="#">The fastest</a>
                    </ul>
                </li>
                <li><a href="#">Helping</a></li>
            </ul>
        </li>
        <li><a href="#">Blog</a></li>
    </ul>
</nav>

Issues opening and closing the sub menus?

First try adding pointer-events: none; CSS to SVG icon inside the sub menu toggles.

For example:

.site-nav__sub-toggle svg,
.site-nav__sub-sub-toggle svg {
    pointer-events: none;
}

This prevents SVG icon being the clicked target element. In other words it should always be the <button> itself.

Hopefully this will be fixed in the JS at some point.

Options

There are several settings (object) which you can pass in as third argument:

new Navigation(navElement, navToggle, {
    action: 'click',
    subNavAnchors: '.js-site-nav-items > .menu-item-has-children > a',
    subSubNavAnchors: '.js-site-nav-items .sub-menu > .menu-item-has-children > a',
    toggleNavClass: true,
    toggleNavClassValue: 'is-opened',
    toggleSubNavClassValue: 'is-opened',
    closeNavOnEscKey: true,
    closeNavOnLastTab: false,
    closeNavOnDocClick: false,
    subNavClass: '.sub-menu',
    subToggleButtonClasses: '',
    subSubToggleButtonClasses: '',
    animateSubNav: false,
    animateSubNavClass: '',
    visuallyHiddenClass: 'screen-reader-text',
    expandChildNavText: 'Sub menu',
    dropDownIcon:
        '<svg class="icon" aria-hidden="true" focusable="false" width="13" height="8" viewBox="0 0 13 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l4.793 4.793a1 1 0 001.414 0L12 1" stroke-width="2" stroke-linecap="round"></path></svg>',

    onCreate: null,
    onOpenNav: null,
    onCloseNav: null,
    onOpenSubNav: null,
    onCloseSubNav: null,
    });

action (string)

Accepted values are either click or hover. Defaults to click.

More info in the section Features and thinking.

subNavAnchors (string)

Targets top level anchors which have sub menus.

In most cases this needs to be changed because it's hard to know classes used in your markup.

Defaults to .js-site-nav-items > .menu-item-has-children > a.

subSubNavAnchors (string)

Targets sub menu anchors which have sub menus.

In most cases this needs to be changed because it's hard to know classes used in your markup.

Defaults to .js-site-nav-items .sub-menu > .menu-item-has-children > a.

toggleNavClass (boolean)

If true, we toggle class provided in toggleNavClassValue in the element we show and hide on smaller screens.

Defaults to true.

toggleNavClassValue (string)

Class we toggle in the element we show and hide on smaller screens.

Defaults to is-opened.

toggleSubNavClassValue (string)

Class we toggle in the sub menus.

Defaults to is-opened.

closeNavOnEscKey (boolean)

By default script closes navigation when pressing ESC key.

Defaults to true.

closeNavOnLastTab (boolean)

Sometimes it's useful to close to navigation when Tab key is used on the last navigation anchor.

Defaults to false.

closeNavOnDocClick (boolean)

Sometimes it's useful not to close navigation when clicking the document.

Defaults to true.

animateSubNavClass (string)

Script have basic animation helpers but animations can be done with CSS alone.

Defaults to ''.

visuallyHiddenClass (string)

Visually hidden class.

Defaults to screen-reader-text.

expandChildNavText (string)

Text for expanding sub menus.

Default to Sub menu.

dropDownIcon (string)

Icon (SVG) inside buttons which triggeres sub menus.

onCreate (function)

Runs when script creates it's magic. navElement and navToggle are passed in as arguments.

Example:

onCreate: function (navElement, navToggle) {
    // Do something
},

onOpenNav (function)

Runs when navigation opens. navElement, navToggle and event are passed in as arguments.

Example:

onOpenNav: function (navElement, navToggle, event) {
    // Do something
},

onCloseNav (function)

Runs when navigation closes. navElement, navToggle and event are passed in as arguments.

Example:

onCloseNav: function (navElement, navToggle, event) {
    // Do something
},

onOpenSubNav (function)

Runs when sub menu opens. navElement, navToggle, submenu and event are passed in as arguments.

Example:

onOpenSubNav: function (navElement, navToggle, submenu, event) {
    // Do something
},

onCloseSubNav (function)

Runs when sub menu closes. navElement, navToggle, submenu and event are passed in as arguments.

Example:

onCloseSubNav: function (navElement, navToggle, submenu, event) {
    // Do something
},

Features and thinking

By default navigation script opens sub menus only on click. This happens by adding <button> element after <a> element and hiding <a> with hidden attribute.

Without JS kicking in:

<li class="menu-item-has-children">
    <a href="#">About</a>
</li>

With JS kicking in:

<li class="menu-item-has-children">
    <a href="#" hidden>About</a>
    <button>About</button>
</li>

Script copies link item text (About) to the button text. And there we have the button which triggeres the sub menu.

If list item doesn't have sub menu, nothing happens. Link will stay as link.

Hover menu

What if all top level items needs to be as links no matter do they have sub menus or not?

With option action: hover you can do exactly that.

Without JS kicking in:

<li class="menu-item-has-children">
    <a href="#">About</a>
</li>

With JS kicking in:

<li class="menu-item-has-children">
    <a href="#">About</a>
    <button><span class="screen-reader-text">Child menu</span><svg>...</svg></button>
</li>

We don't add hidden attribute in this case to the link. And <button> text is visually hidden and SVG icon is indicating the sub menu.

Other features

  • Close navigation and sub menus by Esc key or by clicking outside of the navigation.
  • Use aria-expanded on buttons to indicate state.
  • aria-controls is not really supported in assistive tecnology, therefor this is not implemented.

More reading about navigations