Search code examples
javascripthtmlcssloopstouch-event

Javascript menu dropdown on touchevent (tap)


I'm trying to create responsive version of my website, for the mobile version i'm trying to add an event wherein the user taps on a menu item, the list drops down, and when they tap on it again, it closes back up. I'm using the vanilla javascript touchstart and touchend events, which i'm starting to think is not the best way to perform this action, as as soon as i lift my finger off the menu item the menu collapses.

Please guide me on the best way to achieve this!

HTML

<ul class="mainnav">
  <li class="productstouch">
    <a href="#"><p>Products</p></a>

    <ul class="navlvl2">
      <li class="item">
        <a href="#"><p>Sensors</p></a>

        <ul class="navlvl3">
          <li>
            <a href="#"><p>Proximity Sesnors</p></a>
          </li>
          <li>
            <a href="#"><p>Magnetic Sensors</p></a>
          </li>
          <li>
            <a href="#"><p>Photoelectric Sensors</p></a>
          </li>
        </ul>
      </li>
      <li class="item">
        <a href="#"><p>Controllers</p></a>
      </li>
      <li class="item">
        <a href="#"><p>Motion Devices</p></a>
      </li>
    </ul>
  </li>
  <li>
    <a href="#"><p>Home</p></a>
  </li>
  <li>
    <a href="#"><p>Manufacturers</p></a>
  </li>
</ul>

JS

function watchForHover() {
  let lastTouchTime = 0

  let it = document.getElementsByClassName("mainnav")
  let her = document.getElementsByClassName("navlvl2")
  let him = document.getElementsByClassName("navlvl3")

  function enableHover() {
    if (new Date() - lastTouchTime < 500) return

    for (let i = 0; i < it.length; i++) {
      it[i].classList.add("hasHover")
    }

    for (let c = 0; c < her.length; c++) {
      her[c].classList.add("hasHover")
    }

    for (let d = 0; d < him.length; d++) {
      him[d].classList.add("hasHover")
    }
  }

  function disableHover() {
    for (var i = 0; i < it.length; i++) {
      it[i].classList.remove("hasHover")
    }
    for (let c = 0; c < her.length; c++) {
      her[c].classList.remove("hasHover")
    }

    for (let d = 0; d < him.length; d++) {
      him[d].classList.remove("hasHover")
    }
  }

  function updateLastTouchTime() {
    lastTouchTime = new Date()
  }

  document.addEventListener("touchstart", updateLastTouchTime, true)
  document.addEventListener("touchstart", disableHover, true)
  document.addEventListener("mousemove", enableHover, true)

  enableHover()
}

watchForHover()

//--------------------End of Watch for Hover------------------------------//

function registerTouch() {
  let mobileLevelZeroItems = document.getElementsByClassName("mainnav")

  for (let i = 0; i < mobileLevelZeroItems.length; i++) {
    let Zerochild = mobileLevelZeroItems[i]

    //mobileLevelZeroItems.onclick = mobileLevelZeroItems.classList.add('onTouch')  ;

    Zerochild.addEventListener("touchstart", function () {
      mobileLevelZeroItems[i].classList.add("onTouch")
    })

    Zerochild.addEventListener("touchend", function () {
      mobileLevelZeroItems[i].classList.remove("onTouch")
    })
  }
}

registerTouch()

CSS

.mainnav {
  display: block;
  list-style: none;
  text-align: center;
  width: 100vw;
  margin: 0;
  padding: 0;
}

.mainnav > li {
  background-color: #1a6baa;
}

.mainnav > li > a {
  text-decoration: none;
  color: white;
}

.navlvl3 {
  display: none;
}

.mainnav > li > ul {
  display: none;
}

.mainnav.onTouch > li > ul {
  display: grid;
}

/*----------------------Beginning of .hasHover classes-------------------------------*/

.mainnav.hasHover {
  display: flex;
  list-style: none;
  background-color: lightblue;
  margin: 0;
  padding: 0;
  position: relative;
}

.mainnav.hasHover > li {
  background-color: lightblue;
  padding: 10px 30px 10px 30px;
  margin: 0;
}

.mainnav.hasHover > li > a > p:hover {
  color: green;
}

.mainnav.hasHover > li > a {
  text-decoration: none;
  margin: 0;
  padding: 0;
  color: black;
}

.mainnav.hasHover > li:hover .navlvl2.hasHover {
  visibility: visible;
  display: block;
  top: 100%;
  left: 0;
  margin: 0;
  padding: 0;
}

.mainnav.hasHover > li > a > p {
  margin: 0;
  font-size: 20px;
}

.navlvl2.hasHover {
  visibility: hidden;
  display: flex;
  position: absolute;
  background-color: lightgray;
  margin: 0;
}

.navlvl2.hasHover > li {
  margin: 0;
  padding: 15px;
  list-style: none;
}

.mainnav.hasHover > li:hover {
  background-color: pink;
}

.navlvl2.hasHover > li > a > p {
  margin: 0;
  padding: 0;
}

.navlvl2.hasHover > li:hover {
  background-color: lightgreen;
  color: white;
}

.navlvl3.hasHover {
  display: none;
  position: absolute;
  background-color: teal;
}

.navlvl3.hasHover > li {
  list-style: none;
}

.navlvl2.hasHover > li:hover .navlvl3.hasHover {
  display: block;
  top: 0;
  left: 100%;
}

Solution

  • I think you are overcomplicating things. You should use 'touch' events for things like 'swipe', 'drag', etc. on touch screen devices. Simple things like 'tap' are already handled for you. For instance, you could use hide the dropdown menu in CSS

    .dropdown {
       display: none;
    }
    

    and when the user taps on the menu item simple show this dropdown

    menuItem.addEventListener("click", function(){
       dropdown.style.display = "block";
    })
    

    Obviously, this could be further improved by adding 'transitions' and more styling. But this is the general idea. Events like 'click', 'onmousedown', etc. work on touch screen devices.