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

Improve Mobile Navigation #697

Open
dthenley opened this issue Mar 29, 2021 · 3 comments
Open

Improve Mobile Navigation #697

dthenley opened this issue Mar 29, 2021 · 3 comments

Comments

@dthenley
Copy link
Contributor

Issue Overview

Currently the mobile navigation is not very project ready IMO. The mobile navigation menu item with dropdowns does not hide the dropdown mobile but displays the full menu on mobile. It also removes the dropdown toggle as well.

Also the way it is set up on WPRig current site the menu is in the corner even when you select the menu button it opens in a corner on the right.

Describe your environment

Steps to Reproduce

Expected Behavior

Have an actual mobile menu where you can click the menu item or the dropdown toggle to then open the submenu.

Current Behavior

Possible Solution

Screenshots / Video

image

Related Issues and/or PRs

@Spleeding1
Copy link
Contributor

This is definitely an issue. It looks like it should work, but it doesn't. I guessing that someone is working on this based on the information given in the navigation.js?

@Spleeding1
Copy link
Contributor

I got through this issue today. It still needs some final tweaking but it is functional.

I made changes to global.css and navigation.js. All of the changes I made are marked with "TODO: FIXME:". I also left the original code commented above my changes so they can be seen. I'm not sure how to get added to this project as a contributor. @robruiz can you take a look at this since it seems you are the only contributor these days? What happened to @mor10 and the rest of the crew?

global.css changes

/*--------------------------------------------------------------
## Basic navigation menus - handles submenu and small screen toggle
--------------------------------------------------------------*/
.nav--toggle-small .menu-toggle {
	display: block;
	margin: 1.2em auto;
	padding: 0.6em 1.2em 0.5em;
	font-family: var(--highlight-font-family);
	font-stretch: condensed;
	font-size: 80%;
	text-transform: uppercase;
	border: 2px solid var(--border-color-dark);
	border-radius: 0;
	background: transparent;
}

.nav--toggle-small .menu {
	display: none;
}

/* TODO: FIXME: Changed properties of original */
/* .nav--toggle-sub .dropdown,
.nav--toggle-sub .dropdown-toggle {
	display: none;
} */

.nav--toggle-sub .dropdown,
.nav--toggle-sub .dropdown-toggle {
	font-size: 1em;
	float: right;
	position: absolute;
	right: 10px;
	padding: .8em .6em .5em;
}

/* TODO: FIXME: Added from @media (--wide-menu-query) and modified */
.nav--toggle-sub .dropdown-symbol {
	display: block;
	background: transparent;
	border: solid var(--border-color-dark);
	border-width: 0 2px 2px 0;
	transform: translateY(-50%) rotate(45deg);
	padding: 5px;
}

.menu-item--has-toggle ul {
	display: none;
}

/* TODO: FIXME: Added to show when dropdown clicked */
.menu-item--toggled-on > ul.toggle-show {
	display: block;
}

/* TODO: FIXME: Added from @media (--wide-menu-query) to hide when main menu is opened. */
.nav--toggle-sub ul ul {
	display: none;
	position: relative;
	flex-direction: column;
	background: #fff;
	margin-left: 0;
	box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
	z-index: 100;
}

@media (--narrow-menu-query) {
	
		.nav--toggle-small.nav--toggled-on .menu {
		display: block;
	}
}

@media (--wide-menu-query) {

	.nav--toggle-small .menu-toggle {
		display: none;
	}

	.nav--toggle-small .menu {
		display: block;
	}

	/* TODO: FIXME: Added outsde of media query.
	 * Modified
	 */
	/* .nav--toggle-sub ul ul {
		display: none;
		position: absolute;
		top: 100%;
		flex-direction: column;
		background: #fff;
		margin-left: 0;
		box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
		z-index: 100;
	} */

	.nav--toggle-sub ul ul {
		position: absolute;
	}

	/* TODO: FIXME: Modified original */
	/* .nav--toggle-sub .dropdown,
	.nav--toggle-sub .dropdown-toggle {
		display: block;
		background: transparent;
		position: absolute;
		right: 0;
		top: 50%;
		width: var(--dropdown-symbol-width);
		height: var(--dropdown-symbol-width);
		font-size: inherit;
		line-height: inherit;
		margin: 0;
		padding: 0;
		border: none;
		border-radius: 0;
		transform: translateY(-50%);
		overflow: visible;
	} */

	.nav--toggle-sub .dropdown,
	.nav--toggle-sub .dropdown-toggle {
		display: block;
		background: transparent;
		right: 0;
		top: 50%;
		width: var(--dropdown-symbol-width);
		height: var(--dropdown-symbol-width);
		font-size: .8em;
		line-height: inherit;
		margin: 0;
		padding: 0;
		border: none;
		border-radius: 0;
		transform: translateY(-50%);
		overflow: visible;
		float: none;
	}

	/* TODO: FIXME: Modified original */
	/* .nav--toggle-sub .dropdown-symbol {
		display: block;
		background: transparent;
		position: absolute;
		right: 20%;
		top: 35%;
		width: 60%;
		height: 60%;
		border: solid var(--border-color-dark);
		border-width: 0 2px 2px 0;
		transform: translateY(-50%) rotate(45deg);
	} */
	
	.nav--toggle-sub .dropdown-symbol {
		display: block;
		right: 20%;
		top: 35%;
		width: 60%;
		height: 60%;
	}

	.nav--toggle-sub ul ul .dropdown,
	.nav--toggle-sub ul ul .dropdown-toggle {
		top: 40%;
		right: 0.2em;
	}

	.nav--toggle-sub ul ul .dropdown-symbol {
		transform: rotate(-45deg);
	}

	.nav--toggle-sub .dropdown-toggle:hover,
	.nav--toggle-sub .menu-item--has-toggle:hover .dropdown-toggle {
		pointer-events: none;
	}

	/* Need menu-item-has-children for non-JS */
	.nav--toggle-sub li.menu-item-has-children,
	.nav--toggle-sub li.menu-item--has-toggle {
		position: relative;
		padding-right: var(--dropdown-symbol-width);
	}

	/*
	 * If the dropdown toggle is active with JS, then
	 * we'll take care of showing the submenu with JS.
	 */
	.nav--toggle-sub li:hover > ul,
	.nav--toggle-sub li.menu-item--toggled-on > ul,
	.nav--toggle-sub li:not(.menu-item--has-toggle):focus > ul {
		display: block;
	}

	/*
	 * "focus-within" is an alternative to focus class for
	 * supporting browsers (all but IE/Edge) for no-JS context
	 * (e.g. AMP) See https://caniuse.com/#feat=css-focus-within
	 *
	 * This selector needs to stay separated, otherwise submenus
	 * will not be displayed with IE/Edge.
	 */
	.nav--toggle-sub li:not(.menu-item--has-toggle):focus-within > ul {
		display: block;
	}
}

/*--------------------------------------------------------------
## Main navigation menu
--------------------------------------------------------------*/
.main-navigation {
	display: block;
	margin: 0 auto 2em;
	padding: 0 1em;
	max-width: var(--content-width);
	font-family: var(--highlight-font-family);
	font-stretch: condensed;
}

/* TODO: FIXME: Modified original. Reset in @media (--wide-menu-query) */
/* .main-navigation a {
	display: block;
	width: 100%;
	padding: 0.5em 1em 0.5em 0;
	text-decoration: none;
	color: var(--global-font-color);
} */

.main-navigation a {
	display: inline-block;
	width: 100%;
	padding: 0.5em 1em 0.5em 0;
	text-decoration: none;
	color: var(--global-font-color);
}

.main-navigation a:hover,
.main-navigation a:focus {
	text-decoration: underline;
}

.main-navigation .current-menu-item > a,
.main-navigation .current-menu-ancestor > a {
	font-weight: 600;
}

.main-navigation ul {
	display: block;
	list-style: none;
	margin: 0;
	padding: 0;
}

.main-navigation ul ul li {
	padding-left: 1em;
}

@media (--wide-menu-query) {
	
	/* TODO: FIXME: Added to query */
	.main-navigation a {
		display: block;
	}

	.main-navigation ul li a {
		padding: 0.4em 0.5em;
	}

	.main-navigation ul li {
		margin: 0 0 0 0.5em;
	}

	.main-navigation ul li:first-child {
		margin-left: 0;
	}

	.main-navigation ul ul a {
		width: 200px;
	}

	/* stylelint-disable */
	.main-navigation ul ul li {
		padding-left: 0;
		margin-left: 0;
	}
	/* stylelint-enable */

	.main-navigation ul ul li a {
		width: 218px;
		background: none;
	}

	.main-navigation ul ul ul {
		top: 0;
		left: 100%;
		min-height: 100%;
	}

	.main-navigation .menu {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
	}
}

navigation.js changes

/**
 * Initiate the script to process submenu
 * navigation toggle for a specific navigation menu.
 * @param {Object} nav Navigation element.
 */
function initEachNavToggleSubmenu( nav ) {
	// Get the submenus.
	const SUBMENUS = nav.querySelectorAll( '.menu ul' );

	// No point if no submenus.
	if ( ! SUBMENUS.length ) {
		return;
	}

	// Create the dropdown button.
	const dropdownButton = getDropdownButton();

	for ( let i = 0; i < SUBMENUS.length; i++ ) {
		const parentMenuItem = SUBMENUS[ i ].parentNode;
		let dropdown = parentMenuItem.querySelector( '.dropdown' );

		// If no dropdown, create one.
		if ( ! dropdown ) {
			// Create dropdown.
			dropdown = document.createElement( 'span' );
			dropdown.classList.add( 'dropdown' );

			const dropdownSymbol = document.createElement( 'i' );
			dropdownSymbol.classList.add( 'dropdown-symbol' );
			dropdown.appendChild( dropdownSymbol );

			// Add before submenu.
			SUBMENUS[ i ].parentNode.insertBefore( dropdown, SUBMENUS[ i ] );
		}

		// Convert dropdown to button.
		const thisDropdownButton = dropdownButton.cloneNode( true );

		// Copy contents of dropdown into button.
		thisDropdownButton.innerHTML = dropdown.innerHTML;

		// Replace dropdown with toggle button.
		dropdown.parentNode.replaceChild( thisDropdownButton, dropdown );

		// TODO: FIXME: Changed to if statement to catch 'click' on <i class="dropdown-symbol"></i> inside button.
		// Toggle the submenu when we click the dropdown button.
		// thisDropdownButton.addEventListener( 'click', ( e ) => {
		// 	toggleSubMenu( e.target.parentNode );
		// } );

		thisDropdownButton.addEventListener( 'click', ( e ) => {
			if ( e.target.classList.contains( 'dropdown-symbol')) {
				toggleSubMenu( e.target.parentNode.parentNode);
			} else {
				toggleSubMenu( e.target.parentNode );
			}
		} );

		// Clean up the toggle if a mouse takes over from keyboard.
		parentMenuItem.addEventListener( 'mouseleave', ( e ) => {
			toggleSubMenu( e.target, false );
		} );

		// When we focus on a menu link, make sure all siblings are closed.
		parentMenuItem.querySelector( 'a' ).addEventListener( 'focus', ( e ) => {
			const parentMenuItemsToggled = e.target.parentNode.parentNode.querySelectorAll( 'li.menu-item--toggled-on' );
			for ( let j = 0; j < parentMenuItemsToggled.length; j++ ) {
				toggleSubMenu( parentMenuItemsToggled[ j ], false );
			}
		} );

		// Handle keyboard accessibility for traversing menu.
		SUBMENUS[ i ].addEventListener( 'keydown', ( e ) => {
			// These specific selectors help us only select items that are visible.
			const focusSelector = 'ul.toggle-show > li > a, ul.toggle-show > li > button';

			if ( KEYMAP.TAB === e.keyCode ) {
				if ( e.shiftKey ) {
					// Means we're tabbing out of the beginning of the submenu.
					if ( isfirstFocusableElement( e.target, document.activeElement, focusSelector ) ) {
						toggleSubMenu( e.target.parentNode, false );
					}
					// Means we're tabbing out of the end of the submenu.
				} else if ( islastFocusableElement( e.target, document.activeElement, focusSelector ) ) {
					toggleSubMenu( e.target.parentNode, false );
				}
			}
		} );

		SUBMENUS[ i ].parentNode.classList.add( 'menu-item--has-toggle' );
	}
}

@somaGFX
Copy link

somaGFX commented Mar 21, 2022

I got through this issue today. It still needs some final tweaking but it is functional.

navigation.js changes

		// TODO: FIXME: Changed to if statement to catch 'click' on <i class="dropdown-symbol"></i> inside button.
		// Toggle the submenu when we click the dropdown button.
		// thisDropdownButton.addEventListener( 'click', ( e ) => {
		// 	toggleSubMenu( e.target.parentNode );
		// } );

		thisDropdownButton.addEventListener( 'click', ( e ) => {
			if ( e.target.classList.contains( 'dropdown-symbol')) {
				toggleSubMenu( e.target.parentNode.parentNode);
			} else {
				toggleSubMenu( e.target.parentNode );
			}
		} );

