I’m trying to modify this fiddle https://jsfiddle.net/xobea9cm/ as it pretty much has the exact function that I’m after, except the semantics of the html are not great.
In a nutshell, I’m after a horizontal navigation like so
|header and hero |
|nav item 1 nav item 2 nav item 3 MORE v |
|body copy |
Within “more” there would be “nav item 4” and “nav item 5”. It would change with screen size. As you reduce the browser width, “nav item 3” would be moved into “more”, as you increase the browser width, the nav items within “more” would flow back out when horizontal space permits.
The original version html is “divitus” and looks like:
<nav class="nav">
<div id="menu" class="menu">
<div class="menuitem">One</div>
<div class="menuitem">Two</div>
<div class="menuitem">Three</div>
<div class="menuitem">Four</div>
<div class="menuitem">Five</div>
<div class="menuitem">Six</div>
<div class="more-items">
<div id="more" class="more-item menuitem">More</div>
<div class="menuitem">Seven</div>
<div class="menuitem">Eight</div>
<div class="menuitem">Nine</div>
</div>
</div>
</nav>
My version is here: https://jsfiddle.net/oywkdr6e/ where my html currently looks like:
<nav class="nav-more">
<ul class="nav-more-menu">
<li class="nav-more-menu-item">nav item one</li>
<li class="nav-more-menu-item"><a href="#two">nav item two</a></li>
<li class="nav-more-menu-item"><a href="#three">nav item three</a></li>
<li class="nav-more-menu-item"><a href="#four">nav item four</a></li>
<li class="nav-more-menu-item"><a href="#five">nav item five</a></li>
<li class="nav-more-menu-item"><a href="#six">nav item six</a></li>
<li class="more">
<!--THIS IS WHAT I'M TRYING TO CHANGE-->
<li class="more-trigger nav-more-menu-item">More</li>
<li class="nav-more-menu-item"><a href="#seven">nav item seven</a></li>
<li class="nav-more-menu-item"><a href="#eight">nav item eight</a></li>
</li>
</ul>
</nav>
What I want is this:
<nav class="nav-more">
<ul class="nav-more-menu">
<li class="nav-more-menu-item">nav item one</li>
<li class="nav-more-menu-item"><a href="#two">nav item two</a></li>
<li class="nav-more-menu-item"><a href="#three">nav item three</a></li>
<li class="nav-more-menu-item"><a href="#four">nav item four</a></li>
<li class="nav-more-menu-item"><a href="#five">nav item five</a></li>
<li class="nav-more-menu-item"><a href="#six">nav item six</a></li>
<li class="more">
<!--THIS IS WHAT I WANT-->
<button class="more-trigger">More</button>
<ul class="more-items">
<li class="nav-more-menu-item"><a href="#seven">nav item seven</a></li>
<li class="nav-more-menu-item"><a href="#eight">nav item eight</a></li>
</ul>
</li>
</ul>
</nav>
The current functionality has the “more” menu displaying on a hover of <li class="more">, but I eventually want that available on a button trigger for accessibility, so I’ll just show/hide that button’s sibling <ul class="more-items"> and position it like a dropdown menu.
I’ve tried several times to update the JS for the new markup but I seem to always end up at the same place. On page load, it looks good, but when I resize the browser width, my DOM gets appended with <ul class="more-items"> on every resize event.
How would you fix this to output the desired markup? There’s something I’m just not getting with updating the DOM. Here is the JS:
const parser = new DOMParser();
function dealWithMenu(navMoreMenu) {
//console.log('dealWithMenu()');
let navLIs = navMoreMenu.querySelectorAll('li');
let moreLI = parser.parseFromString('<li class="more"></li>', 'text/html').body.firstChild;
//console.log('navLIs:', navLIs);
//console.log('moreLI:', moreLI);
//console.log('navMoreMenu:', navMoreMenu);
let count = 0;
for (var i = navLIs.length; i--;) {
let $this = navLIs[i];
//show at least two nav items
if (count >= navLIs.length - 2) {
continue;
}
//if trailing nav item offsetTop value is different than first nav item, trailing nav item has wrapped to a new line, so move into "more" dropdown
if ($this.offsetTop > navLIs[0].offsetTop || moreLI.offsetTop > navLIs[0].offsetTop) {
navMoreMenu.appendChild(moreLI);
moreLI.insertBefore($this, moreLI.firstChild);
count++;
} else {
i = 0;
}
}
//we have overflow nav items in "more" dropdown
if (moreLI.children.length) {
console.log('moreLI.children.length', moreLI.children.length);
moreLI.insertBefore(
parser.parseFromString('<li class="more-trigger nav-more-menu-item">More</li>', 'text/html').body.firstChild,
moreLI.firstChild
);
}
moreLI.addEventListener('click', (e) => {
console.log('more click:', e.target);
});
}
function shorterMenu() {
//console.log('shorterMenu()');
const navMoreMenu = document.querySelector('.nav-more-menu');
const moreLI = navMoreMenu.querySelector('.more');
const moreTrigger = navMoreMenu.querySelector('.more-trigger');
//console.log('moreLI:', moreLI);
//console.log('moreTrigger:', moreTrigger);
moreTrigger?.remove();
if (moreLI != undefined && moreLI.children?.length > 0) {
Array.from(moreLI.children).forEach(child => moreLI.parentElement.appendChild(child));
moreLI.remove();
}
dealWithMenu(navMoreMenu);
}
shorterMenu();
window.addEventListener('resize', () => shorterMenu());