4
\$\begingroup\$

I have some vanilla JavaScript for a website menu which works perfectly (thanks mainly to Titus on Stack Overflow), opening and closing a sub-menu (toggling <ul class="hidden"> ) if the UL's sibling button is clicked or closing the sub-menu if another button on the same level as the sibling is clicked, adjusting other CSS classes for the <li> elements appropriately. The code is specific to each level of the menu and I have repeated the code for several more menu levels, just changing each occurrence of '1' to '2' and then to '3' and so on (potentially there could be up to 10 levels; I already have 5 for testing purposes). Is there a way to use less lines of code whilst maintaining each level's independence?

Here is the code for two levels:

const buttons1 = Array.from(document.querySelectorAll(".btn-level-1"));
buttons1.forEach((button1) => {
    button1.addEventListener('click', () => {
    buttons1.filter(b1 => b1 != button1).forEach(b1 => {
        b1.classList.remove('opened');
        b1.classList.add('not-open');
        b1.nextElementSibling.classList.add('hidden')
        });
    button1.nextElementSibling.classList.toggle('hidden');
    button1.classList.toggle('not-open');
    button1.classList.toggle('opened');
    });
});
const buttons2 = Array.from(document.querySelectorAll(".btn-level-2"));
buttons2.forEach((button2) => {
    button2.addEventListener('click', () => {
    buttons2.filter(b2 => b2 != button2).forEach(b2 => {
        b2.classList.remove('opened');
        b2.classList.add('not-open');
        b2.nextElementSibling.classList.add('hidden')
        });
    button2.nextElementSibling.classList.toggle('hidden');
    button2.classList.toggle('not-open');
    button2.classList.toggle('opened');
    });
});

Demo HTML, CSS and another bit of JS in a snippet:

