/* Split Menu Buttons: created: Aug 10th, 2012 by DynamicDrive.com. This notice must stay intact for usage * Author: Dynamic Drive at http://www.dynamicdrive.com/ * Visit http://www.dynamicdrive.com/ for full source code * Updated: Aug 21st, 2012 to v1.1: * -Added ability for a button to have no drop down menu, but retain the same style as one that does. * -Just remove data-showmenu="dropmenuid" from the button's A element * Updated: Oct 4th, 2012 to v1.2: * -Added option to hide top level menu automatically onMouseout (set hidetoplevelmouseout to true) * -Menu auto hides now after user clicks on a menu item within it */ jQuery.fn.splitmenubuttonMenu = function(options){ var $ = jQuery, startzindex = 1 var s = $.extend({}, {split:true, triggerevt:'mouseover', hidetoplevelmouseout:true, hidedelay:200, fxduration:100}, options) var ismobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i) != null s.triggerevt = ismobile? 'click' : (s.triggerevt == 'mouseover'? 'mouseenter' : s.triggerevt) function deselecttoggler($dropmenu){ // func to deselect toggler var $activetoggler = $dropmenu.data('$activetoggler') if ($activetoggler && $activetoggler.length == 1){ $activetoggler.removeClass('selected') var menucolors = $activetoggler.data('menucolors') if (menucolors) $activetoggler.css('backgroundColor', menucolors[0]) } } function positionmenu(s, level, $toggler, $dropmenu){ if (level == "toplevel"){ var docrightedge = $(document).scrollLeft() + $(window).width() - 40 var docbottomedge = $(document).scrollTop()+$(window).height()-40 this.docrightedge = docrightedge // cache this value this.docbottomedge = docbottomedge var $offset = $toggler.data('mainanchor').offset() var togglerWidth = (s.split)? $toggler.data('mainanchor').width() + $toggler.width() : $toggler.width() var togglerHeight = $toggler.height() var dropmenuWidth = $dropmenu.outerWidth() var dropmenuHeight = $dropmenu.outerHeight() var leftpos = (($offset.left + dropmenuWidth) > docrightedge)? $offset.left - (dropmenuWidth - togglerWidth) : $offset.left var toppos = (($offset.top + dropmenuHeight) > docbottomedge)? $offset.top - (dropmenuHeight + togglerHeight) : $offset.top return {left: leftpos, top: toppos} } else{ var $offset = $toggler.offset() var submenuWidth = $dropmenu.outerWidth() var submenuHeight = $dropmenu.outerHeight() var leftpos = ($offset.left + (submenuWidth*2) > this.docrightedge)? -submenuWidth : submenuWidth var toppos = ($offset.top + (submenuHeight) > this.docbottomedge)? -submenuHeight + $toggler.height() : 0 return {left: leftpos, top: toppos} } } function hidemenu($dropmenu){ $dropmenu.find('ul').hide().end() .slideUp(s.fxduration, function(){ deselecttoggler($dropmenu) }) } return this.each(function(){ //return jQuery obj var $this = $(this).wrapInner('') // reference anchor item and add tag inside it if (this.tagName == "A"){ // only apply split menu to element if it's a link var menucolors = $this.attr('data-menucolors') if (menucolors){ menucolors = jQuery.trim(menucolors).split(/,\s*/) $this.css('backgroundColor', menucolors[0]) } var $dropmenu = $('ul#' + this.getAttribute('data-showmenu')) $dropmenu.data('timer', {}) var splitit = $this.attr('data-splitmenu') || s.split splitit = (typeof splitit == "string")? (splitit == "true" ? true : false ) : splitit if (splitit && $dropmenu.length == 1){ // split up toggler and menu link itself? var $toggler = $(' ').insertAfter($this) // append new "toggler" element after menu button if (menucolors){ $toggler.css('backgroundColor', $this.css('backgroundColor')) } } else{ var $toggler = $this if ($dropmenu.length == 1){ $toggler.addClass('downtoggler').find('span.innerspan:eq(0)').addClass('downarrow') } } $toggler.data({'mainanchor':$this, 'menucolors':menucolors}) // store ref to menu link (which may be different from toggler if splitit == true) $dropmenu.appendTo(document.body) //move drop down menu so it's a child of document.body if ($dropmenu.length == 1){ $toggler.bind(s.triggerevt, function(e){ // action when toggler is activated clearTimeout($dropmenu.data('timer').hidetimer) var $this = $(this) var menucolors = $this.data('menucolors') // get menu colors (if data-menucolors attr defined) if (menucolors) $toggler.css('backgroundColor', menucolors[1]) $toggler.addClass('selected') // add CSS class of 'selected' to toggler var slidefunc = (e.type =='click')? 'slideToggle' : 'slideDown' if ($dropmenu.data('$activetoggler') && $dropmenu.data('$activetoggler').get(0) != $this.get(0)){ deselecttoggler($dropmenu) slidefunc = 'slideDown' } var menupos = (positionmenu(s, "toplevel", $toggler, $dropmenu)) $dropmenu.css({zIndex:++startzindex, left:menupos.left, top:menupos.top + $this.outerHeight()}) [slidefunc](s.fxduration, function(){ var $this = $(this) if ($this.css('display') == 'none') deselecttoggler($this) }) $dropmenu.data('$activetoggler', $this) return false }) // end $toggler.trigger if (s.hidetoplevelmouseout){ $toggler.bind('mouseleave', function(e){ // action when mouse rolls out of toggler $dropmenu.data('timer').hidetimer = setTimeout(function(){ hidemenu($dropmenu) }, s.hidedelay) }) } } // end if $dropmenu.length == 1 else{ // else if this button has no drop down menu $toggler.hover( function(){ $toggler.addClass('selected') if ($toggler.data('menucolors')) $toggler.css('backgroundColor', menucolors[1]) }, function(){ $toggler.removeClass('selected') if ($toggler.data('menucolors')) $toggler.css('backgroundColor', menucolors[0]) } ) } if ($dropmenu.data('isbuilt')) // if this drop down menu has been built already, move on to the next one return true $dropmenu.on('click mouseenter', function(e){ clearTimeout($dropmenu.data('timer').hidetimer) if (e.type == "mouseenter") $(this).css({zIndex: startzindex++}) else{ if ($(e.target).parent('li').hasClass('headerli')) e.stopPropagation() } }) if (s.hidetoplevelmouseout){ $dropmenu.on('mouseleave', function(e){ $dropmenu.data('timer').hidetimer = setTimeout(function(){ hidemenu($dropmenu) }, s.hidedelay) }) } var $headers = $dropmenu.find('ul').parent() $headers.each(function(i){ var $li=$(this).css({zIndex: 1000+i}).addClass('headerli') var $subul = $li.find('ul:eq(0)').css({display:'block'}) //set sub UL to "block" so we can get dimensions $subul.data('$parentliref', $li) //cache parent LI of each sub UL $subul.data('timers', {}) $li.data('$subulref', $subul) //cache sub UL of each parent LI $li.children("a:eq(0)").addClass('rightarrow') $li.bind(s.triggerevt, function(e){ //show sub UL when mouse moves over parent LI var $targetul=$(this).addClass("selected").data('$subulref') if ($targetul.queue().length<=1){ //if 1 or less queued animations clearTimeout($targetul.data('timers').hidetimer) var menupos = (positionmenu(s, "sublevel", $(this), $targetul)) $targetul.css({left: menupos.left, top: menupos.top}) $targetul.stop(true, true).slideDown(s.fxduration) } }) $li.bind('mouseleave', function(e){ //hide sub UL when mouse moves out of parent LI var $targetul=$(this).data('$subulref') clearTimeout($targetul.data('timers').showtimer) $targetul.data('timers').hidetimer=setTimeout(function(){ $targetul.stop(true, true).slideUp(s.fxduration).data('$parentliref').removeClass('selected') }, s.hidedelay) }) $subul.bind('mouseenter', function(e){ clearTimeout($(this).data('timers').hidetimer) }) }) // end $headers.each() $dropmenu.find('ul').andSelf().css({display:'none', visibility:'visible'}) //collapse all ULs again $dropmenu.data('isbuilt', true) if (!$(document).data('hidemenuevt')){ // hide top level drop down menus when user clicks on document $(document).on('click', function(e){ $('ul.splitdropdown:visible').find('ul').hide().end() .slideUp(s.fxduration, function(){ var $this = $(this) if ($this.css('display') == 'none') deselecttoggler($this) }) }) $(document).data('hidemenuevt', true) } } }) }