droppy-menu

Pure JavaScript multi-level drop-down menu.

Usage no npm install needed!

<script type="module">
  import droppyMenu from 'https://cdn.skypack.dev/droppy-menu';
</script>

README

Droppy

Pure JavaScript multi-level drop-down menu.

At it's core, Droppy adds or removes CSS classes. This way you are able to control the menu appearance, just editing the .droppy_drop and .droppy__drop--active CSS classes.

<!-- Classic HTML menu -->
<nav>
  <ul class="menu">
    <li>
      <a href="#">First level - Link #1</a>
      <ul class="menu">
        <li><a href="#">Second level - Link #1</a></li>
        <li><a href="#">Second level - Link #2</a></li>
      </ul>
    </li>
    <li><a href="#">First level - Link #2</a></li>
    <li><a href="#">First level - Link #3</a></li>
  </ul>
</nav>

Quick Start

Start using Droppy in three steps.

  1. Download latest Droppy package from Github or via NPM or Bower.
# NPM
npm install droppy-menu --save
# Bower
bower install droppy-menu --save
  1. Add dist/droppy.min.css and dist/droppy.min.js to your web page.
<link href="/path/to/droppy/dist/droppy.min.css" rel="stylesheet" media="screen">
<script src="/path/to/droppy/dist/droppy.min.js"></script>
  1. Initialize Droppy in a custom script.
var element = document.querySelector( '.dropdown-menu' );
// Initialize Droppy.
var droppy = new Droppy( element, {
  parentSelector: 'li',
  dropdownSelector: 'li > ul.menu',
  triggerSelector: 'a'
} );

That's it. You're all set to start using Droppy.

Initialize with HTML

You can initialize Droppy in HTML, just adding data-droppy attribute to the menu element. Options can be set in its value using valid JSON.

<!-- Init with HTML -->
<nav class="dropdown-menu" data-droppy='{ "parentSelector": "li", "dropdownSelector": "li > ul.menu", "triggerSelector": "a", "closeOthers": true, "clickOutToClose": true, "clickEscToClose": true, "animationIn": '', "animationOut": '', "preventDefault": true }'>

Options

To make Droppy work properly you have to describe your menu structure by setting the options parentSelector, dropdownSelector and triggerSelector.

// Default options.
var droppy = new Droppy( element, {
  parentSelector: 'li',
  dropdownSelector: 'li > ul',
  triggerSelector: 'a',
  closeOthers: true,
  clickOutToClose: true,
  clickEscToClose: true,
  animationIn: '',
  animationOut: '',
  preventDefault: true
}, callbacks );
<nav class="dropdown-menu"> <!-- The Droppy's element -->
  <ul class="menu">
    <li> <!-- The parent selector "li" -->
      <a href="#">Link #1</a> <!-- The trigger selector "a" -->
      <ul class="menu"> <!-- The drop-down selector "li > ul" -->
        <li><a href="#">Link #1</a></li>
        <li><a href="#">Link #2</a></li>
      </ul>
    </li>
    <li><a href="#">Link #2</a></li>
    <li><a href="#">Link #3</a></li>
  </ul>
</nav>
  • dropdownSelector - It's a valid CSS selector of your drop-down. In the example above I have a drop-down when there is a <ul class="menu"> son of a <li>. That's why the dropdownSelector is set to li > ul.menu.

  • parentSelector - It's a valid CSS selector of your parent element. The parent element have to contain the trigger element and the drop-down element. It should be the closest parent. In the example above the closest parent of both - dropdownSelector and triggerSelector - is the <li> element. That's why the parentSelector is set to li.

  • triggerSelector - It's a valid CSS selector of the element that triggers the open or close event. In the example above the trigger is the <a> element. That's why the triggerSelector is set to a.

  • closeOthers - A boolean value. Set to true if you want keep open only one drop-down at a time.

  • clickOutToClose - A boolean value. Set to true if you want to close all the drop-downs by clicking on the outside of the current menu.

  • clickEscToClose - A boolean value. Set to true if you want to close all the drop-downs by clicking ESC.

  • animationIn - A CSS class where is declared an animation. This class will be applied at the open of a drop-down and removed at the end of the animation.

  • animationOut - A CSS class where is declared an animation. This class will be applied at the close of a drop-down and removed at the end of the animation.

  • preventDefault - A boolean value. If the triggerSelector is an element that fires an event, you can prevent the execution of the default behavior setting this option to true. E.g. If triggerSelector is a link, we want the browser opens the drop-down, not follows the link. Setting preventDefault to true prevents the browser to follow the link.

Methods

Methods are actions done by Droppy instances.

// Instantiate
var droppy = new Droppy( element, options, callbacks );

init()

Initialize a Droppy object. It adds Droppy CSS classes and events.

// Init Droppy object
droppy.init();

destroy()

Reset a Droppy instance to a pre-init state. It removes Droppy CSS classes and events.

// Reset Droppy instance to a pre-init state
droppy.destroy();

open( dropdown, withDescendants )

Open the given drop-down. If closeOthers is set to true, the other drop-downs will be closed before opening the current one.

  • {Element|DroppyNode} dropdown - The drop-down element to open.
  • {Boolean} [withDescendants=false] - Should open or not all the drop-downs in the given drop-down.
var dropdown = document.querySelector( '#menu-main .droppy__drop' )
// Open a drop-down
droppy.open( dropdown );

close( dropdown, withDescendants )

Close the given drop-down and all its descendants.

  • {Element|DroppyNode} dropdown - The drop-down element to close.
  • {Boolean} [withDescendants=true] - Should close or not all the drop-downs in the given drop-down.
var dropdown = document.querySelector( '#menu-main .droppy__drop' )
// Close a drop-down
droppy.close( dropdown );

toggle( dropdown, withDescendants )

Open or close the given drop-down.

  • {Element|DroppyNode} dropdown - The drop-down element to toggle.
  • {Boolean} [withDescendants=undefined] - Should toggle or not all the drop-downs in the given drop-down.
var dropdown = document.querySelector( '#menu-main .droppy__drop' )
// Toggle a dropdown
droppy.toggle( dropdown );

openAll()

Open all drop-downs of a menu.

// Open all drop-downs
droppy.openAll();

closeAll()

Close all drop-downs of a menu.

// Close all drop-downs
droppy.closeAll();

on( event, callback, context )

Subscribe to an event.

  • {string} event - The name of the event to subscribe to.
  • {function} callback - The function to call when the event is emitted.
  • context - The context to bind to the callback.
// The callback function.
function alertOpen( event ) {
  alert( 'Open!' );
  console.log( event );
}

// Subscribe to open event.
droppy.on( 'open', alertOpen );

once( event, callback, context )

Subscribe to an event once.

  • {string} event - The name of the event to subscribe to.
  • {function} callback - The function to call when the event is emitted.
  • context - The context to bind to the callback.
// SUbscribe to open event once.
droppy.once( 'open', alertOpen );

off( event, callback )

Unsubscribe to an event. If no callback is provided, unsubscribe from all events.

  • {string} event - The name of the event to unsubscribe from.
  • {function} callback - The function used when binding to the event.
// Unsubscribe to open event.
droppy.off( 'open', alertOpen );

Droppy.prototype.isInitialized( droppy )

It returns true if the given Droppy instance is initialized, false otherwise.

var droppy = new Droppy( element, options );
// Check if initialized
var initialized = Droppy.prototype.isInitialized( droppy );

Droppy.prototype.getInstance( element )

It returns the Droppy instance used by the given element.

var element = document.querySelector( '[data-droppy]' );
// Get the instance
var droppy = Droppy.prototype.getInstance( element );

Events

Events are "things" that happen at a specific point of execution. You can listen to Droppy's events and react to them. Here's a list of Droppy's events.

  • init - Dispatched when a Droppy object is initialized.
  • destroy - Dispatched when a Droppy object is destroyed.
  • open - Dispatched when a drop-down is opened.
  • close - Dispatched when a drop-down is closed.
  • openAll - Dispatched when all drop-downs of a menu are opened.
  • closeAll - Dispatched when all drop-downs of a menu are closed.

Callbacks

DEPRECATED - Callbacks will be removed in v2.0.0 in favor of events.

Callbacks are function called at a specific point of execution. Callbacks does not wait for animation end, this may cause the execution of afterOpen, afterClose, afterOpenAll and afterCloseAll when the drop-down is not completely opened or closed yet.

// Default callbacks.
var droppy = new Droppy( element, options, {
  beforeOpen: null,
  afterOpen: null,
  beforeClose: null,
  afterClose: null,
  beforeOpenAll: null,
  afterOpenAll: null,
  beforeCloseAll: null,
  afterCloseAll: null,
  beforeInit: null,
  afterInit: null,
  beforeDestroy: null,
  afterDestroy: null
} );

Define a callback

Define a callback in three steps.

  1. Create your callback function.
function alertCallback() {
  alert( 'Before open.' );
}
  1. Assign the function to the callbacks object.
var callbacks = {
  beforeOpen: alertCallback
}
  1. Create a Droppy object with the callbacks.
var droppy = new Droppy(element, options, callbacks);

Detect drop-down structure

Understanding how Droppy detect your drop-down menu structure, can help you set *Selector options correctly.

Droppy starts from dropdownSelector to detect your drop-down menu structure. For each drop-down, Droppy loops through element parents until reach the first element that matches the parentSelector. Once got the parent element, Droppy selects the first element child of the parent element that matches the triggerSelector.

Instead of querying at every click the DOM, Droppy stores your drop-down menu structure in the tree property. The tree is an array of DroppyNode instances.

var droppy = new Droppy( element, options, callbacks );
// Print the tree
console.log( droppy.tree );

Polyfills

Droppy uses a bunch of polyfills to be compatible with old browsers. Here's a list of polyfills used.