document.addEventListener('DOMContentLoaded', () => {
    document.getElementById("mainbody").addEventListener("click", closeMenu);
    function closeMenu(event) {
    if(event.target.type != "button") {
    let level1 = document.getElementsByClassName("level-1");
    let level2 = document.getElementsByClassName("level-2");
    let btnlevel1 = document.getElementsByClassName("btn-level-1");
    let btnlevel2 = document.getElementsByClassName("btn-level-2");
        for (let i = 0; i < level1.length; i++) {
        level1[i].classList.add("hidden"); }
        for (let i = 0; i < level2.length; i++) {
        level2[i].classList.add("hidden"); }
        for (let i = 0; i < btnlevel1.length; i++){
        btnlevel1[i].classList.remove("opened");}
        for (let i = 0; i < btnlevel2.length; i++) {
        btnlevel2[i].classList.remove("opened");}
        }
    }
    const buttons1 = Array.from(document.querySelectorAll(".btn-level-1"));
    buttons1.forEach((button1) => {
        button1.addEventListener('click', () => {
        buttons1.filter(b1 => b1 != button1).forEach(b1 => {
            b1.classList.remove('opened');
            b1.nextElementSibling.classList.add('hidden')
            });
        button1.nextElementSibling.classList.toggle('hidden');
        button1.classList.toggle('opened');
        });
    });
    const buttons2 = Array.from(document.querySelectorAll(".btn-level-2"));
    buttons2.forEach((button2) => {
        button2.addEventListener('click', () => {
        buttons2.filter(b2 => b2 != button2).forEach(b2 => {
            b2.classList.remove('opened');
            b2.nextElementSibling.classList.add('hidden')
            });
        button2.nextElementSibling.classList.toggle('hidden');
        button2.classList.toggle('opened');
        });
    });
})
.sticky-main-menu {
    position: fixed;
    top: 6rem;
    left: 0;
    right: 0;
    z-index: 9999;
  }
  .stickymenuwrapper {
    background-color: #fff;
    transition-duration: 0s;
    padding: 1rem;
    margin: 0 auto;
    z-index: 995;
    border: 1px solid gray;
  }
  .hmenu .hidden {
    display: none;
  }
  .hmenu ul {
    list-style: none;
    margin-bottom: 0;
    white-space: nowrap;
    padding: 0;
  }
  .hmenu ul.navbar {
    background-color: #f7a1d6;
    padding: 0 1rem;
    justify-content: center;
    display: flex; /* required for VSC */
    /* text-align: center;*/
  }
  .hmenu .navbar {
    line-height: 1.5;
  }
  .hmenu li ul {
    position: absolute;
    top: 100%;
    background-color: pink;
    padding: 0 12px 12px;
  }
  .hmenu ul[class^="level"] {
    background-color: white;
    position: relative;
    top: 100%;
    left: 0;
    padding: 1rem 0 1rem 1rem;
    margin-top: 0.5rem;
  }
  .hmenu ul.level-1 {
    position: absolute;  /* ? */
    top: 100%;
    left: 0;
    margin-top: 0;
    padding: 1rem;
    box-shadow: 0 2px 5px #acaaaa;
  }
  .hmenu ul.level-2,
  .hmenu ul.level-4{
    background-color: #dedede;
  }
  .hmenu ul li:last-child {
    padding-bottom: 0;
  }
  .hmenu li.active>a {
    text-decoration: underline;
  }
  .hmenu a.separator:hover {
    text-decoration: none;
  }
  .hmenu ul[class^="level"] li {
    margin-right: 1rem;
  }
  .hmenu li {
    position: relative;
  /*  display: flex;*/
    margin: 0;
    padding: 0 0 0.5rem;
    line-height: 1.5rem;
  }
  .hmenu li a {
    color: #0d6efd;
  }
  .hmenu li.parent {
    margin-right: 0; /* was 2rem */
  }
  .hmenu .navbar>li {
    padding: 0;
    margin: 0 0.5rem;
    display: flex;
  }
  .hmenu .navbar>li.parent {
    margin-right: 2rem;
  }
  .hmenu .navbar>li.divider.parent {
    margin-right: 0;
  }
  .hmenu .navbar>li>a {
    padding: 0.5rem 0 0.5rem 0.5rem;
    margin: 0 0 0 0;
  }
  .hmenu .navbar>li:first-child>a {
    padding: 0.5rem 0.5rem 0.5rem 0;
    margin-left: 0;
  }
  .hmenu li.heading>a,
  .hmenu li.divider>a,
  .hmenu li.separator>a {
    text-decoration: none;
    padding-right: 2rem;
  } 
  .hmenu li.separator-line {
    position: relative;
    height: 1rem;
    display: inline-block;
    width: 100%;
  }
  .hmenu li.separator-line span {
    display: none;
  }
  .hmenu li.separator-line:after {
    position: absolute;
    height: 0;
    top: 60%;
    width: 100%;
    border-top: 1px solid#8c8a8a;
    border-bottom: 1px solid lightgray;
    margin: 0 0;
    content: "";
  } 
  .hmenu ul.navbar>li a {
    padding-top: 0.5rem;
    position: relative;
  }
  [type="button"] {
      -webkit-appearance: button;
    }
  [type="button"]:not(:disabled) {
      cursor: pointer;
  }
  .hmenu button,
  .hmenu button:focus {
    display: inline-flex;
    position: absolute;
    background: transparent;
    border: none;
    color: #0d6efd;
    height: 1.5rem;
    outline: none;
    padding: 0 0 0 .5rem;
    margin: 0;
    right: 0;
    width: 40px;
    top: 6px;
  }
   .hmenu li.linked button {
    position: relative;
    top: 0;
  }
  .hmenu li.linked button.btn-level-1 {
    top: 10px;
  }
  
  .hmenu .navbar > li.linked.top-level {
    margin-right: 0.5rem;
  }
  .hmenu button.btn-level-1 {
    right: 0;
    position: absolute;
    top: 0;
  }
  .hmenu li button[class^="btn-level"]:after,
  .hmenu li a[class^="btn-level"]:after{
    display: inline-flex;
    width: 0;
    height: 0;
    margin: 0 .5em;
    content: "";
    transition: all .3s ease-out;
    position: absolute;
    top: 12px;
    cursor: pointer;
    right: -2rem;
    border-top: 10px solid;
    border-right: 8px solid transparent;
    border-left: 8px solid transparent;
  }
  .hmenu li.separator>button[class^="btn-level"]:after,
  .hmenu li.separator>a[class^="btn-level"]:after{
    right: 0;
  }
  .hmenu li button[class^="btn-level"].opened,
  .hmenu li a[class^="btn-level"].opened{
    color: red;
  }
  .hmenu li button[class^="btn-level"].opened:after,
  .hmenu li a[class^="btn-level"].opened:after{
    border-top: none;
    border-bottom: 10px solid;
    border-right: 8px solid transparent;
    border-left: 8px solid transparent;
    top: 12px;
    right: 0;
  }
  .hmenu li.linked > a[class^="btn-level"].opened:after {
    right: -2rem;
  }
  .hmenu li button.linked {
    display: inline-flex;
    position: relative;
  }
  .hmenu li button.linked:after{
    top: 10px;
    right: 0;
  }
  .hmenu li button[class^="btn-level"].linked.opened:after{
    top: 6px;
    right: 0;
  }
  .hmenu li button.btn-level-1.linked {
    display: inline-flex;
    position: relative;
  }
  .hmenu li button.btn-level-1:after,
  .hmenu li button.btn-level-1.opened:after {
    top: 30%;
    right: 0;
  }
  .hmenu li a.btn-level-1:after,
  .hmenu li a.btn-level-1.opened:after{
    top: 40%;
    right: -2rem;
  } 
  
