Austin Weiss
Austin Weiss

Reputation: 45

Javascript: "Mouseover" and "mouseout" event handlers work, but "mouseout" doesn't work properly when "mouseover" is replaced by "click"

I am making a dropdown menu on a webpage using JavaScript(not jQuery).
My research has shown only similar issues with images and colors, but the solutions to those problems do not fix my dropdown menu.

When "mouseover" is used to open my dropdown menu in an event handler, the "mouseout" event handler allows me to scroll over my links in the list just fine before the dropdown menu is closed when the "mouseout" event occurs. When "mouseover" is replaced by "click" in the event handler, "mouseout" fires as it leaves the text content of the dropdown button, which does not allow me to move my cursor to items in the dropdown menu.

The links float as inline-block elements to the left of the top of the screen. They are fixed and do not overflow when the screen is resized. The dropdown menu has a dropBtn that is visible while the rest of the dropItem list-items are display: none; using CSS. The JavaScript targets the list-items that are display: none; when the dropBtn fires an event. The list items then gain display: list-item;. When the cursor leaves the navigation it is supposed to give the list-items display: none; again, but display: none; happens too soon when the "click" event is paired with the "mouseout" event.

Does anyone have a way to write this to allow me to click the dropBtn to open the drop menu, allowing me to move my cursor across the links before closing the drop menu with a mouseout event?

let dropMenu = document.querySelector("#dropMenu");
let navList = document.querySelectorAll("#dropMenu li");
let navDrop = document.querySelector("#navDrop");
let listContainer = document.querySelector("#listContainer");
let navItemNumber = navList.length;


function toggleNavOpen() {


  for (let i = 1; i < navItemNumber; i++) {
    navList[i].style.display = "list-item";
  }
  for (let i = 1; i < navItemNumber; i++) {
    navList[i].style.width = "110px";
  }
}


dropMenu.addEventListener("mouseover", toggleNavOpen);
//replace mouseover with click in the previous line, and the 
//code doesn't work properly.


function toggleNavClose() {

  for (let i = 1; i < navItemNumber; i++) {

    navList[i].style.display = "none";

  }
}

dropMenu.addEventListener("mouseout", toggleNavClose);
.content {
  clear: both;
}

#logo {
  float: left;
  margin: 20px 20px 5px 20px;
  padding: 31px 37px;
  background-image: url("../images/logo.png");
  background-repeat: no-repeat;
}

.row {
  float: left;
  overflow: unset;
  white-space: nowrap;
  background-color: #d8e9ee;
  width: 100%;
}

.top {
  position: fixed;
}

.menu {
  display: inline-block;
  list-style-type: none;
  margin: 25px 8px 0px 8px;
  padding: 12px 14px;
  border-style: groove;
  border-width: 2px;
  box-shadow: 2px 2px 2px;
  font-size: 1.4em;
  color: rgb(11, 12, 36);
  background-color: #f5f5ed;
  border-radius: 1em;
}

.menu:hover {
  margin: 24px 8px 0px 6px;
  border-style: ridge;
  box-shadow: 3px 3px 3px;
  font-size: 1.45em;
  border-radius: .9em;
  /*transition-duration: 250ms;*/
}

#navDrop {
  position: fixed;
  display: inline-block;
}

#dropMenu {
  font-size: 1.4em;
  border-style: groove;
  border-width: 2px;
  box-shadow: 2px 2px 2px;
  color: rgb(11, 12, 36);
  background-color: #f5f5ed;
  border-radius: 1em;
  margin: 25px 8px 0 8px;
}

#dropMenu:hover {
  font-size: 1.45em;
  border-style: ridge;
  box-shadow: 3px 3px 3px;
  margin: 24px 8px 0px 7px;
  border-radius: .9em;
}

.dropBtn {
  padding: 12px 14px;
}

.dropItem {
  display: none;
  padding: 12px 14px;
}

nav ul li a {
  text-decoration: none;
}
<ul>
  <li class="menu"><a href="index.html">Home</a></li>
  <li class="menu"><a href="content/tuning.html#tuning">Tuning</a></li>
  <li class="menu"><a href="content/videos.html">Lessons</a></li>
  <li class="menu"><a href="content/tools.html">Tools</a></li>
  <li class="menu"><a href="content/signup.html">Sign Up</a></li>
  <li class="menu"><a href="content/signin.html">Sign in</a></li>
  <li id="navDrop">
    <ul id="dropMenu">
      <li class="dropBtn"><a href="#">More</a></li>
      <li class="dropItem"><a href="content/tuning.html#repairs">Repairs</a></li>
      <li class="dropItem"><a href="content/supplies.html">Supplies</a></li>
      <li class="dropItem"><a href="content/pricing.html">Pricing</a></li>
      <li class="dropItem"><a href="content/services.html">Services</a></li>
      <li class="dropItem"><a href="content/about.html">About</a></li>
      <li class="dropItem"><a href="content/contact.html">Contact</a></li>
    </ul>
  </li>
</ul>

Upvotes: 0

Views: 475

Answers (1)

mplungjan
mplungjan

Reputation: 177885

How about this - note I added a class called open

let dropMenu = document.querySelector("#dropMenu");
let navList = document.querySelectorAll("#dropMenu li");
let navDrop = document.querySelector("#navDrop");
let listContainer = document.querySelector("#listContainer");
let navItemNumber = navList.length;


function toggleNav(e) { // called on click AND mouseleave
  const tgt = e.target; // clicked or mouseleave'd object
  const li = document.querySelector(".dropBtn"); 
  if (tgt.id && tgt.id === "dropMenu" && e.type === "mouseleave") {
    li.classList.add("open"); // pretend the li class is open
  }
  const open = li.classList.contains("open"); // is it open?
  [...navList].forEach(nav => nav.classList.toggle("open", !open)); // toggle the class

  if (tgt.tagName === "A" && tgt.closest("li").classList.contains("dropBtn")) {
    li.classList.toggle("open"); // toggle if clicked
  }
}
dropMenu.addEventListener("click", toggleNav);
dropMenu.addEventListener("mouseleave", toggleNav);
.content {
  clear: both;
}

#logo {
  float: left;
  margin: 20px 20px 5px 20px;
  padding: 31px 37px;
  background-image: url("../images/logo.png");
  background-repeat: no-repeat;
}

.row {
  float: left;
  overflow: unset;
  white-space: nowrap;
  background-color: #d8e9ee;
  width: 100%;
}

.top {
  position: fixed;
}

.menu {
  display: inline-block;
  list-style-type: none;
  margin: 25px 8px 0px 8px;
  padding: 12px 14px;
  border-style: groove;
  border-width: 2px;
  box-shadow: 2px 2px 2px;
  font-size: 1.4em;
  color: rgb(11, 12, 36);
  background-color: #f5f5ed;
  border-radius: 1em;
}

.menu:hover {
  margin: 24px 8px 0px 6px;
  border-style: ridge;
  box-shadow: 3px 3px 3px;
  font-size: 1.45em;
  border-radius: .9em;
  /*transition-duration: 250ms;*/
}

#navDrop {
  position: fixed;
  display: inline-block;
}

#dropMenu {
  font-size: 1.4em;
  border-style: groove;
  border-width: 2px;
  box-shadow: 2px 2px 2px;
  color: rgb(11, 12, 36);
  background-color: #f5f5ed;
  border-radius: 1em;
  margin: 25px 8px 0 8px;
}

#dropMenu:hover {
  font-size: 1.45em;
  border-style: ridge;
  box-shadow: 3px 3px 3px;
  margin: 24px 8px 0px 7px;
  border-radius: .9em;
}

.dropBtn {
  padding: 12px 14px;
}

.dropItem {
  display: none;
  padding: 12px 14px;
}

nav ul li a {
  text-decoration: none;
}

.open {
  display: list-item;
  width: 110px;
}
<ul>
  <li class="menu"><a href="index.html">Home</a></li>
  <li class="menu"><a href="content/tuning.html#tuning">Tuning</a></li>
  <li class="menu"><a href="content/videos.html">Lessons</a></li>
  <li class="menu"><a href="content/tools.html">Tools</a></li>
  <li class="menu"><a href="content/signup.html">Sign Up</a></li>
  <li class="menu"><a href="content/signin.html">Sign in</a></li>
  <li id="navDrop">
    <ul id="dropMenu">
      <li class="dropBtn"><a href="#">More</a></li>
      <li class="dropItem"><a href="content/tuning.html#repairs">Repairs</a></li>
      <li class="dropItem"><a href="content/supplies.html">Supplies</a></li>
      <li class="dropItem"><a href="content/pricing.html">Pricing</a></li>
      <li class="dropItem"><a href="content/services.html">Services</a></li>
      <li class="dropItem"><a href="content/about.html">About</a></li>
      <li class="dropItem"><a href="content/contact.html">Contact</a></li>
    </ul>
  </li>
</ul>

Upvotes: 1

Related Questions