βΉοΈ This project is a fork from https://github.com/grubersjoe/slide-menu. See Original Demo
A library agnostic, dependency free multilevel page menu with a smooth slide effect based on CSS transitions and various options. Allows foldable submenu structures for desktop views.
Support: All current browsers and IE11+ (if using dist/slide-menu.ie.js
).
npm install smdm-slide-menu
Now import 'smdm-slide-menu/dist/slide-menu'
and import 'smdm-slide-menu/dist/slide-menu.css'
in your bundler or build system of choice or use a 1998 <script>
and <link>
tag. Afterwards SlideMenu
will be available in the global namespace (window.SlideMenu
).
To keep the bundle size small Internet Explorer 11 is not supported out of the box. If you need to support Internet Explorer 11 use dist/slide-menu.ie.js
instead.
All you need is the traditional menu HTML markup and a wrapper with the class slide-menu
. Menus can be nested endlessly to create the desired hierarchy. If you wish to programmatically control the menu, you should also set IDs to be able to use the API (see below).
Example
<nav class="slide-menu" id="example-menu">
<ul>
<li>
<a href="/">Home</a>
<ul>
<li><a href="#">Submenu entry 1</a></li>
<li><a href="#">Submenu entry 2</a></li>
<li><a href="#">Submenu entry 3</a></li>
</ul>
</li>
<li>
<a href="/blog">Blog</a>
</li>
<li>
<a href="/about">About</a>
</li>
</ul>
</nav>
Create the menu then like this:
const initSlideMenu = () => {
const menuElement = document.getElementById('example-menu');
const menu = new SlideMenu(menuElement);
};
if(window.SlideMenu) {
initSlideMenu()
} else {
window.addEventListener("sm.ready", initSlideMenu);
}
The SlideMenu()
constructor takes an optional second parameter to pass in various options:
Option | Description | Valid values | Default |
---|---|---|---|
backLinkAfter |
HTML to append to back link in submenus | HTML code | '' |
backLinkBefore |
HTML to prepend to back link in submenus | HTML code | '' |
keyClose |
Key used to close the menu | Any valid KeyboardEvent key | Escape |
keyOpen |
Key used to open the menu | Any valid KeyboardEvent key | undefined |
position |
Position of the menu | 'left' or 'right' |
'right' |
showBackLink |
Add a link to navigate back in submenus (first entry) | boolean | true |
submenuLinkBefore |
HTML to prepend to links with a submenu | HTML code | '' |
submenuLinkAfter |
HTML to append to links with a submenu | HTML code | '' |
closeOnClickOutside |
Menu closes when clicked outside menu element | boolean | false |
onlyNavigateDecorator |
Prevents navigation when clicking the link directly, triggers menu slide navigation only when clicking the link decorator elements | boolean | false |
menuWidth |
Width of the menu slider (without fold) | number | 320 |
minWidthFold |
Minimum window width in pixel for fold menu to be shown as fold not as slide | number | 640 |
transitionDuration |
Duration of slide animation in milliseconds | number | 300 |
dynamicOpenTarget |
Dynamically determine the default menu that will be opened based on current location.pathname or location.hash (especially useful for SPA's) |
boolean | false |
debug |
Shows verbose logs / warnings | boolean | false |
Example:
const initSlideMenu = () => {
const menu = new SlideMenu(document.getElementById('example-menu'),{
showBackLink: false,
submenuLinkAfter: '<strong>β</strong>'
});
};
You can call the API in two different ways:
-
Reuse the reference to the
SlideMenu
instance:const menu = new SlideMenu(document.getElementById('example-menu')); // ... later menu.close();
-
The
SlideMenu
instance is also added as property of the menu DOM element. So if you need to control an existing menu without a reference to it, you can fetch it any time this way:const menu = document.getElementById('example-menu')._slideMenu; menu.open();
close(animate = true)
- Close the menuback(closeFold = false)
- Navigate on level back if possible. Additionally closes foldnavigateTo(target)
Open the menu level which contains specified menu element.target
can either be adocument.querySelector
compatible string selector or the the DOM element (inside the menu). The first found element (if any) will be used.show(animate = true)
- Shows the menu if closedopen(animate = true)
- Opens the menu if closed an potentially navigates to target- If attribute
data-open-target="..."
is provided in the root menu element the menu will navigate to that target per default. Target must be adocument.querySelector
compatible string - If option
dynamicOpenTarget
is true opens submenu matching the currently active slug or hash in the browser URL (e.g. if you are on the pagehttps://example.com/about
slide menu will try to open the submenu of the item<a href="/about">About</a>
or open the submenu containing it)
- If attribute
toggle(animate = true)
- Toggle the menu (if defined opens thedata-open-target="..."
/dynamicOpenTarget
by default)
For initializing SlideMenu, as soon as the SlideMenu
class can be used globally the event sm.ready
is fired on the window
object.
SlideMenu
emits events for all kind of actions, which trigger as soon as the action is method is called. Plus, all events have also an <event>-after
equivalent, which is fired after the step is complete (completely animated).
sm.back[-after]
fires immediately when navigating backwards in the menu hierarchy or after the animation is complete respectively.sm.close[-after]
fires immediately when theclose()
method is called or after the animation is complete respectively.sm.forward[-after]
fires immediately when navigating forward in the menu hierarchy or after the animation is complete respectively.sm.navigate[-after]
fires immediately when calling thenavigateTo()
method or after the animation is complete respectively.sm.open[-after]
fires immediately when theopen()
method is called or after the animation is complete respectively.sm.init
fires when the slide menu object is finished initializing.
Make sure to add the event listener to the HTML element, which contains the menu, since the events for this specific menu are dispatched there:
const initSlideMenu = () => {
const menuElement = document.getElementById('example-menu');
const menu = new SlideMenu(menuElement);
// Attach the event listener to the *DOM element*, not the SlideMenu instance
menuElement.addEventListener('sm.open', function () {
console.log('The menu opens');
});
menuElement.addEventListener('sm.open-after', function () {
console.log('The menu has opened');
});
}
if(window.SlideMenu) {
initSlideMenu();
} else {
window.addEventListener("sm.ready", function () {
initSlideMenu();
});
}
To open a specific submenu with the open
or toggle
action you can give the slide menu element the attribute data-open-target
and pass the id, href or a css selector for the desired target submenu item. Example:
<nav class="slide-menu" id="example-menu" data-open-target="default-submenu">
<ul>
<li>
<a href="/blog">Blog</a>
</li>
<li>
<a id="default-submenu" href="/submenu">Submenu</a>
<ul>
<!-- this submenu wil be opened as default -->
<li><a href="#">Submenu entry 1</a></li>
<li><a href="#">Submenu entry 2</a></li>
<li><a href="#">Submenu entry 3</a></li>
</ul>
</li>
<li>
<a href="/about">About</a>
</li>
</ul>
</nav>
To add an overlay over the screen while the menu is open add an element with class="slide-menu__overlay"
before the slide menu. It uses --smdm-sm-color-overlay
as default color which can be adjusted as needed.
<div class="slide-menu__overlay">
<nav class="slide-menu" id="example-menu">
...
</nav>
Buttons to control the menu can be created easily. Add the class slide-menu__control
to anchors or buttons and set the data
attributes target
to the ID of the desired menu and action
to the API method:
<button type="button" class="slide-menu__control" data-target="example-menu" data-action="open">Open</button>
<button type="button" class="slide-menu__control" data-target="example-menu" data-action="back">Back</button>
<button type="button" class="slide-menu__control" data-target="example-menu" data-action="toggle">Toggle</button>
<button type="button" class="slide-menu__control" data-target="example-menu" data-action="navigateTo" data-arg="blog">Navigate to #blog</button>
Inside the menu container the attribute data-target
can be omitted or set to to the string this
to control this menu.
<a class="slide-menu-control" data-target="example-menu" data-action="close">Close this menu</a>
<a class="slide-menu-control" data-target="this" data-action="close">Close this menu</a>
<a class="slide-menu-control" data-action="close">Close this menu</a>
A dynamic menu title slide-menu__title
can optionally be added anywhere in the menu structure (e.g. in the slide-menu__controls
). The title will be updated dynamically according to the current menu level.
<nav class="slide-menu" id="test-menu-left">
<div class="slide-menu__controls">
...
<div class="slide-menu__title">Submenu Title</div>
...
</div>
<ul>
...
</ul>
</nav>
Foldable submenus can be created using the class slide-menu__item--has-foldable-submenu
on the item that precedes the submenu.
...
<li>
<a class="slide-menu__item--has-foldable-submenu" href="#"><span>Foldable</span></a>
<ul>
...foldable submenu items go here...
</ul>
</li>
...
Foldable Submenus will only fold open if the window width is bigger than the configured minWidthFold
.
Any arbitrary additonal content (e.g. search input fields, detail info, images,...) can be inserted anywhere in the menu structure. The class slide-menu--additional-content
provides standard padding like it is used for the menu items. Example:
<nav class="slide-menu" id="test-menu-left">
<div class="slide-menu__controls">
...
</div>
<div class="slide-menu--additional-content">
<img src="https://studiomitte.com/build/images/logo/logo-white.svg" alt="Studiomitte Logo">
<p>Some detail text</p>
</div>
<ul>
...
</ul>
</nav>
To add Links for closing the menu in the end of Submenus for convenient keyboard navigation you can add additional navigation items in the HTML with class="slide-menu__control"
and a data-action="close"
like the following example:
<ul>
<li>...Menu item 1...</li>
<li>...Menu item 2...</li>
<li><a href="#" data-action="close" class="slide-menu__control">X Close</a></li>
</ul>
If you want to insert backlinks manually like in the following example use the attribute data-action="back"
on them. Additonally, use the attribute data-arg="close-fold"
on the backlink, if you intend to close all the currently open foldable submenus.
<ul>
<li><a href="#" data-action="back" class="slide-menu__control">π Backlink</a></li>
<li>...Menu item 1...</li>
<li>...Menu item 2...</li>
</ul>
π‘ Backlinks can also be generated automatically with the option
showBackLink
and the contentsbackLinkAfter
/backLinkBefore
.
Basic Styling is provided by slide-menu
. To adjust it to your theme select the elements by the classes slide menu applies to the elements.
The following default CSS variables can be overwritten as needed:
:root {
--smdm-sm-transition-easing: ease-in-out;
--smdm-sm-color-bg: rgb(10 10 9);
--smdm-sm-color-text: rgb(238 237 235);
--smdm-sm-color-active: rgb(32 31 29);
--smdm-sm-color-hover: rgb(20 20 19);
--smdm-sm-color-controls: rgb(20 20 19);
--smdm-sm-color-overlay: rgba(0, 0, 0, 0.3);
--smdm-sm-control-content-back: 'β';
--smdm-sm-control-content-close: 'β';
--smdm-sm-item-padding: 0.9rem 1.5rem;
}
# if using with ddev first do
ddev start
ddev ssh -s frontend
# then open https://slide-menu.frontend.ddev.site:9999/
# for development run
npm install
npm run watch
# run e2e tests (in cypress container)
npm run test
Create a production build for release:
# outside of ddev container
npm run build
# or
npm run pre-version # includes linting & checks
https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages
npm login
npm run version-patch # increments & commits patch version number
# or
npm run version-minor # increments & commits minor version number
# commit changes
npm run post-version
Test package in a project and install it locally:
npm install path/to/my-package
Install package through npm package registry:
npm install smdm-slide-menu@latest