Scrolling a CSS-driven dropdown menu and overflow:hidden?

Scrolling a CSS-driven dropdown menu and overflow:hidden? The main #menubar > li will scroll and hide when scrolled to the left of #menubar's border, but the dropdown menus .aMenu remain visible?

Before scrolling …

HTML:

<div id="menubarParent">
<ul id="menubar">
	<li>
	<!-- stuff -->
	</li>

	<li class="drop">          					
		Demo
		<ul class="aMenu">                    			
			<li> 
				<a href="ABOUT.html">
				text21
				</a>
			</li>
		</ul>
	</li>
</ul>
</div>

CSS:

#menubarParent {
	position: relative;
	z-index:  1000;
}

#menubar {
/*
	position: relative;
	z-index:  1000;
*/
}

#menubar {
    white-space: nowrap;
    overflow-x: auto;
}

/*
	ROUNDED BORDER
*/
#menubar {
	background: white;
	
	border:             solid 0.1em #8b0000;
	border-left-width:  1.0em;
	border-right-width: 1.0em;	
	border-radius:      0.5em;
}

#menubar li .aMenu {
	margin: 0 auto 0 auto;
	
	min-width: 8.1em;
}

/* drop + sub-menus (initially hidden) */
#menubar .aMenu {
	left: -99999px;

	/*
		so these menus show on top of
		the horizontal scroll bar
	*/	
	position: absolute;

	margin:  0em;
	border:  solid .125em #8b0000;
	padding: 0em;
}

JS:

Here’s the marvelous code for scrolling the horizontal menu (sitePoint.com):

$itsFocusedItem;

/* PaulOB's creation at sitepoint.com changed to jQuery */
function scrollMenusAdjust() {

	const $MBScroller = $("#menubar");
	var   $thisDropMenu;
	
	$MBScroller.on("scroll", function(evt) {
		$("li.drop > .aMenu").each(function() {
			$thisDropMenu = $(this);
			
			$thisDropMenu[0].style.transform =
                  "translateX(-" +
                  $MBScroller[0].scrollLeft +
                  "px)";
		});	
	});
	
	// Otherwise, clicking on scrollbar to scroll makes the
	// dropmenu lose focus and pressing TAB later won't
	// continue where it left off.
	//
	// THANK YOU! Paul and also stackoverflow.com

	$MBScroller.on("mousedown", function(evt) {
		$itsFocusedItem = getFocusedItem();
	});
	
	$MBScroller.on("mouseup", function(evt) {
		$itsFocusedItem.focus();
	});

}   // scrollMenusAdjust

With scrolling using the horizontal scrollbar, the $(#menubar > li) hide under the left border … but the dropdown menus = .aMenu do not?

OH-OH!

My initial solution for this was to do this:

#menubarParent {
/*
	position: relative;
	z-index:  1000;
*/
}

#menubar {
	position: relative;
	z-index:  1000;
}

But, here’s the price tag:

I even tried putting a height:whatever; inside #menubar, but that made the result equally ugly.

HAALP!

Hi,

There are a couple of things to consider here.

First of all you can’t hide the overflow on the scroller or you won’t see the dropdowns. Also you want the dropdown menus to be visible outside the scroller width because a dropdown menu item may need that extra space to show.

Here’s a screenshot from my old demo showing that if the menu did not appear outside the scrollbar width it would be of no use.

That behaviour has to remain in place or the usability of the menu is gone.

Also at the other end of the menu you need the dropdown to be visible as long as you are hovering on an item or even a portion of a menu item like this.

The above two screenshots are wanted behaviour.

What you don’t want is that once you start scrolling you need the menus to disappear once the mouse pointer is no longer on that menu item. The only way I can see to do this is that in the scroll routine you add a class (.isScrolling) to the parent item and then use that class in css to hide any dropdowns while the scrolling is in effect.

I have added that function to my old demo and I believe gives the best function for that menu.

I added some js here to add a class to the parent and remove it instantly it had scrolled.

function checkScroll() {
    scroller.classList.add("isScrolling");
    for (let i = 0; i < dropDown.length; i++) {
      dropDown[i].style.transform =
        "translateX(-" + scroller.scrollLeft + "px)";
    }
    scroller.classList.remove("isScrolling");
  }

I then used this CSS to hide the menu.

/* remove hover when scrolling */
.isScrolling .nav ul.dropdown{ left: -999em;}

Hope you can implement something similar for your demo as I believe it is the only realistic solution.

(As I said in the original thread the scroll event should most likely be debounced (throttled) to avoid it causing any lag on the page (but that would be a question for the js forum).)

Hi again,

I’m thinking that instead of hiding the dropdown .aMenu when it goes beyond the right/left edges of #menubar, force the scrolling #menubar to scroll to the parent #menubar > li.

Come to think of it, I believe that is exactly what I want.

My on('mousedown' … and on('mouseup' ... returns to the <li> that was originally focused. And that works. But, it may be offscreen = way to the left or way to the right. So all I have to do is make the main #menubar > li visible.

Gotta marinate on this …

I’d hate that if you did it to me while I was trying to hover the menu item and then you moved it? Your mouse would then be in the wrong place to traverse that menu.

That doesn’t sound like a usable UI unless I misunderstood what you meant.

A better choice would be to make the right side drop menus drop down from the right of the menu item and go left. In that way they will stay within the bounds of the menu (assuming you don’t have multiple nested items as in my example). Of course you’d need to script that behaviour if you have many menu items as the side they drop down from will change depending on which way you are scrolling.

You will still need the code I gave you to stop the menu still showing while you scroll the menu bar sideways with the mouse. I’m not sure if we are talking about different issues though as I am sideways scrolling with a mac mouse ?

I didn’t make myself clear enough …

We are not talking using the mouse to go from menu to menu. That is handled with just the CSS alone.

Moving the mouse won’t scroll anything because you can’t move it to a menu that’s off screen … once you’ve dropped a menu.

You can mouse from one main menu to another as long as no drop menu is showing. And this works just dandy.

Scrolling occurs great.

To go from a menu item in one menu to a menu item in another menu requires TABbing.

I’m talking TABbing:

  1. TABbing from menu item to menu item, which may take us to a different menu, forward or backward. All based on tabindex which has already been added to $(“li”) with start up.

This automatically selects each li in turn.

So we’re TABbing and suddenly we’ve tabbed beyond the right edge of the #menubar because we’ve gone to the next #menubar > li, which was previously off screen. This causes automatic scrolling because we’ve selected a different main menu.

And this works great.

So I am thinking that when we TAB forward e.g. to something off screen, we decide to click on the scroll bar. Unless we click on some other menu, our mouseup Handler will automatically select what our previous selection was before we clicked the scroll bar.

THAT’S what I want to automatically scroll to.

No mouse involved

Ah ok :slight_smile:

Yes you can on a mac.:slight_smile:

I think that’s the same behaviour you are experiencing when you tab and the tabbed item shows off screen. I believe that will still happen in your version so should be looked at.

I put a blur() in my example top that happening with tab.

It seems to work if you just use the keyboard but if you mix tabbing and mouse actions then it seems to get a bit confused.

I think that you may indeed need to scroll into view a tabbed item using js if that item is not visible. (My demo seems to more or less do this using focus:within unless as I said the mouse is also used.)

Needs a bit of thinking about :slight_smile:

1 Like

I came across scrollIntoView() and this does the trick … yours does also.

/* JAL: PaulOB's awesome creation at sitepoint.com changed to jQuery */
function scrollMenusAdjust() {

	const $MBScroller = $("#menubar");
	var   $thisDropMenu;

	$MBScroller.on("scroll", function(evt) {	
		$("li.drop > .aMenu").each(function() {
			$thisDropMenu = $(this);			
			$thisDropMenu[0].style.transform = "translateX(-" + $MBScroller[0].scrollLeft + "px)";
		});
	});
		
	// Otherwise, clicking on scrollbar makes the dropmenu lose focus
	// and pressing TAB later won't continue where it left off.
	//
	// THANK YOU! Paul and also stackoverflow.com
	$MBScroller.on("mousedown", function(evt) {
		$itsFocusedItem = getFocusedItem();
	});
	
	$MBScroller.on("mouseup", function(evt) {
		var $itsFocusedMenuTitle = $itsFocusedItem.closest("#menubar > li");
		$itsFocusedMenuTitle[0].scrollIntoView();
		$itsFocusedItem.focus();
	});
	
}   // scrollMenusAdjust

Currently I’m delving into my code testing for 2 keypresses at the same time = SHIFT-TAB for going backwards.

It does go backwards from li to li, but apparently there’s a timing issue that is addressed with a keys = [].

I hope someone gives me credit for being the inspiration for the next jQuery for Dummies book …

1 Like

:slight_smile:

I think I’m at the end …

Have added this to incorporate pressing the left/right arrow to make up for the fact that when I use the mouse to click on your scrollbar of your scroller, the drop menu that contains the currently selected <li> always scrolls into view via scrollIntoView().

This automatic scrolling into view prohibits clicking on your scrollbar to see other menus etc … so the left/right arrows allow the user to do just that:

else if (code === 39)   // right arrow
{
	$MBScroller.focus();   // = $("#menubar").focus()
	
	var dx = $MBScroller[0].clientWidth * .10;
	// right arrow means we are seeing more of the
	// menus on the far-right and seeing less of
	// the left => scrollLeft is increasing
	$MBScroller[0].scrollLeft += dx;
}
else if (code === 37)   // left arrow
{
	$MBScroller.focus();
	
	var dx = $MBScroller[0].clientWidth * .10;
	// left arrow means we are seeing more of the
	// menus on the far-left and seeing less of
	// the right => scrollLeft is decreasing
	$MBScroller[0].scrollLeft -= dx;
}

Note that whereas I can fast-TAB via continuous pressing of the TAB key to rapidly traverse from one li to another … but I have got to press an arrow key, release it (one at a time), versus pressing and holding an arrow key. I’m guessing it’s because I call $MBScroller[0].scrollLeft +- dx each time.

Gotta learn about fx queues.

1 Like

SOLVED = place code within keydown event versus keyup

1 Like

Well done :slight_smile: Glad you got there in the end.

default elements in html that are tabbable are:

<a> , <area> , <button> , <input> , <object> , <select> , and <textarea>

If someone were to “plop” my code for drop-down menus, my tabbable elements are my li.

What I wish to do is select a non- TAB key to traverse my li, thus keeping the TAB key reserved by HTML

FWIW, I just let my selection of the TAB key ride … eventually it gets to my last li and then tabs to your <a> or <input> html and then proceeds to the Browser’s tabbable stuff and cycles back to the beginning of my li items.

Not too awful I guess?

Suggestions appreciated

Usually with TAB its best left to do its own thing. Once you start messing with it or controlling it then things get difficult.:slight_smile:

I suppose you could take your menu out of the tab loop and then do all the tabbing to and fro via js but that seems a lot of work.

tabindex="0" will allow any element to be tabbed to in the natural order and tabindex="-1" stops it being tabbable.

Your advice is golden

I’m done

1 Like

There oughta be a rule that once in a while that typos have zero effect, like in horseshoes where “close is good enough”

1 Like