// http://callmenick.com/post/slide-and-push-menus-with-css3-transitions (function(window) { 'use strict'; /** * Extend Object helper function. */ function extend(a, b) { var key; for (key in b) { if (b.hasOwnProperty(key)) { a[key] = b[key]; } } return a; } /** * Each helper function. */ function each(collection, callback) { var i; var item; for (i = 0; i < collection.length; i++) { item = collection[i]; callback(item); } } /** * Menu Constructor. */ function Menu(options) { this.options = extend({}, this.options); extend(this.options, options); this._init(); } /** * Menu Options. */ Menu.prototype.options = { wrapper: '#o-wrapper', // The content wrapper. type: 'slide-left', // The menu type. menuOpenerClass: '.c-button', // The menu opener class names (i.e. the buttons). maskId: '#c-mask' // The ID of the mask. }; /** * Initialise Menu. */ Menu.prototype._init = function() { this.body = document.body; this.wrapper = document.querySelector(this.options.wrapper); this.mask = document.querySelector(this.options.maskId); this.menu = document.querySelector('#c-menu--' + this.options.type); this.closeBtn = this.menu.querySelector('.c-menu__close'); this.menuOpeners = document.querySelectorAll(this.options.menuOpenerClass); this._initEvents(); }; /** * Initialise Menu Events. */ Menu.prototype._initEvents = function() { // Event for clicks on the close button inside the menu. this.closeBtn.addEventListener('click', function(e) { e.preventDefault(); this.close(); }.bind(this)); // Event for clicks on the mask. this.mask.addEventListener('click', function(e) { e.preventDefault(); this.close(); }.bind(this)); }; /** * Open Menu. */ Menu.prototype.open = function() { this.body.classList.add('has-active-menu'); this.wrapper.classList.add('has-' + this.options.type); this.menu.classList.add('is-active'); this.mask.classList.add('is-active'); this.disableMenuOpeners(); }; /** * Close Menu. */ Menu.prototype.close = function() { this.body.classList.remove('has-active-menu'); this.wrapper.classList.remove('has-' + this.options.type); this.menu.classList.remove('is-active'); this.mask.classList.remove('is-active'); this.enableMenuOpeners(); }; /** * Disable Menu Openers. */ Menu.prototype.disableMenuOpeners = function() { each(this.menuOpeners, function(item) { item.disabled = true; }); }; /** * Enable Menu Openers. */ Menu.prototype.enableMenuOpeners = function() { each(this.menuOpeners, function(item) { item.disabled = false; }); }; /** * Add to global namespace. */ window.Menu = Menu; })(window); var slideLeft = new Menu({ wrapper: '#o-wrapper', type: 'slide-left', menuOpenerClass: '.c-button', maskId: '#c-mask' }); var slideLeftBtn = document.querySelector('#c-button--slide-left'); slideLeftBtn.addEventListener('click', function(e) { e.preventDefault(); slideLeft.open(); }); //---------------------------------------------------------------------------------------------- // https://addyosmani.com/resources/essentialjsdesignpatterns/book/ // http://www.phpied.com/3-ways-to-define-a-javascript-class/ // https://gist.github.com/mhulse/2abd5ffd828e0fae45cbeec86317b97d // https://gist.github.com/mhulse/3068831 // http://stackoverflow.com/a/1535687/922323 // jQuery-centric (based on above example): (function(namespace, $, undefined) { 'use strict'; var self; // Constructor: function Menu(options) { // Private variable: var privateVariable = 'foo'; console.log('instantiated'); // Public variable: this.publicVariable = 'bar'; this.options = $.extend({}, this.defaults, options); self = this; this._init(); } // Object default options: Menu.prototype.defaults = { billy: 'bobby' }; // Static variable shared by all instances: Menu.staticProperty = 'baz'; // Public property: Menu.prototype.test = 'foo'; // Instance method will be available to all instances but only load once in memory: Menu.prototype.publicMethod = function() { alert(this.publicVariable); }; // Private instance method: Menu.prototype._init = function() { console.log('_init', this.options); this._private.foo(); }; Menu.prototype._private = {}; Menu.prototype._private.foo = function() { console.log('_private', self.options.billy); }; // Add to namespace global: namespace.Menu = Menu; })((window.FT = (window.FT || {})), jQuery); var a = new FT.Menu(); a.test = 'baz'; var b = new FT.Menu({ billy: 'bubba' }); console.log(a.test, b.test); // Output: // // instantiated // _init Object {billy: "bobby"} // _private bobby // instantiated // _init Object {billy: "bubba"} // _private bubba // baz foo