Demos by Paul Mains

Slide Menu Article

Welcome to the sliding menu demo.

This is a basic example of a sliding menu/navigation. There are no media queries in the demo, so the navigation may not look perfect at all resolutions.

I've put together some examples with the absolute minimum markup and styling. The only difference between them is some slight style tweeks that greatly change the slide effect.

The HTML

First things first, the basic markup structure.

                                
                                    <!-- The container is the relative element that holds everything together -->
                                    <div id="container">
                                        <!-- The slide menu is absolutely positioned with a transform on it to hide 80% of it's width -->
                                        <nav id="slide-menu">
                                            <!-- The button will have an event listener attached to it that adds the 'open-menu' class to the container. -->
                                            <button id="slide-menu-button"/>
                                        </nav>
                                        <!-- The wrapper in this case will be relatively positioned to allow the menu to slide over the top of it. -->
                                        <div id="wrapper">
                                            <!-- Now here is the bit i find odd. This is the content container and it's only job is to imitate the window for scrolling purposes. -->
                                            <div class="content">
                                                <!-- As long as there is a content and inner-content relationship set up, it will appear to behave like a normal window. -->
                                                <div class="inner-content">
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                
                            

The Sass

I'm using SASS to produce my stylesheets. In this example i'm using a couple of mixins for ease of viewing.

The mixins

The present mixins exist so i don't have to keep writing all the relevent browser specific prefixes.

                                
                                    @mixin box-sizing{
                                        -webkit-box-sizing: border-box;
                                        -moz-box-sizing: border-box;
                                        -o-box-sizing: border-box;
                                        box-sizing: border-box;
                                    }

                                    @mixin transition($seconds){
                                        -webkit-transition: ease-in-out $seconds;
                                        -moz-transition: ease-in-out $seconds;
                                        -o-transition: ease-in-out $seconds;
                                        transition: ease-in-out $seconds;
                                    }

                                    @mixin opacity-transition($seconds){
                                        -webkit-transition: opacity ease-in-out $seconds ;
                                        -moz-transition: opacity ease-in-out $seconds ;
                                        -o-transition: opacity ease-in-out $seconds ;
                                        transition: opacity ease-in-out $seconds ;
                                    }

                                    @mixin translate3d($x, $y, $z){
                                        -webkit-transform: translate3d($x, $y, $z);
                                        -moz-transform: translate3d($x, $y, $z);
                                        -o-transform: translate3d($x, $y, $z);
                                        transform: translate3d($x, $y, $z);
                                    }

                                    @mixin rotate($degrees){
                                        -webkit-transform: rotate($degrees);
                                        -moz-transform: rotate($degrees);
                                        -ms-transform: rotate($degrees);
                                        -o-transform: rotate($degrees);
                                        transform: rotate($degrees)
                                    }

                                    @mixin transform-origin($x, $y, $z){
                                        -webkit-transform-origin:$x $y $z;
                                        -moz-transform-origin:$x $y $z;
                                        -o-transform-origin:$x $y $z;
                                        transform-origin:$x $y $z;
                                    }
                                
                            

The style