I tried your solution from the navigation.js first it seems to work good but if you have two or more submenus ( I do not speak of sub submenus ) it's not working properly due to the mouseleave event. I then put up another eventListener to listen on the touchstart event and set a helper variable to true. In the mouseleave event it first checks if there was a touch event triggered and if not than fire the toggleSubMenu. Like so (see the GOT ADDED and GOT CHANGED sections):

/**
 * Initiate the script to process submenu
 * navigation toggle for a specific navigation menu.
 * @param {Object} nav Navigation element.
 */
function initEachNavToggleSubmenu( nav ) {
	// GOT ADDED 
	// Helper var for storing if touch event happened.
	let isTouched;

	// Get the submenus.
	const SUBMENUS = nav.querySelectorAll( '.menu ul' );

	// No point if no submenus.
	if ( ! SUBMENUS.length ) {
		return;
	}

	// Create the dropdown button.
	const dropdownButton = getDropdownButton();

	for ( let i = 0; i < SUBMENUS.length; i++ ) {
		const parentMenuItem = SUBMENUS[ i ].parentNode;
		let dropdown = parentMenuItem.querySelector( '.dropdown' );

		// If no dropdown, create one.
		if ( ! dropdown ) {
			// Create dropdown.
			dropdown = document.createElement( 'span' );
			dropdown.classList.add( 'dropdown' );

			const dropdownSymbol = document.createElement( 'i' );
			dropdownSymbol.classList.add( 'dropdown-symbol' );
			dropdown.appendChild( dropdownSymbol );

			// Add before submenu.
			SUBMENUS[ i ].parentNode.insertBefore( dropdown, SUBMENUS[ i ] );
		}

		// Convert dropdown to button.
		const thisDropdownButton = dropdownButton.cloneNode( true );

		// Copy contents of dropdown into button.
		thisDropdownButton.innerHTML = dropdown.innerHTML;

		// Replace dropdown with toggle button.
		dropdown.parentNode.replaceChild( thisDropdownButton, dropdown );

		// GOT CHANGED FROM robruiz
		// Toggle the submenu when we click the dropdown button.
		thisDropdownButton.addEventListener( 'click', ( e ) => {
			if ( e.target.classList.contains( 'dropdown-symbol' ) ) {
				toggleSubMenu( e.target.parentNode.parentNode );
			} else {
				toggleSubMenu( e.target.parentNode );
			}
		} );

		// GOT ADDED
		// Check if there is a touch event so it does not trigger the mouseleave event.
		parentMenuItem.addEventListener( 'touchstart', ( ) => {
			isTouched = true;
		} );

		// GOT CHANGED
		// Clean up the toggle if there was no touch event and a mouse takes over from keyboard.
		parentMenuItem.addEventListener( 'mouseleave', ( e ) => {
			if ( ! isTouched ) {
				toggleSubMenu( e.target, false );
			} else {
				// putting back to false in case there is also a mouse on the device.
				isTouched = false;
			}
		} );

		// When we focus on a menu link, make sure all siblings are closed.
		parentMenuItem.querySelector( 'a' ).addEventListener( 'focus', ( e ) => {
			const parentMenuItemsToggled = e.target.parentNode.parentNode.querySelectorAll( 'li.menu-item--toggled-on' );
			for ( let j = 0; j < parentMenuItemsToggled.length; j++ ) {
				toggleSubMenu( parentMenuItemsToggled[ j ], false );
			}
		} );

		// Handle keyboard accessibility for traversing menu.
		SUBMENUS[ i ].addEventListener( 'keydown', ( e ) => {
			// These specific selectors help us only select items that are visible.
			const focusSelector = 'ul.toggle-show > li > a, ul.toggle-show > li > button';

			if ( KEYMAP.TAB === e.keyCode ) {
				if ( e.shiftKey ) {
					// Means we're tabbing out of the beginning of the submenu.
					if ( isfirstFocusableElement( e.target, document.activeElement, focusSelector ) ) {
						toggleSubMenu( e.target.parentNode, false );
					}
					// Means we're tabbing out of the end of the submenu.
				} else if ( islastFocusableElement( e.target, document.activeElement, focusSelector ) ) {
					toggleSubMenu( e.target.parentNode, false );
				}
			}
		} );

		SUBMENUS[ i ].parentNode.classList.add( 'menu-item--has-toggle' );
	}
}

Maybe it'll help someone too... (of course the CSS needs to be changed as well)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants