From b414bdcb614246cc8523e703159bff4d4690c981 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Mon, 18 Mar 2013 22:32:09 -0700 Subject: [PATCH 01/13] Added links to demo via htmlpreview.github.com --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b05ce8d..5ee5641 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,7 @@ menu-aim is a jQuery plugin for dropdown menus that can differentiate between a user trying hover over a dropdown item vs trying to navigate into a submenu's contents. -menu-aim assumes that you are using a menu with submenus that expand -to the menu's right. It will fire events when the user's mouse enters a new -dropdown item *and* when that item is being intentionally hovered over. +[Try a demo.](http://htmlpreview.github.com/?https://github.com/kamens/jQuery-menu-aim/blob/master/example/example.html) ![Amazon screenshot](https://raw.github.com/kamens/jQuery-menu-aim/master/amazon.png) @@ -55,9 +53,13 @@ the relevant row's HTML element as the execution context ('this'): submenuSelector: "*" }); +menu-aim assumes that you are using a menu with submenus that expand +to the menu's right. It will fire events when the user's mouse enters a new +dropdown item *and* when that item is being intentionally hovered over. + ## Want an example to learn from? -Check out example/example.html -- it has a working dropdown for you to play with: +Check out example/example.html -- it has [a working dropdown for you to play with](http://htmlpreview.github.com/?https://github.com/kamens/jQuery-menu-aim/blob/master/example/example.html): ![Example screenshot](https://raw.github.com/kamens/jQuery-menu-aim/master/example.png)
_Play with the above example full of fun monkey pictures by opening example/example.html after downloading the repo._ From 16dbc05da2217438793a3ab08371e30adb8b0b0a Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Mon, 25 Mar 2013 22:35:48 -0700 Subject: [PATCH 02/13] Make menu-aim work when jQuery collection contains multiple elements --- jquery.menu-aim.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/jquery.menu-aim.js b/jquery.menu-aim.js index 4b3ffb5..dc61de6 100644 --- a/jquery.menu-aim.js +++ b/jquery.menu-aim.js @@ -66,8 +66,17 @@ * https://github.com/kamens/jQuery-menu-aim */ (function($) { + $.fn.menuAim = function(opts) { + // Initialize menu-aim for all elements in jQuery collection + this.each(function() { + init.call(this, opts); + }); + + return this; + }; + function init(opts) { var $menu = $(this), activeRow = null, mouseLocs = [], @@ -248,18 +257,13 @@ /** * Hook up initial menu events */ - var init = function() { - $menu - .mouseleave(mouseleaveMenu) - .find(options.rowSelector) - .mouseenter(mouseenterRow) - .mouseleave(mouseleaveRow); - - $(document).mousemove(mousemoveDocument); - }; + $menu + .mouseleave(mouseleaveMenu) + .find(options.rowSelector) + .mouseenter(mouseenterRow) + .mouseleave(mouseleaveRow); + $(document).mousemove(mousemoveDocument); - init(); - return this; }; })(jQuery); From dd4adf5b5dfca9dc13b90b8998f8ce2a210a1a52 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Mon, 25 Mar 2013 23:01:17 -0700 Subject: [PATCH 03/13] Add exitMenu callback option to be used when the mouse exits the whole menu entirely. Some users want submenus to disappear in this case, others do not. Added appropriate docs to README --- README.md | 7 +++++++ jquery.menu-aim.js | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ee5641..d361363 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,13 @@ the relevant row's HTML element as the execution context ('this'): // Function to call when mouse exits a menu row. exit: function() {}, + // Function to call when mouse exits the entire menu. If this returns + // true, the current row's deactivation event and callback function + // will be fired. Otherwise, if this isn't supplied or it returns + // false, the currently activated row will stay activated when the + // mouse leaves the menu entirely. + exitMenu: function() {}, + // Selector for identifying which elements in the menu are rows // that can trigger the above events. Defaults to "> li". rowSelector: "> li", diff --git a/jquery.menu-aim.js b/jquery.menu-aim.js index dc61de6..0136991 100644 --- a/jquery.menu-aim.js +++ b/jquery.menu-aim.js @@ -89,7 +89,8 @@ enter: $.noop, exit: $.noop, activate: $.noop, - deactivate: $.noop + deactivate: $.noop, + exitMenu: $.noop }, opts); var MOUSE_LOCS_TRACKED = 3, // number of past mouse locations to track @@ -113,6 +114,16 @@ if (timeoutId) { clearTimeout(timeoutId); } + + // If exitMenu is supplied and returns true, deactivate the + // currently active row on menu exit. + if (options.exitMenu(this)) { + if (activeRow) { + options.deactivate(activeRow); + } + + activeRow = null; + } }; /** From 23ad9e2addd06e9ce09af2a9a5ac5d961b6aec6c Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Mon, 25 Mar 2013 23:04:44 -0700 Subject: [PATCH 04/13] Remove trailing comma --- README.md | 2 +- jquery.menu-aim.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d361363..7d7e734 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ dropdown. $("#menu").menuAim({ activate: $.noop, // fired on row activation - deactivate: $.noop, // fired on row deactivation + deactivate: $.noop // fired on row deactivation }); ...to receive events when a menu's row has been purposefully (de)activated. diff --git a/jquery.menu-aim.js b/jquery.menu-aim.js index 0136991..303ce2b 100644 --- a/jquery.menu-aim.js +++ b/jquery.menu-aim.js @@ -29,7 +29,7 @@ * * $("#menu").menuAim({ * activate: $.noop, // fired on row activation - * deactivate: $.noop, // fired on row deactivation + * deactivate: $.noop // fired on row deactivation * }); * * ...to receive events when a menu's row has been purposefully (de)activated. From 5e321a8a6905ba93697d91d84458b331986b0ee6 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Tue, 26 Mar 2013 00:02:14 -0700 Subject: [PATCH 05/13] Add submenuDirection option so menu-aim supports submenus that open to the left/right/above/below main menus. --- README.md | 8 ++++++- jquery.menu-aim.js | 52 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7d7e734..4913f1e 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,13 @@ the relevant row's HTML element as the execution context ('this'): // You may have some menu rows that aren't submenus and therefore // shouldn't ever need to "activate." If so, filter submenu rows w/ // this selector. Defaults to "*" (all elements). - submenuSelector: "*" + submenuSelector: "*", + + // Direction the submenu opens relative to the main menu. This + // controls which direction is "forgiving" as the user moves their + // cursor from the main menu into the submenu. Can be one of "right", + // "left", "above", or "below". Defaults to "right". + submenuDirection: "right" }); menu-aim assumes that you are using a menu with submenus that expand diff --git a/jquery.menu-aim.js b/jquery.menu-aim.js index 303ce2b..b06f888 100644 --- a/jquery.menu-aim.js +++ b/jquery.menu-aim.js @@ -60,7 +60,11 @@ * // You may have some menu rows that aren't submenus and therefore * // shouldn't ever need to "activate." If so, filter submenu rows w/ * // this selector. Defaults to "*" (all elements). - * submenuSelector: "*" + * submenuSelector: "*", + * + * // Direction the submenu opens relative to the main menu. Can be + * // left, right, above, or below. Defaults to "right". + * submenuDirection: "right" * }); * * https://github.com/kamens/jQuery-menu-aim @@ -85,6 +89,7 @@ options = $.extend({ rowSelector: "> li", submenuSelector: "*", + submenuDirection: "right", tolerance: 75, // bigger = more forgivey when entering submenu enter: $.noop, exit: $.noop, @@ -191,13 +196,21 @@ } var offset = $menu.offset(), + upperLeft = { + x: offset.left, + y: offset.top - options.tolerance + }, upperRight = { x: offset.left + $menu.outerWidth(), - y: offset.top - options.tolerance + y: upperLeft.y + }, + lowerLeft = { + x: offset.left, + y: offset.top + $menu.outerHeight() + options.tolerance }, lowerRight = { x: offset.left + $menu.outerWidth(), - y: offset.top + $menu.outerHeight() + options.tolerance + y: lowerLeft.y }, loc = mouseLocs[mouseLocs.length - 1], prevLoc = mouseLocs[0]; @@ -247,13 +260,34 @@ return (b.y - a.y) / (b.x - a.x); }; - var upperSlope = slope(loc, upperRight), - lowerSlope = slope(loc, lowerRight), - prevUpperSlope = slope(prevLoc, upperRight), - prevLowerSlope = slope(prevLoc, lowerRight); + var decreasingCorner = upperRight, + increasingCorner = lowerRight; + + // Our expectations for decreasing or increasing slope values + // depends on which direction the submenu opens relative to the + // main menu. By default, if the menu opens on the right, we + // expect the slope between the cursor and the upper right + // corner to decrease over time, as explained above. If the + // submenu opens in a different direction, we change our slope + // expectations. + if (options.submenuDirection == "left") { + decreasingCorner = lowerLeft; + increasingCorner = upperLeft; + } else if (options.submenuDirection == "below") { + decreasingCorner = lowerRight; + increasingCorner = lowerLeft; + } else if (options.submenuDirection == "above") { + decreasingCorner = upperLeft; + increasingCorner = upperRight; + } + + var decreasingSlope = slope(loc, decreasingCorner), + increasingSlope = slope(loc, increasingCorner), + prevDecreasingSlope = slope(prevLoc, decreasingCorner), + prevIncreasingSlope = slope(prevLoc, increasingCorner); - if (upperSlope < prevUpperSlope && - lowerSlope > prevLowerSlope) { + if (decreasingSlope < prevDecreasingSlope && + increasingSlope > prevIncreasingSlope) { // Mouse is moving from previous location towards the // currently activated submenu. Delay before activating a // new menu row, because user may be moving into submenu. From b1db2d3b1d95d00f594a522f3675783528f677f9 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Mon, 25 Mar 2013 23:59:55 -0700 Subject: [PATCH 06/13] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4913f1e..0708e27 100644 --- a/README.md +++ b/README.md @@ -80,4 +80,5 @@ _Play with the above example full of fun monkey pictures by opening example/exam ## FAQ 1. What's the license? [MIT](http://en.wikipedia.org/wiki/MIT_License). -2. I'm not nearly bored enough. Got anything else? [Read about this plugin's creation](http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown). +2. Does it support horizontal menus or submenus that open to the left? Yup. Check out the submenuDirection option above. +3. I'm not nearly bored enough. Got anything else? [Read about this plugin's creation](http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown). From c98a809aa511229750fdcaec5db37ac6fb320a67 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Tue, 26 Mar 2013 21:31:28 -0700 Subject: [PATCH 07/13] Move example's submenu contents into HTML structure of menu itself so new exitMenu event fires at proper times in example. --- example/example.html | 152 +++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/example/example.html b/example/example.html index d2f934b..e4f9b03 100644 --- a/example/example.html +++ b/example/example.html @@ -50,6 +50,17 @@ max-width: 250px; } + .dropdown-menu { + -webkit-border-top-right-radius: 0px; + -webkit-border-bottom-right-radius: 0px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + + -webkit-box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2); + } + .dropdown-menu > li > a:hover { background-image: none; color: white; @@ -83,7 +94,6 @@ @@ -117,53 +192,6 @@

