pulling

Flexible slideout menu for mobile webapps

Usage no npm install needed!

<script type="module">
  import pulling from 'https://cdn.skypack.dev/pulling';
</script>

README

Pulling

Flexible slideout menu for mobile webapps

const panel = document.querySelector('#panel');
const toggle = document.querySelectorAll('#toggle');
const menu = document.querySelector('#menu');

const pullin = Pulling.create({
  menu,
  panel,
});

toggle.addEventListener('click', () => pullin.toggle());

Installation

Pulling is available on npm.

  • npm install pulling
  • yarn add pulling

Features

  • Flicking gesture support
  • Supports multiple slideout menus
  • Standalone, dependency-free
  • Simple markup
  • Native scrolling
  • Easy customization
  • CSS transforms & transitions
  • Two different display modes
  • Just 10 Kb!

Usage

You just need a panel for your main content and a menu element:

<nav id="menu">
  <header>
    <h2>Menu</h2>
  </header>
</nav>

<main id="panel">
  <header>
    <h2>Panel</h2>
  </header>
</main>

Styling is simple, too. The library handles most things for you, so just add a background on your panel and menu, and add a min-height on the panel if you're using the reveal mode.

Then include Pulling and set up the menu.

const panel = document.querySelector('#panel');
const menu = document.querySelector('#menu');

const pullin = Pulling.create({
  menu,
  panel,
});

See test.html for a full example.

Browser Support

  • Chrome (IOS, Android, desktop)
  • Firefox (Android, desktop)
  • Safari (IOS, Android, desktop)
  • Opera (desktop)
  • IE 10+ (desktop and mobile)

API

Pulling.create(options)

Creates a new Pulling instance

options

  • menu - Element

    DOM element of menu

  • panel - Element

    DOM element of panel

  • side - "left" | "right" (default: left)

    side menu should display on

  • mode - "drawer" | "reveal" (default: drawer)

    whether the menu slide over the panel (drawer) or the panel should slide to reveal the menu (reveal)

  • margin - number (default: 25)

    the number of pixels from the edge of the viewport in which a touch event will be accepted

  • timing - number (default: 200)

    milliseconds of the transition animation for opening and closing, in the case where it isn't pulled out manually

  • timingFunction - string (default: ease)

    CSS3 transition timing function

  • width - number (default: 256)

    width of menu in pixels

  • sensitivity - number (default: 0.25)

    the speed needed to activate a flick

  • slope - number (default: 0.5)

    maximum ratio of vertical touch movement to horizontal touch movement that should be accepted

  • touch - boolean (default: true)

    Enable touch functionality

  • ignoreScrollables - boolean (default: true)

    Ignore horizontally scrollable elements

Instance Methods

All methods are chainable

#open()

Opens the menu. Emits beforeopen and opened events.

#close()

Closes the menu. Emits beforeclose and closed events.

#toggle(condition?: boolean)

Toggles the state of the menu (closed -> open, open -> closed).

  • If condition === true, it opens the menu
  • If condition === false, it closes the menu

#on(eventName: string, handler: Function)

Add an event handler for the event

#off(eventName: string, handler?: Function)

Remove an event handler for the event, or remove all handlers for the event

#ignore(selector: string)

Ignore touch events from a specific selector

#unignore(selector: string)

Remove selector from ignore list. Will not override ignored selectors.

#disable()

Disable all functionality of the menu

#enable()

Enable the menu's functionality

#disableTouch()

Disable touch event functionality for this menu

#enableTouch()

Enable touch event functionality for this menu

Events

  • beforeclose
    Emitted before close begins, including when dragging closed with touch
  • closed
    Emitted after close completes
  • beforeopen
    Emitted before open begins, including when dragging open with touch
  • opened
    Emitted after open completes
  • touchstart
    Emitted when a touch movement begins
  • touchmove
    Emitted on every movement due to touch
  • touchend
    Emitted when a touch movement ends

You can use these to support multiple menus by disabling one when the other opens:

pullinLeft.on('beforeopen', () => pullinRight.disableTouch());
pullinLeft.on('closed', () => pullinRight.enableTouch());
pullinRight.on('beforeopen', () => pullinLeft.disableTouch());
pullinRight.on('closed', () => pullinLeft.enableTouch());

State

  • .state.closed - boolean
  • .state.closing - boolean
  • .state.open - boolean
  • .state.opening - boolean

FAQ

How do I prevent it from opening when scrolling through a carousel?

You can either use .ignore(selector) to add the specific element selector to the ignore list, or you can do .ignore('[data-pullin-ignore]') to add that selector the the list and then just add the data-pullin-ignore attribute to every element you want ignored.

How do I add a toggle button?

// vanilla js
document.querySelector('.toggle-menu').addEventListener('click', () => pullin.toggle());

// jQuery
$('.toggle-menu').on('click', () => pullin.toggle());

How do I use two menus, one on each side?

You'll want to use events to keep one menu disabled while the other is open.

pullinLeft.on('beforeopen', () => {
  pullinRight.close().disableTouch();
});
pullinLeft.on('closed', () => pullinRight.enableTouch());
pullinRight.on('beforeopen', () => {
  pullinLeft.close().disableTouch();
});
pullinRight.on('closed', () => pullinLeft.enableTouch());