diff --git a/bootstrap-hover-dropdown.js b/bootstrap-hover-dropdown.js index 521ef35..155bf6b 100644 --- a/bootstrap-hover-dropdown.js +++ b/bootstrap-hover-dropdown.js @@ -31,11 +31,15 @@ var $this = $(this), $parent = $this.parent(), defaults = { - delay: 500, + delayClose: 1000, + delayOpen: 300, + delaySwitch: 100, instantlyCloseOthers: true }, data = { - delay: $(this).data('delay'), + delayClose: $(this).data('delay-close'), + delayOpen: $(this).data('delay-open'), + delaySwitch: $(this).data('delay-switch'), instantlyCloseOthers: $(this).data('close-others') }, showEvent = 'show.bs.dropdown', @@ -43,35 +47,70 @@ // shownEvent = 'shown.bs.dropdown', // hiddenEvent = 'hidden.bs.dropdown', settings = $.extend(true, {}, defaults, options, data), - timeout; + timeoutOpen, + timeoutClose; + $parent.hover(function (event) { + // so a neighbor can't open the dropdown - if(!$parent.hasClass('open') && !$this.is(event.target)) { + // FIX: see https://github.com/CWSpear/bootstrap-hover-dropdown/issues/55 + if($parent.hasClass('open') && !$this.is(event.target)) { // stop this event, stop executing any code // in this callback but continue to propagate return true; } - openDropdown(event); + var isChildMenu = $parent.parents('.dropdown-menu').length, + siblingIsOpen = $parent.siblings().hasClass('open'); + + window.clearTimeout(timeoutClose); + + timeoutOpen = window.setTimeout(function(){ + openDropdown(event); + }, siblingIsOpen || isChildMenu ? settings.delaySwitch : settings.delayOpen); + + + }, function () { - timeout = window.setTimeout(function () { + clearTimeout(timeoutOpen); + + var isChildMenu = $parent.parents('.dropdown-menu').length; + + timeoutClose = window.setTimeout(function () { $parent.removeClass('open'); $this.trigger(hideEvent); - }, settings.delay); + }, timeoutOpen && !isChildMenu ? settings.delayClose : settings.delaySwitch); }); + + // clear timeout if hovering submenu + $allDropdowns.find('.dropdown-menu').hover(function(){ + window.clearTimeout(timeoutClose); + }, function(){ + + var isChildMenu = $(this).parents('.dropdown-menu').length; + if(isChildMenu){ + return true; + } + + timeoutClose = window.setTimeout(function () { + $parent.removeClass('open'); + $this.trigger(hideEvent); + }, timeoutOpen > 0 ? settings.delayClose : 0); + }); + + // this helps with button groups! $this.hover(function (event) { // this helps prevent a double event from firing. // see https://github.com/CWSpear/bootstrap-hover-dropdown/issues/55 - if(!$parent.hasClass('open') && !$parent.is(event.target)) { + if($parent.hasClass('open') && !$parent.is(event.target)) { // stop this event, stop executing any code // in this callback but continue to propagate return true; } - openDropdown(event); }); // handle submenus @@ -87,17 +126,25 @@ var $submenu = $this.children('.dropdown-menu'); subTimeout = window.setTimeout(function () { $submenu.hide(); - }, settings.delay); + }, timeoutOpen > 0 ? settings.delayClose : 0); }); }); function openDropdown(event) { $allDropdowns.find(':focus').blur(); - if(settings.instantlyCloseOthers === true) - $allDropdowns.removeClass('open'); + if(settings.instantlyCloseOthers === true){ + + // not the first level + if($this.parents('.dropdown-menu').length && $this.siblings().parent().hasClass('open')){ + $this.siblings().parent().removeClass('open'); + }else{ + $this.parent('li').siblings().removeClass('open'); + } + } + - window.clearTimeout(timeout); + window.clearTimeout(timeoutClose); $parent.addClass('open'); $this.trigger(showEvent); } diff --git a/bootstrap-hover-dropdown.min.js b/bootstrap-hover-dropdown.min.js index 51a81ef..bd2f7f7 100644 --- a/bootstrap-hover-dropdown.min.js +++ b/bootstrap-hover-dropdown.min.js @@ -1,13 +1 @@ -/** - * Project: Bootstrap Hover Dropdown - * Author: Cameron Spear - * Contributors: Mattia Larentis - * - * Dependencies: Bootstrap's Dropdown plugin, jQuery - * - * A simple plugin to enable Bootstrap dropdowns to active on hover and provide a nice user experience. - * - * License: MIT - * - * http://cameronspear.com/blog/bootstrap-dropdown-on-hover-plugin/ - */(function(e,t,n){var r=e();e.fn.dropdownHover=function(n){if("ontouchstart"in document)return this;r=r.add(this.parent());return this.each(function(){function h(e){r.find(":focus").blur();l.instantlyCloseOthers===!0&&r.removeClass("open");t.clearTimeout(c);s.addClass("open");i.trigger(a)}var i=e(this),s=i.parent(),o={delay:500,instantlyCloseOthers:!0},u={delay:e(this).data("delay"),instantlyCloseOthers:e(this).data("close-others")},a="show.bs.dropdown",f="hide.bs.dropdown",l=e.extend(!0,{},o,n,u),c;s.hover(function(e){if(!s.hasClass("open")&&!i.is(e.target))return!0;h(e)},function(){c=t.setTimeout(function(){s.removeClass("open");i.trigger(f)},l.delay)});i.hover(function(e){if(!s.hasClass("open")&&!s.is(e.target))return!0;h(e)});s.find(".dropdown-submenu").each(function(){var n=e(this),r;n.hover(function(){t.clearTimeout(r);n.children(".dropdown-menu").show();n.siblings().children(".dropdown-menu").hide()},function(){var e=n.children(".dropdown-menu");r=t.setTimeout(function(){e.hide()},l.delay)})})})};e(document).ready(function(){e('[data-hover="dropdown"]').dropdownHover()})})(jQuery,this); \ No newline at end of file +(function(b,a,c){var d=b();b.fn.dropdownHover=function(e){if("ontouchstart" in document){return this}d=d.add(this.parent());return this.each(function(){var l=b(this),i=l.parent(),h={delayClose:1000,delayOpen:300,delaySwitch:100,instantlyCloseOthers:true},j={delayClose:b(this).data("delay-close"),delayOpen:b(this).data("delay-open"),delaySwitch:b(this).data("delay-switch"),instantlyCloseOthers:b(this).data("close-others")},n="show.bs.dropdown",f="hide.bs.dropdown",g=b.extend(true,{},h,e,j),o,k;i.hover(function(q){if(i.hasClass("open")&&!l.is(q.target)){return true}a.clearTimeout(k);var p=i.parents(".dropdown-menu").length;o=a.setTimeout(function(){m(q)},o>0||p?g.delaySwitch:g.delayOpen)},function(){clearTimeout(o);var p=i.parents(".dropdown-menu").length;k=a.setTimeout(function(){i.removeClass("open");l.trigger(f)},o&&!p?g.delayClose:g.delaySwitch)});d.find(".dropdown-menu").hover(function(){a.clearTimeout(k)},function(){var p=b(this).parents(".dropdown-menu").length;if(p){return true}k=a.setTimeout(function(){i.removeClass("open");l.trigger(f)},o>0?g.delayClose:0)});l.hover(function(p){if(i.hasClass("open")&&!i.is(p.target)){return true}o=a.setTimeout(function(){m(p)},o>0?0:g.delayOpen)});i.find(".dropdown-submenu").each(function(){var q=b(this);var p;q.hover(function(){a.clearTimeout(p);q.children(".dropdown-menu").show();q.siblings().children(".dropdown-menu").hide()},function(){var r=q.children(".dropdown-menu");p=a.setTimeout(function(){r.hide()},o>0?g.delayClose:0)})});function m(p){d.find(":focus").blur();if(g.instantlyCloseOthers===true){if(l.parents(".dropdown-menu").length&&l.siblings().parent().hasClass("open")){l.siblings().parent().removeClass("open")}else{l.parent("li").siblings().removeClass("open")}}a.clearTimeout(k);i.addClass("open");l.trigger(n)}})};b(document).ready(function(){b('[data-hover="dropdown"]').dropdownHover()})})(jQuery,this); \ No newline at end of file