Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #19: added support for scrollbar on dropdown menu #30

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

tgirardi
Copy link

@tgirardi tgirardi commented Dec 4, 2012

Implements enhancements needed to give support for features mentioned on issue #19: scrollbar support on menu.

Originally, using CSS, you could force the menu to have a max-height and use a scroll (overflow:auto or overflow-y:auto) to reach the list items that where outside the visual area. However, clicking on the scrollbar that could appear would not work (you could only scroll using the mouse wheel, trackpad gesture or similar). The reason was that any click on it would fire a blur event that would close the menu.

This commit solves this issue by making a difference between clicks on a list item or clicks anywhere else inside the menu (including the scrollbar). The first case will produce the original behavior (item is selected and then the menu is closed). The second one will not select an item and will avoid the closing of the menu (also it will reposition the focus on the input element).

The second part of this enhancement is related with navigation using up and down arrows: now that we have a "hidden" area, we must ensure that any selected item is kept away from that area by forcing the menu to scroll (vertically) accordingly.

In order to achieve this:

  • When going up: prev() method now checks if the top border of the newly selectes li element is inside the visible area or not. If not, the menu is scrolled (using jQuery's scrollTop method) to the exact position of that top border (leaving the mentioned element fully visible).
  • When going down: next() method does something similar to the previous case but taking in consideration the bottom border of the selected li element

It was also necessary to take care of the mouseenter event that could be triggered when we scroll programmatically (the mouse is not moved, but the elements behind the mouse - the menu items - do move because of the scrolling movement). The solution for that was setting a variable that tells the next mouseenter method invocation to avoid changing the active element, after prev() or next() is called (if the user moves the mouse after prev() or next() the mouseenter can start changing the active element again... this is done in order to avoid messing the expected behavior of mouseenter in cases where prev() or next() makes the menu scroll but the mouse never leaves the element it is currently over, which could happen for small scrolling distances).

Originally, using CSS, you could force the menu to have a max-height and use a scroll (overflow:auto or overflow-y:auto) to reach the list items that where outside the visual area. However, clicking on the scrollbar that could appear would not work (you could only scroll using the mouse wheel, trackpad gesture or similar). The reason was that any click on it would fire a blur event that would close the menu.

This commit solves this issue by making a difference between clicks on a list item (<li>) or clicks anywhere else inside the menu (including the scrollbar). The first case will produce the original behavior (item is selected and then the menu is closed). The second one will not select an item and will avoid the closing of the menu (also it will reposition the focus on the input element).

The second part of this enhancement is related with navigation using up and down arrows: now that we have a "hidden" area, we must assure that any selected item is kept away from that area by forcing the menu to scroll (vertically) accordingly.

In order to achieve this:

* When going up: prev() method now checks if the top border of the newly selectes <li> element is inside the visible area or not. If not, the menu is scrolled (using jQuery's scrollTop method) to the exact position of that top border (leaving the mentioned element fully visible).
* When going down: next() method does something similar to the previous case but taking in consideration the bottom border of the selected <li> element

It was also necessary to take care of the mouseenter event that could be triggered when we scroll programmatically (the mouse is not moved, but the elements behind the mouse - the menu items - do move because of the scrolling movement). The solution for that was setting a variable that tells the next mouseenter method invocation to avoid changing the active element, after prev() or next() is called (if the user moves the mouse after prev() or next() the mouseenter can start changing the active element again... this is done in order to avoid messing the expected behavior of mouseenter in cases where prev() or next() makes the menu scroll but the mouse never leaves the element it is currently over, which could happen for small scrolling distances).
@@ -363,14 +363,28 @@ function ($) {
// Selects the next result
//
next: function (event) {
var active = this.$menu.find('.active').removeClass('active');
var that = this;
var $menu = that.$menu;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to use this.$menu very often, so I believe using a variable gives shorter file, specially after "minification"

@tgirardi
Copy link
Author

tgirardi commented Dec 4, 2012

I forgot to make one final adjustment. It seems that the blur method has an error in the verification of the focus owner:

Where it says

if (!that.$menu.is(':focus')) {

It should be:

if (!that.$element.is(':focus')) {

I'll add a commit to this pull request to change this. I don't know if in branch 2.0 this has been solved or if there is any reported issue about this that could be closed after this solution.

Blur method fires a timeout which will check where the focus is after 150 ms in order to close the menu or keep it open. However, that verification was checking if the $menu was the owner of the focus. $menu is an "ul", so it can't be the owner of any focus. Probably the real intetion was to check $element, so I made the corresponding change.

Note: in issue tcrosen#24 @smucode suggest checking for $menu.is(':hover'). That could be a better solution to the problem discussed in that issue... or probably there's even a better one out there... It would be a good idea to give it some thoughts in order to fix that issue too.
@tgirardi
Copy link
Author

tgirardi commented Dec 4, 2012

I made an online demo for testing in JSFiddle: http://jsfiddle.net/r44dt/

I was considering clicking "inside an item" as equivalent to clicking inside an
anchor inside the menu. But there was a case that I forgot: when someone clicks
in the "highlighted area" of the text inside the anchor. That area is achieved
by surrounding the corresponding text with a <strong> tag. So, when clicking on
that area, the target for the event fired will not be the anchor, but the strong
tag. This made it necessary to check not only if the tagName for the target is
equal to 'A', but also if the tagName of the parent of the target is (this is
the case when we click inside the highlighted area).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant