CSS Drop Down Menu hidden behind horizontal scrollbar

ha ha :slight_smile: thank you :slight_smile:

1 Like

That doesn’t seem to be Paul’s code you’re using, as it’s still not keyboard accessible, unlike the example in his Codepen.

2 Likes

PaulOB and TechnoBear

I have to put Paul’s keyboard accessibility code toward the end of the line, at least for the moment because I have been trying to find out why the dropdown css menus do not work on the iPhone … i.e., the main menu li.drop titles highlight, but the li.drop .aMenu do not drop.

Sounds suspiciously like a position:absolute problem, but I have:

/* main menu items in #menubar */
#menubar > li {
	display: inline-block;
}

#menubar .aMenu {
/*
	Use display = none/block to hide/show these menus.
	Use left to effect placement of drop menus due to
	their being items in a scrollable menubar.
*/
	display:  none;

	position: absolute;
}

… and later,

/*
	Show drop menu when over main menu and
	show 1st sub-menu when over sub-menu's parent
*/
#menubar li:hover .aMenu {
	display: block;
}

All this works on my iMac desktop (Safari, Chrome + Firefox), but not on my iPhone (Safari, Chrome + Firefox), i.e., drop menu titles highlight, but no drop menu (.aMenu) shows.

For the record, I use the very old “suckerfish” code which has the following .js code:

 function sfHover() {

	if (document.getElementById && document.getElementsByTagName)
		var sfMenus = document.getElementById("menubar").getElementsByTagName("li");
	else
		var sfMenus = document.all.tags("li");
	
	for (var i = 0; i < sfMenus.length; i++)
	{
		sfMenus[i].onmouseover = function()
		{
			this.className += " sfhover";
		}
		
		sfMenus[i].onmouseout = function()
		{
			this.className = this.className.replace(" sfhover", "");
			this.className = this.className.replace(" sfclick", "");
		}
	}
	
}   // function sfHover() {

<body onload="sfHover(); scrollMenusAdjust()">

FYI, that 2nd function is Paul’s adjust javascript for accessing far-right menus scrolled out of view.

One more tidbit, some .html code might be nice:

		<li class="drop">
			<a href="#">text2</a>   <!-- #menubar > li -->
			<ul class="aMenu">                    <!-- drop menu = .aMenu -->
				<li><a href="#">text21</a></li>   <!-- drop-menu item -->
				<li><a href="#">text22</a></li>   <!-- another drop-menu item  -->
			</ul>
		</li>

One last bit,

iPhone works great IF I forego the scrollable menubar, that is, I do not have:

#menubar {
	white-space: nowrap;   /* default = normal  */
	overflow-y:  hidden;   /* default = visible */
	overflow-x:  auto;     /* default = visible */
	-webkit-overflow-scrolling: touch;   /* for iOS */
	
	padding-left: 0em;   /* negate natural indention of ul list items */
}

But, a scrolling menubar is mandatory for me.

It’s the -webkit-overflow-scrolling:touch that causes the problem as it creates a stacking context and thus the overflow is restricted.

I removed it from my demo and the menu works fine and indeed there is still momentum scrolling on my iphone SE.

2 Likes

OMG … so simple. Would have been scratching my head forever and still not discovered this short beauty. THANK YOU, THANK YOU, THANK YOU.

Down to the keyboard accessibility issue.

THANKS AGAIN.

2 Likes

2 posts were split to a new topic: Problem downloading sitePoint book to iPhone

How to specify multiple classes?

From what I have read, the answer is, for example:

$multipleClasses= $(“p.class1,p.class2”);

  • or

$multipleClasses= $(“p:not(.class3),p:not(.class4));

If you say that the above is correct, I will have to dig deeper into my code.

It is correct AND I solved my problem by ignoring $multipleClasses and instead using a if clause to solve my problem; e.g.,

if( !$aparm.hasClass("class3") &&  !$aparm.hasClass("class4") {

/* do stuff */

}

Well … it looks like I will tackle PaulOB’s keyboard interaction with the css drop menus. When and only when I understand what he did, then I will add the code to mine - with, once again, Paul’s name in fireworks as the source.

1 Like

It looks as though you have solved your problem to your satisfaction but I thought I might explain the selectors a little.

If the multiple classes are on the element itself then you would target with dot separated selectors the same as you would in CSS.

e.g.

<p class="test1 test2">Example 1</p>
<p class="test1 test2 test3">Example 2</p>
<p class="test1">Example 3</p>
$("p.test1.test2").on( "click", function() {
  	$(this).toggleClass('active');
});

Example1 and Example 2 would both activate the active class when clicked but clicking example 3 would do nothing.

In CSS you would say.

.test1.test2{background:red}

That is different to saying

.test1, test2{ background:red }

The first example must find an example that contains the 2 classes while the second example only checks if one class is present.

Or if you wanted to exclude those 2 classes you could do something like:

$("p").not( ".test1.test2" ).on( "click", function() {
  	$(this).toggleClass('active');
});

Hope that clears it up a little :slight_smile:

1 Like

PaulOB

FYI, the comments say it all:

/* from PaulOB at sitepoint.com */
function scrollMenusAdjust() {

	const scroller = document.querySelector("#menubar");
	const dropDown = document.querySelectorAll("li.drop > .aMenu");

	scroller.addEventListener("scroll", checkScroll);

	function checkScroll() {
		for (let i = 0; i < dropDown.length; i++) {
			dropDown[i].style.transform = "translateX(-" + scroller.scrollLeft + "px)";
		/*
			.transform creates a new Stacking Index such that if there is just a
			regular (not <li.drop>) menu item in #menubar with a <a href> link,
			then after returning from that linked destination, any drop-menus of
			the other menu items will then show *below* the scrollbar.
		*/
			dropDown[i].style.zIndex = 100;   /* this cures it */
		}
	}

}   // function scrollMenusAdjust {

I have no further excuses … keyboard access, here I come.

Did find out that accesskey="1" or whatever does not work … the simple hardly ever does.

A few more cups of coffee and I’ll burrow down to your code, Paul, and go from there.

It may be simple to implement in theory, but it’s fraught with difficulties in practice.

https://webaim.org/techniques/keyboard/accesskey

1 Like

A clue :slight_smile:

:focus-within

Good Grief - it took me 3 hours to figure out why Safari wasn’t tabbing … you have to set an Advanced Preference to make it happen.

Chrome, Firefox are good kids, but not Safari.

I even found out that my using display: none; to hide sub-sub menus is a big NO NO. So, I returned to left: -99999px; to hide and left: auto; to show. Now, tabbing to hidden stuff is back.

Gotta figure out how to make my tabbing in to a item in a buried sub-menu temporarily visible and back to hidden when tabbing out.

See you later …

(I’m still “upset” at Safari for abusing my 3 hours)

Guys, I’m getting there. Back to burrow mode.

I’m no speedy wheels, but I am getting there … I still am burrowing around for inserting CMD-key equivalents, e.g., CMD-A for “About …”.

I just uploaded the current site …

CHALLENGE: accessing compound class with .closest() … in short, .closest does not find my intended target

HTML snippet:

<ul id="menubar">

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

			<li>
				<a href="ABOUT.html">
				text22
				</a>
			</li>
			<!-- parent of sub-menu  -->
			<li class="daddy">
				text23
				<!--
					sub-menu = .daddy .aMenu
					sub-menu item = .daddy .aMenu li
				-->
					<ul class="aMenu">
					<li>text231</li>
					<!-- parent of sub-sub-menu -->
					<li class="daddy">
						text232
						<ul class="aMenu">
							<li>
								<a href="ABOUT.html">
								text2321
								</a>
							</li>
							<!-- parent of sub-sub-sub-menu -->
							<li class="daddy">
								text2322
								<ul class="aMenu">
									<li>text23221</li>
								</ul>
							</li>   <!-- daddy text2322 -->
						</ul>
					</li>   <!-- daddy text232 -->
				</ul>
			</li>   <!-- daddy text23 -->
		</ul>
		
	</li>   <!-- drop menu item "Demo" -->

</ul/>   <!-- #menubar -->

JAVASCRIPT snippet:

function getSubMenu($currItem) {
	
	$itsSubMenu = $currItem.closest("li.daddy").find(.aMenu);
		
	return $itsSubMenu;

}


function getDropMenu($currItem) {
	
	$itsDropMenu = $currItem.closest("li.drop").find(.aMenu);
		
	return $itsDropMenu;

}


function getMainMenu($currItem) {
	
	$itsMainMenu = $currItem.closest("#menubar").find(li);;
		
	return $itsMainMenu;

}

What I’d like to do is:

$itsSubMenu = $currItem.closest("li.daddy > .aMenu");

and

$itsDropMenu = $currItem.closest("li.drop > .aMenu");

… but NEITHER using .closest().find() nor .closest() by itself works!

aMenu needs quotes around it.

.find(".aMenu")

TechnoBear,

Well, it’s getting nicer and accessible for keyboard users … with the Tab key.

" … frought with difficulties"
The understatement of the millineum.

Here’s another understatement “the last 5% takes 95% of the time”.

lovesongforever.com/testmenubar

BTW I am aware of the non-dismissal of each .aMenu when tabbing backwards and switching between keyboard access and mouse :hover event.

But, it’s getting there.

It is an improvement, but it’s somewhat confusing, as it requires double tabbing to reach a link, which is not how any other menu I’ve used behaves. Tab once and the outline appears to indicate the item is in focus, but pressing “Enter” at that point does nothing. It requires a second tab to focus on the link text within the item before it can be used.

As you say, the fact that the drop-downs persist is a big issue, too.

To quote Fred Flintstone

Yabby Dabby Do

Just about there. Every thing seems to work, except Shift-Tab (backward).

But, Tab (forward) seems to be in place. And to think it all started with Paul’s hint “focus-within” and Technobear’s unrelenting insistence on keyboard Tabbing thru all the sub-menus.

Note, e.g., that the user has to Tab just once (not 2X) and then hit Enter/Return to activate the link even though the <a>'s are the children of the <li>'s:

$("a").each(function() {
    $(this).attr("tabindex", -1);
});

followed by:

$("li").on('click', function (evt) {
	$currItem = getFocusedItem();
			
	$aLink = $currItem.children("a");
	if ($aLink.length != 0) {
		window.location = $aLink.attr("href");
	}
});

Also, got wrapped around the flagpole when I was trying to test for equality a jQuery object with a DOM object without the [0].

Note, how the sub-menus appear/disappear as each <li> parent is tabbed to. The <a>’s are out of the tabbing sequence.

Never sure, but I think that just about covers it … except for tabbing backwards (Shift-Tab).

(Note that I just bought a wireless keyboard for my iMac and it took me about 30 minutes to figure out that Enter = Shift-Return or fn-Return. The Devil is in the details.)

“Forward” to dealing with tabbing backwards … OR … maybe using the Arrow Keys to also change :focus.

Someone at SO doesn’t like using anything other than Tab/Shift-Tab because “So, you want to break what the arrow keys normally do (scroll the page in different directions) just on your page, even though other keys (tab and shift-tab) already do the job? For your users’ sake I hope you’re building a spreadsheet, because I can’t see this being a good idea any other time.”