mmenu tutorial

In this tutorial, we'll create an off-canvas app look-alike menu using the mmenu.js plugin. Just follow the tutorial step by step and you'll have it up and running in no time.

Lets start with creating a .html file and add some basic markup for a header and the content. For this tutorial we'll assume you already have a stylesheet (called my-styles.css) for styling this markup.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>My webpage</title>
        <link href="path/to/my-styles.css" rel="stylesheet" />
    </head>
    <body>
        <div id="my-page">
            <div id="my-header"></div>
            <div id="my-content">
                <h1>Page title</h1>
                <p>Some content.</p>
            </div>
        </div>
    </body>
</html>

Note the ID "my-page" on the <div />, we'll be using it later on to target the page when firing the plugin.

Next, include the mmenu .js and .css files. Most commonly, .css files are located in the <head /> and .js files are located before the closing <body />.

<!DOCTYPE html>
<html>
    <head>
        ...
        <link href="path/to/mmenu.css" rel="stylesheet" />
    </head>
    <body>
        ...
        <script src="path/to/mmenu.js"></script>
    </body>
</html>

The page

As you might have noticed, we've wrapped the header and content in a single <div /> (and we gave this <div /> the ID "my-page"). If you can, you should always do this when using the mmenu.js plugin.

In this tutorial, we'll be referring to this wrapper (<div id="my-page" />) as the "page".

The "page" is best off without a (min-/max-)width and (min-/max-)height, padding, border, margin, position, top, right, bottom, left or any other CSS value that changes its size and position.

If you're using some other element than a <div /> for the "page" (for example a <section />), you must specify this in the offCanvas.page.nodetype option in the configuration object. Since this option probably applies to all menus on your webpage, you could also override it in the plugin defaults.

The below example assumes you already have some knowledge of firing the mmenu.js plugin.

Override instance configuration
<script>
    document.addEventListener(
        "DOMContentLoaded", () => {
            new Mmenu( "#my-menu", {
                // options
            }, {
                // configuration
                offCanvas: {
                    page: {
                        nodetype: "section"
                    }
                }
            });
        }
    );
</script>
Override plugin defaults
<script>
    Mmenu.configs.offCanvas.page.nodetype = "section";

    document.addEventListener(
        "DOMContentLoaded", () => {
            new Mmenu( "#my-menu", {
                // options
            });
        }
    );
</script>

Keep reading if you want to know why wrapping all markup in a single wrapper is important. Otherwise, continue to setup the menu.

Why wrap all markup?

Because some of the logic in the plugin is based on the assumption that there is only one element it should consider to be the "page".

Why is it not required?

Because if you don't wrap all markup, the plugin will do it for you. Have a look at the HTML before and after firing the plugin.

HTML Before
<body>
    <div id="my-header"></div>
    <div id="my-content"></div>
    <div id="my-footer"></div>
</body>
HTML after firing mmenu
<body>
    <div class="mm-page">
        <div id="my-header"></div>
        <div id="my-content"></div>
        <div id="my-footer"></div>
    </div>
</body>

Then what's the problem?

If you let the mmenu.js plugin (or actually any other JavaScript) wrap the HTML, a <video /> might restart (or not start at all), JavaScript in an inline <script /> will run twice, an <iframe /> will reload and so on.

Best practice

Even if you do wrap all markupmarkup, some other JavaScript plugin or a third party widget can add HTML to the <body />, forcing the mmenu.js plugin to (once again) wrap all markup.

HTML Before
<body>
    <div id="my-page">
        <div id="my-header"></div>
        <div id="my-content"></div>
        <div id="my-footer"></div>
    </div>
</body>
After firing a lightbox
<body>
    <div id="my-page">
        <div id="my-header"></div>
        <div id="my-content"></div>
        <div id="my-footer"></div>
    </div>
    <div id="lightbox"></div>
</body>
After firing mmenu
<body>
    <div class="mm-page">
        <div id="my-page">
            <div id="my-header"></div>
            <div id="my-content"></div>
            <div id="my-footer"></div>
        </div>
        <div id="lightbox"></div>
    </div>
</body>

It is therefor recommended you tell the plugin what HTML element it should consider to be the "page". This way, the plugin can simply use that element and it does not need to wrap all markup in a wrapper.

You can do this by using the offCanvas.page.selector option in the configuration object. Since this option probably applies to all menus on your webpage, you could also override it in the plugin defaults.

The below example assumes you already have some knowledge of firing the mmenu.js plugin.

Remember the ID "my-page" on the <div />? We're now using it to target the "page".

Override instance configuration
<script>
    document.addEventListener(
        "DOMContentLoaded", () => {
            new Mmenu( "#my-menu", {
                // options
            }, {
                // configuration
                offCanvas: {
                    page: {
                        selector: "#my-page"
                    }
                }
            });
        }
    );
</script>
Override plugin defaults
<script>
    Mmenu.configs.offCanvas.page.selector = "#my-page";

    document.addEventListener(
        "DOMContentLoaded", () => {
            new Mmenu( "#my-menu", {
                // options
            });
        }
    );
</script>

Next up:
Setup the menu