Sane Hover

Say you want to hover a header and it will change its behavior (lets say background color changes), but then you hover again to the same header, it should open to a certain amount of time hovering to “reconnect to the hovering div.” Another way of thinking is “to prevent the changing of the div if you’re still using it.”

Try hovering the example headers below:

Header
Main
Footer
Header
Main
Footer

You will notice at the left, the header quickly changes its color every hover, whereas the right only changes after keeping it hovered for a while. And if you keep switching hover on both headers, the right is pleasant to look at, because it does not blinks quickly.

The main idea to solve this problem is to add a setTimeout for every mouseover or mouseleave and reset its state (clearTimeout) whenever the events are called again. In that way even if the setTimeout is yet to be executed, it has to allow the new event triggered to be executed instead. The same rules applied for setInterval.

// on windows ready
window.addEventListener("DOMContentLoaded", function () {
  let interval_id = 0
  let timeout_id = 0
  let hovering = 0
  let mouseover_on = 20
  let mouseout_on = 200
  let interval = 1

  document.querySelector("header")?.addEventListener("mouseover", function (e) {
    hovering = 0
    clearInterval(interval_id)
    clearTimeout(timeout_id)
    interval_id = setInterval(change_div(this), interval)
    change_div(this)()
  })
  document.querySelector("header")?.addEventListener("mouseout", function (e) {
    hovering = 0
    clearInterval(interval_id)
    clearTimeout(timeout_id)
    timeout_id = setTimeout(() => {
      this.classList.remove("bg-yellow-500")
    }, mouseout_on)
  })

  const change_div = (_this: HTMLElement) => () => {
    hovering++
    if (hovering > mouseover_on)
      _this.classList.add("bg-yellow-500")
  }

})

The interval_id and timeout_id is used to clear the previous and create new setInterval and setTimeout respectively. The hovering increments every setInterval iteration. The mouseover_on is used as a limit until the UI update triggers. The mouseout_on is the timeout duration. And the interval is for the milliseconds delay before the next interval.

<header class="p-4 cursor-pointer">Header</header>
<main class="flex-1 p-4">Main</main>
<footer class="p-4">Footer</footer>
.yellow {
  background-color: rgb(234 179 8);
}

If you still need this in a jquery:

// on windows ready
$(()=> {
  let interval_id = 0
  let timeout_id = 0
  let hovering = 0
  let mouseover_on = 20
  let mouseout_on = 200
  let interval = 1

  $("header").mouseover(function (e) {
    hovering = 0
    clearInterval(interval_id)
    clearTimeout(timeout_id)
    interval_id = setInterval(change_div(this), interval)
    change_div(this)()
  })
  $("header").mouseout(function (e) {
    hovering = 0
    clearInterval(interval_id)
    clearTimeout(timeout_id)
    setTimeout(() => {
      this.classList.remove("yellow")
    }, mouseout_on)
  })
  
  // change_div will perform the color change
  function change_div(_this: HTMLElement) {
    return () => {
      hovering++
      if (hovering > mouseover_on) _this.classList.add("yellow")
    }
  }
})