If you were to use this styling you would no doubt think the result looked a hideous mess. This is purely the rudimentary styling that enables us to get the desired effect. The styling would need more work to make it look more palatable to the user. The styling for the this demo is a bit more fleshed out. The thing that gives the sliding effect is the CSS3 transform translate3d. It can be used in different ways, as demonstrated by the different examples in the menu.

                                
                                    //everything should be box sized!
                                    *, *:after, *::before {
                                        @include box-sizing;
                                    }

                                    //let's make sure everything is the height of the window
                                    //as we won't be using these elements for document scrolling
                                    html, body, #container{
                                        height:100%;
                                    }

                                    //as there is going to be some x-axis distance manipulation, the perspective needs to be set here
                                    #container{
                                        position:relative;
                                        overflow:hidden;
                                        -webkit-perspective: 2000px;
                                        perspective: 2000px;
                                    }

                                    //the after psuedo element is used to darken the content when the menu is activated
                                    //The transform style needs to be set to preserve its 3D position
                                    //this will allow the element to fall into the background
                                    #wrapper{
                                        position:relative;
                                        height:100%;
                                        width:100%;
                                        float:right;
                                        @include transition(0.25s);
                                        -webkit-transform-style: preserve-3d;
                                        transform-style: preserve-3d;

                                        &:after{
                                            position:absolute;
                                            top:0;
                                            right:0;
                                            width:0;
                                            height:0;
                                            background:rgba(0,0,0,0.4);
                                            content:'';
                                            opacity:0;
                                            @include opacity-transition(0.25s);
                                        }
                                    }

                                    //the menu by default is transformed out of view
                                    //when the menu is closed the content is set to 'MENU', when it's open it is set to 'CLOSE'
                                    //the nav bar is positioned outside of it's container so that it is always visible.
                                    #slide-menu{
                                        position:absolute;
                                        top:0;
                                        left:0;
                                        z-index:1000;
                                        width:200px;
                                        height:100%;
                                        background:#FFF;
                                        @include transition(0.25s);
                                        @include translate3d(-100%,0,0);

                                        button::after{
                                            content:'MENU';
                                        }

                                        .nav-bar{
                                            width:60px;
                                            height:100%;
                                            position:absolute;
                                            top:0;
                                            right:-60px;
                                            background:#FFF;
                                        }
                                    }

                                    //when the menu is open the site wrapper will fall backwards
                                    .menu-open{
                                        #wrapper{
                                            @include translate3d(100px,0,-800px);
                                        }

                                        #wrapper::after{
                                            width:100%;
                                            height:100%;
                                            opacity: 1;
                                        }

                                        #slide-menu{
                                            -webkit-transform: translate3d(0, 0, 0);
                                            transform: translate3d(0, 0, 0);

                                            button::after{
                                                content:'CLOSE';
                                            }
                                        }
                                    }

                                    //the content class will resume the duties of the document scroll
                                    .content{
                                        overflow-y:scroll;
                                        height:100%;
                                    }

                                    .inner-content{
                                        position:relative;
                                        width:100%;
                                    }
                                
                            

The javascript

Here we can see the script that triggers the menu operation. This will apply the class that designates that the menu is open to the container. It also controls what happens when the user clicks the body.


                                var slideMenu = (function(){
                                    /* A list of the elements that will be effected by this script.*/
                                    var container = document.getElementById('container'),
                                        menu = document.getElementById('slide-menu');
                                        menuButton = document.getElementById('slide-menu-button');

                                    /* The init function deciphers whether the device is a mobile device or not. This will provide the corrent event type.*/
                                    /* It then provides access to the menuEvent function and also makes sure it has been attached to the menu button at the appropriate time.*/
                                    function init(){
                                        var type = mobileBrowser() ? 'touchstart' : 'click';
                                        var menuEvent = function(e){
                                            if(hasClass(container, 'menu-open')){
                                                container.className = '';
                                                document.removeEventListener(type, menuEvent);
                                            }
                                        }

                                        menuButton.addEventListener(type, function(e){
                                            e.stopPropagation();
                                            e.preventDefault();
                                            if(container.className == 'menu-open'){
                                                menuEvent();
                                            }else{
                                                container.className = 'menu-open';
                                                document.addEventListener(type, menuEvent);
                                            }
                                        })
                                    }
                                    /* This is code that is mostly borrowed from http://detectmobilebrowsers.com/ */
                                    function mobileBrowser(){
                                        var mobile = false;
                                        (function(a,b){
                                            if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)
                                                ||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))mobile = true})
                                                (navigator.userAgent||navigator.vendor||window.opera);
                                        return mobile;
                                    }

                                    /* A basic class checker... i would usually use jquery for this. */
                                    function hasClass(el, cl) {
                                        return el.className 
                                                && new RegExp("(^|\\s)" + cl + "(\\s|$)").test(el.className);
                                    }

                                    init();
                                })();