jQuery-menu-aim example

- - - - - - - - - - - - @@ -187,15 +215,14 @@

King Colobus

var $row = $(row), submenuId = $row.data("submenuId"), $submenu = $("#" + submenuId), - offset = $menu.offset(), height = $menu.outerHeight(), width = $menu.outerWidth(); // Show the submenu $submenu.css({ display: "block", - top: offset.top, - left: offset.left + width - 5, // main should overlay submenu + top: -1, + left: width - 3, // main should overlay submenu height: height - 4 // padding for main dropdown's arrow }); @@ -217,6 +244,7 @@

King Colobus

// Simply hide the submenu on any click. Again, this is just a hacked // together menu/submenu structure to show the use of jQuery-menu-aim. $(".popover").css("display", "none"); + $("a.maintainHover").removeClass("maintainHover"); }); From 1737981130341232c64cfa2a50349a1c9537e6c6 Mon Sep 17 00:00:00 2001 From: Ben Kamens Date: Tue, 26 Mar 2013 21:45:15 -0700 Subject: [PATCH 08/13] Make sure example page's overridden styles are more specific than vanilla boostrap's --- example/example.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/example/example.html b/example/example.html index e4f9b03..b9938c1 100644 --- a/example/example.html +++ b/example/example.html @@ -32,7 +32,7 @@ You can choose to do whatever you want w/ those events. -->