<!DOCTYPE html>
<html lang="en-gb"dir="ltr">
<head>
<meta charSet="utf-8" />
<link rel="stylesheet" href="menu-demo-2a.css">

<script type="text/javascript">
    // Fix for Firefox autofocus CSS bug
    // See: http://stackoverflow.com/questions/18943276/html-5-autofocus-messes-up-css-loading/18945951#18945951
</script>
</head>
<body id="mainbody" class="outer">
    <div id="stickymenuwrapper" class="stickymenuwrapper"><p>Click a triangle to open the sub-menu. Click it again to close the sub-menu or click the other triangle to open its menu and close the first. Clicking a triangle on the next level down will leave the top level open, but its siblings work in the same way.
        Clicking outside the menu but inside this box will also  close the menu. The CSS for this layout is not finished and is not intended for narrow screens. I'm aware that the second triangle will overflow the pink background on a very narrow view. Please ignore that. </p>
        <div class="hmenu">
            <ul id="menucontent" class="navbar">
                <li class="item-103 deeper parent linked">
                    <a href="/" >ITEM 1</a>
                    <button class="btn-level-1 linked" type="button" name="btn-103"></button>
                    <ul class="level-1 hidden not-separator">
                        <li class="item-104">
                            <a href="/" >About us</a>
                        </li>
                        <li class="item-107 divider deeper parent separator">
                            <a class="btn-level-2" type="button" >Level 2 separator</a>
                            <ul class="level-2 hidden separator">
                                <li class="item-108">
                                    <a href="/" >Lower article</a>
                                </li>
                            </ul>
                        </li>
                        <li class="item-141 deeper parent linked">
                            <a class="btn-level-2 heading" type="button" >Menu heading</a>
                            <ul class="level-2 hidden menu-heading">
                                <li class="item-142">
                                    <a href="/" >Article under menu heading</a>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li class="item-122 divider deeper parent">
                    <a class="btn-level-1" type="button">ITEM TWO</a>
                    <ul class="level-1 hidden separator">
                        <li class="item-121">
                            <a href="/" >Test level 2</a>
                        </li>
                        <li class="item-134 deeper parent linked">
                            <a class="btn-level-2 heading" type="button" >Test Two menu heading</a>
                            <ul class="level-2 hidden menu-heading">
                                <li class="item-135">
                                    <a href="/" >Article under menu heading</a>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>
<p>Duis ac lorem. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse potenti. Sed tincidunt varius arcu.</p>
        </div>
    </div>
    <script src="menu-demo-2a.js"></script>
</body>
</html>

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Can you also add the HTML for the menu? This can give more context to what you're trying to achieve. We may even be able to suggest a better approach given that information. \$\endgroup\$
    – Joseph
    Commented Nov 20, 2020 at 15:25
  • \$\begingroup\$ Our rules/guidelines are a little different than stack overflow. This could be a great question if you make a few changes. Please read our guidelines on how to ask a good question. The title should be about what the code does. Since the JavaScript code is affecting the HTML, it would be best to have the HTML in the question as well. \$\endgroup\$
    – pacmaninbw
    Commented Nov 20, 2020 at 15:46
  • \$\begingroup\$ HTML added (and also now mentioned in title). \$\endgroup\$
    – Gillian
    Commented Nov 20, 2020 at 16:47
  • \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see What should I do when someone answers my question? as well as what you may and may not do after receiving answers. \$\endgroup\$ Commented Nov 20, 2020 at 18:30

1 Answer 1

4
\$\begingroup\$

DRY The logic for buttons1 and buttons2 is identical, so condense it by making a function to which you pass a collection of buttons instead - or, iterate over an array of selectors.

for (const selector of [".btn-level-1", ".btn-level-2"]) {
  const buttons = document.querySelectorAll(selector);

Use CSS rules instead of multiple classes, when possible - it'll reduce the number of moving parts in your JavaScript. Here, rather than having an opened and not-open class, apply a default style (say, for not-open) and then have the opened class override it.

In addition, rather than b1.nextElementSibling.classList.add('hidden'), you can use the adjacent sibling combinator in your CSS rules to select the sibling after an open button.

Avoid sloppy equality - it has strange coercion rules a script writer or reader should not be required to have memorized in order to understand the code. Always use === or !== - avoid == and !=.

for (const selector of [".btn-level-1", ".btn-level-2"]) {
    const buttons = [...document.querySelectorAll(selector)];
    for (const button of buttons) {
        button.addEventListener('click', () => {
            buttons.filter(b => b !== button).forEach(b => {
                b.classList.remove('opened');
            });
            button.classList.toggle('opened');
        });
    }
}
button {
  background-color: yellow;
}
.opened {
  background-color: orange;
}

button + span {
  display: none;
}
.opened + span {
  display: inline;
}
<div>
  <button class="btn-level-1">button level 1</button>
  <span>description 1-1 (if you can see this, this button is open)</span>
</div>
<div>
  <button class="btn-level-1">button level 1</button>
  <span>description 1-2 (if you can see this, this button is open)</span>
</div>

<br><br>

<div>
  <button class="btn-level-2">button level 2</button>
  <span>description 2-1 (if you can see this, this button is open)</span>
</div>
<div>
  <button class="btn-level-2">button level 2</button>
  <span>description 2-2 (if you can see this, this button is open)</span>
</div>

With the new code, there are more fixes:

Menu closing To close the menu when a click occurs outside of it:

  • Have the click listener check to see if the menu content is an ancestor of the clicked element - your current check of whether a button is clicked probably isn't intuitive for users, because even clicks inside the menu on a non-button will close it.
  • Instead of iterating over all the different possible class descendants and remove the opened from them, a simple querySelector would be sufficient:
document.body.addEventListener('click', (e) => {
  if (!e.target.closest('#menucontent')) {
    // If click was outside of menu, close menu:
    document.querySelector('.opened')?.classList.remove('opened');
  }
});

Don't use DOMContentLoaded unless you need to - it's unnecessarily enclosing your whole script in another level of indentation. You can avoid DOMContentLoaded by either putting the <script> tag at the bottom of the <body> (which you're doing), or by giving the script tag the defer attribute.

Live demo with the new code:

for (const selector of [".btn-level-1", ".btn-level-2"]) {
  const buttons = [...document.querySelectorAll(selector)];
  for (const button of buttons) {
    button.addEventListener('click', () => {
      buttons.filter(b => b !== button).forEach(b => {
        b.classList.remove('opened');
      });
      button.classList.toggle('opened');
    });
  }
}
document.body.addEventListener('click', (e) => {
  if (!e.target.closest('#menucontent')) {
    // If click was outside of menu, close menu:
    document.querySelector('.opened')?.classList.remove('opened');
  }
});
button + ul, a[type=button] + ul {
  display: none;
}
button.opened + ul, a[type=button].opened + ul {
  display: block;
}
.sticky-main-menu {
  position: fixed;
  top: 6rem;
  left: 0;
  right: 0;
  z-index: 9999;
}

.stickymenuwrapper {
  background-color: #fff;
  transition-duration: 0s;
  padding: 1rem;
  margin: 0 auto;
  z-index: 995;
  border: 1px solid gray;
}

.hmenu ul {
  list-style: none;
  margin-bottom: 0;
  white-space: nowrap;
  padding: 0;
}

.hmenu ul.navbar {
  background-color: #f7a1d6;
  padding: 0 1rem;
  justify-content: center;
  display: flex;
  /* required for VSC */
  /* text-align: center;*/
}

.hmenu .navbar {
  line-height: 1.5;
}

.hmenu li ul {
  position: absolute;
  top: 100%;
  background-color: pink;
  padding: 0 12px 12px;
}

.hmenu ul[class^="level"] {
  background-color: white;
  position: relative;
  top: 100%;
  left: 0;
  padding: 1rem 0 1rem 1rem;
  margin-top: 0.5rem;
}

.hmenu ul.level-1 {
  position: absolute;
  /* ? */
  top: 100%;
  left: 0;
  margin-top: 0;
  padding: 1rem;
  box-shadow: 0 2px 5px #acaaaa;
}

.hmenu ul.level-2,
.hmenu ul.level-4 {
  background-color: #dedede;
}

.hmenu ul li:last-child {
  padding-bottom: 0;
}

.hmenu li.active>a {
  text-decoration: underline;
}

.hmenu a.separator:hover {
  text-decoration: none;
}

.hmenu ul[class^="level"] li {
  margin-right: 1rem;
}

.hmenu li {
  position: relative;
  /*  display: flex;*/
  margin: 0;
  padding: 0 0 0.5rem;
  line-height: 1.5rem;
}

.hmenu li a {
  color: #0d6efd;
}

.hmenu li.parent {
  margin-right: 0;
  /* was 2rem */
}

.hmenu .navbar>li {
  padding: 0;
  margin: 0 0.5rem;
  display: flex;
}

.hmenu .navbar>li.parent {
  margin-right: 2rem;
}

.hmenu .navbar>li.divider.parent {
  margin-right: 0;
}

.hmenu .navbar>li>a {
  padding: 0.5rem 0 0.5rem 0.5rem;
  margin: 0 0 0 0;
}

.hmenu .navbar>li:first-child>a {
  padding: 0.5rem 0.5rem 0.5rem 0;
  margin-left: 0;
}

.hmenu li.heading>a,
.hmenu li.divider>a,
.hmenu li.separator>a {
  text-decoration: none;
  padding-right: 2rem;
}

.hmenu li.separator-line {
  position: relative;
  height: 1rem;
  display: inline-block;
  width: 100%;
}

.hmenu li.separator-line span {
  display: none;
}

.hmenu li.separator-line:after {
  position: absolute;
  height: 0;
  top: 60%;
  width: 100%;
  border-top: 1px solid#8c8a8a;
  border-bottom: 1px solid lightgray;
  margin: 0 0;
  content: "";
}

.hmenu ul.navbar>li a {
  padding-top: 0.5rem;
  position: relative;
}

[type="button"] {
  -webkit-appearance: button;
}

[type="button"]:not(:disabled) {
  cursor: pointer;
}

.hmenu button,
.hmenu button:focus {
  display: inline-flex;
  position: absolute;
  background: transparent;
  border: none;
  color: #0d6efd;
  height: 1.5rem;
  outline: none;
  padding: 0 0 0 .5rem;
  margin: 0;
  right: 0;
  width: 40px;
  top: 6px;
}

.hmenu li.linked button {
  position: relative;
  top: 0;
}

.hmenu li.linked button.btn-level-1 {
  top: 10px;
}

.hmenu .navbar>li.linked.top-level {
  margin-right: 0.5rem;
}

.hmenu button.btn-level-1 {
  right: 0;
  position: absolute;
  top: 0;
}

.hmenu li button[class^="btn-level"]:after,
.hmenu li a[class^="btn-level"]:after {
  display: inline-flex;
  width: 0;
  height: 0;
  margin: 0 .5em;
  content: "";
  transition: all .3s ease-out;
  position: absolute;
  top: 12px;
  cursor: pointer;
  right: -2rem;
  border-top: 10px solid;
  border-right: 8px solid transparent;
  border-left: 8px solid transparent;
}

.hmenu li.separator>button[class^="btn-level"]:after,
.hmenu li.separator>a[class^="btn-level"]:after {
  right: 0;
}

.hmenu li button[class^="btn-level"].opened,
.hmenu li a[class^="btn-level"].opened {
  color: red;
}

.hmenu li button[class^="btn-level"].opened:after,
.hmenu li a[class^="btn-level"].opened:after {
  border-top: none;
  border-bottom: 10px solid;
  border-right: 8px solid transparent;
  border-left: 8px solid transparent;
  top: 12px;
  right: 0;
}

.hmenu li.linked>a[class^="btn-level"].opened:after {
  right: -2rem;
}

.hmenu li button.linked {
  display: inline-flex;
  position: relative;
}

.hmenu li button.linked:after {
  top: 10px;
  right: 0;
}

.hmenu li button[class^="btn-level"].linked.opened:after {
  top: 6px;
  right: 0;
}

.hmenu li button.btn-level-1.linked {
  display: inline-flex;
  position: relative;
}

.hmenu li button.btn-level-1:after,
.hmenu li button.btn-level-1.opened:after {
  top: 30%;
  right: 0;
}

.hmenu li a.btn-level-1:after,
.hmenu li a.btn-level-1.opened:after {
  top: 40%;
  right: -2rem;
}
<div id="stickymenuwrapper" class="stickymenuwrapper">
  <p>Click a triangle to open the sub-menu. Click it again to close the sub-menu or click the other triangle to open its menu and close the first. Clicking a triangle on the next level down will leave the top level open, but its siblings work in the same
    way. Clicking outside the menu but inside this box will also close the menu. The CSS for this layout is not finished and is not intended for narrow screens. I'm aware that the second triangle will overflow the pink background on a very narrow view.
    Please ignore that. </p>
  <div class="hmenu">
    <ul id="menucontent" class="navbar">
      <li class="item-103 deeper parent linked">
        <a href="/">ITEM 1</a>
        <button class="btn-level-1 linked" type="button" name="btn-103"></button>
        <ul class="level-1 not-separator">
          <li class="item-104">
            <a href="/">About us</a>
          </li>
          <li class="item-107 divider deeper parent separator">
            <a class="btn-level-2" type="button">Level 2 separator</a>
            <ul class="level-2 separator">
              <li class="item-108">
                <a href="/">Lower article</a>
              </li>
            </ul>
          </li>
          <li class="item-141 deeper parent linked">
            <a class="btn-level-2 heading" type="button">Menu heading</a>
            <ul class="level-2 menu-heading">
              <li class="item-142">
                <a href="/">Article under menu heading</a>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li class="item-122 divider deeper parent">
        <a class="btn-level-1" type="button">ITEM TWO</a>
        <ul class="level-1 separator">
          <li class="item-121">
            <a href="/">Test level 2</a>
          </li>
          <li class="item-134 deeper parent linked">
            <a class="btn-level-2 heading" type="button">Test Two menu heading</a>
            <ul class="level-2 menu-heading">
              <li class="item-135">
                <a href="/">Article under menu heading</a>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
    <p>Duis ac lorem. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse potenti. Sed tincidunt varius arcu.</p>
  </div>
</div>

\$\endgroup\$
5
  • \$\begingroup\$ I've removed the 'not-open' class (see new snippet) but I'm not sure about your comment regarding ==, === and != because only those are used (except where only = is allowed, e.g. let i = 0) \$\endgroup\$
    – Gillian
    Commented Nov 20, 2020 at 16:48
  • \$\begingroup\$ You're using b1 != button1 and b2 != button2 - better to use strict equality. \$\endgroup\$ Commented Nov 20, 2020 at 17:38
  • \$\begingroup\$ Sorry, I missed the !== instead of !=. The DOMContentLoaded is for Joomla. It won't work otherwise. I'll remove it from this snippet. \$\endgroup\$
    – Gillian
    Commented Nov 20, 2020 at 18:20
  • \$\begingroup\$ Thank you so much. From potentially dozens of lines of code to just 16. Wonderful. I'm sorry I broke so many rules. I will carry on with my JavaScript beginner's course and next time I post here I hope it will be ok. \$\endgroup\$
    – Gillian
    Commented Nov 21, 2020 at 11:24
  • \$\begingroup\$ In my development site I have removed the "id=menucontent" from the top <ul> and have added a class menucontent instead. I then amended your (!e.target.closest('#menucontent') to (!e.target.closest('.menucontent') to avoid potential duplicate IDs on the same page, which could happen if the menu is displayed twice, e.g. top and bottom. \$\endgroup\$
    – Gillian
    Commented Nov 21, 2020 at 16:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.