Skip to content

Instantly share code, notes, and snippets.

@tlaak
Last active September 24, 2016 20:05
Show Gist options
  • Save tlaak/928ac49362b2f6b0767c3bee6e6e9ab3 to your computer and use it in GitHub Desktop.
Save tlaak/928ac49362b2f6b0767c3bee6e6e9ab3 to your computer and use it in GitHub Desktop.
jQuery plugin for a custom megamenu. Found from the depths of my Dropbox
/**
* @version 0.1 Alpha
* @author Timo Laak 7/6/2013
*
*/
(function($) {
/**
* Constants for recurring use
*/
var constants = {
/** @const */
PLUGIN_NAME: 'bmmMegamenu',
/** @const */
ACTIVE_PARENT_CLASS: 'active',
/** @const */
ACTIVE_NODE_CLASS: 'node-active',
/** @const */
ACTIVE_SHEET_CLASS: 'sheet-active',
/** @const */
BTN_TOGGLE_CLASS: 'btn-toggle-node',
/** @const */
PLUGIN_NAME_CLASS: 'bmm-megamenu'
};
/**
* Templates for repeating elements
*/
var templates = {
toggleButton: '<i class="' + constants.BTN_TOGGLE_CLASS +'"></i>'
};
var helpers = {
isTouch: function() {
if (window.navigator.msMaxTouchPoints) {
return true;
}
if (typeof Modernizr === "object" && Modernizr.touch) {
return true;
}
return false;
}
};
/**
* Plugin methods
*/
var methods = {
init: function(options) {
if (!this.data('navigationDomBackup')) {
this.data('navigationDomBackup', this.html());
}
// Check the requested layout and destroy & build only if it's not yet active
if (options.layout === 'sheet' && this.data('layout') !== 'sheet') {
methods.destroyTree.apply(this);
methods.restore.apply(this);
methods.buildSheet.apply(this);
} else if (options.layout === 'tree' && this.data('layout') !== 'tree') {
methods.destroySheet.apply(this);
methods.restore.apply(this);
methods.buildTree.apply(this);
}
return this;
},
buildTree: function () {
this.addClass(constants.PLUGIN_NAME_CLASS);
this.data('initialized', 'true');
this.hide();
var root = this.children('ul');
var nodes = root.find('ul');
for (var x = 0, length = nodes.length; x < length; x++) {
var node = new Node(nodes[x], this);
if (node.isActive()) {
node.toggle();
}
}
// Set the active layout
this.data('layout', 'tree');
return this;
},
buildSheet: function () {
this.show();
this.addClass(constants.PLUGIN_NAME_CLASS);
this.data('initialized', 'true');
var topLevel = this.children('ul').children('li'), timerOutId;
topLevel.on('mouseenter.topLevel touchstart', function (event) {
var _this = this;
// Disable main level link click events on touchstart
if (event.type === "touchstart") {
topLevel.children('a').click(function(event) {
event.stopPropagation();
event.preventDefault();
});
}
// Add short timeout to prevent accidental opening of menu
timerOutId = setTimeout(function() {
$(_this).addClass(constants.ACTIVE_SHEET_CLASS);
$(_this).siblings().removeClass(constants.ACTIVE_SHEET_CLASS);
}, 300);
});
topLevel.on('mouseleave.topLevel', function(event) {
clearTimeout(timerOutId);
});
topLevel.children('ul').each(function() {
/* Note! IE10 on Win8 tablets is buggy and fires up
mouseout event when you click inside the menu */
$(this).on('mouseleave.sheet', function() {
if (!helpers.isTouch()) {
$(this).parent().removeClass(constants.ACTIVE_SHEET_CLASS);
}
});
});
$('html').on('click touchstart', function (event) {
var _target = $(event.target);
if (!_target.closest('.'+constants.ACTIVE_SHEET_CLASS).length) {
_target.removeClass(constants.ACTIVE_SHEET_CLASS);
topLevel.removeClass(constants.ACTIVE_SHEET_CLASS);
}
});
this.data('layout', 'sheet');
var promoContainer = $('.has-promo > ul', this);
var sectionNormal = promoContainer.children('li').not('.is-references, .promo');
var sectionReferences = promoContainer.children('li.is-references');
var sectionPromo = promoContainer.children('li.promo');
promoContainer.prepend(
$('<li class="sections bmm-megamenu-sections"></li>')
.append($('<ul class="normal bmm-megamenu-normal"></ul>')
.append(sectionNormal)
)
);
$('.sections', promoContainer).append(
$('<ul class="references"></ul>')
.append(sectionReferences)
);
return this;
},
destroyTree: function() {
// Don't try to destroy a layout which is not active
if (this.data('layout') === 'sheet') {
return this;
}
// Find all toggle buttons
var toggleButtons = $('.'+constants.BTN_TOGGLE_CLASS);
// Remove event bindings
toggleButtons.off('.toggle');
// Remove buttons
toggleButtons.remove();
// Remove plugin class name from tree
this.removeClass(constants.PLUGIN_NAME_CLASS);
// Remove data attribute
this.removeData('initialized');
this.removeData('layout');
return this;
},
destroySheet: function() {
var topLevel = this.children('ul').children('li');
topLevel.off('.topLevel');
this.removeData('initialized');
this.removeData('layout');
return this;
},
restore: function() {
var navigationDomBackup = this.data('navigationDomBackup');
this.html(navigationDomBackup);
return this;
}
};
/**
* Node
* @constructor
*/
var Node = function Node(root, _this) {
this.root = root;
this.parent = $(root).parent();
this.visible = true;
this.toggleButton = $(templates.toggleButton);
this.toggleButton.on('click.toggle', null, null, function() {
$(this).toggleClass(constants.ACTIVE_NODE_CLASS);
$(this).nextAll('ul').attr('style', '');
$(this).nextAll('ul').removeAttr('style');
});
this.parent.prepend(this.toggleButton);
};
/**
* Add methods for Node object
*/
Node.prototype = {
/**
* Returns true if node parent element (li) has css class indicating its child element is the current page
*/
isActive: function isActive() {
return this.parent.hasClass(constants.ACTIVE_PARENT_CLASS);
},
/**
* Returns the count of elements in this node
*/
length: function length() {
return this.children('li').length;
},
/**
* Toggles node visibility
*/
toggle: function toggle() {
this.visible = !this.visible;
this.toggleButton.toggleClass(constants.ACTIVE_NODE_CLASS);
}
};
/**
* The actual plugin
*/
$.fn[constants.PLUGIN_NAME] = function(method) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on bmmMegamenu' );
}
/**
* Custom settings
*/
var settings = $.extend( {
});
};
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment