608.620.5104 | info@tenforward.consulting

How to add a delayed hover affect with JavaScript

Published May 24, 2022

The Challenge

A client's website had a page with a gallery of partner logos; visitors could click each logo to trigger a modal showing details about the partnership.

They asked us to change the interaction so that visitors could hover over logos to see the modal, instead of having to click them. Seemed easy enough - but given the design, we had issues where closing the modal immediately re-opened it for the partner underneath the "close" button, resulting in a loop that was hard to break. 

Our Star Trek-ified example of what was happening:


The Solution

Here's our first stab, which resulted in the loop from above:


  initListeners: =>
	$(".js-character-image").on "mouseover", @onCharacterHover
    $(".js-close-details").on "click", @onCloseDetailsClick

  onCharacterHover: (ev) =>
    $(".js-character-details").hide()

    $character = $(ev.target).closest(".js-character-image")
    position = $character.position().top
    $details = $character.next(".js-character-details")

    $details.css("top", position)
    $details.slideDown()

  onCloseDetailsClick: (ev) ->
    $descriptionContainer = $(ev.target).closest(".js-character-details")
    $descriptionContainer.slideUp()


To fix the instantaneous loop issue, we added a timeout, so that the code wouldn't immediately execute:


  initListeners: =>
	$(".js-character-image").on "mouseover", @onCharacterHoverTimeout
    $(".js-close-details").on "click", @onCloseDetailsClick

  onCharacterHoverTimeout: (ev) =>
    t = undefined
    window.clearTimeout(t)

    t = window.setTimeout(( ->
      $(".js-character-details").hide()

      $character = $(ev.target).closest(".js-character-image")
      position = $character.position().top
      $details = $character.next(".js-character-details")

      $details.css("top", position)
      $details.slideDown()
    ), 500)

    $(ev.target).mouseleave ->
      window.clearTimeout(t)

  onCloseDetailsClick: (ev) ->
    $descriptionContainer = $(ev.target).closest(".js-character-details")
    $descriptionContainer.slideUp()


Now, when a visitor mouses over a character image, our code doesn't execute until after 500 milliseconds; this gives them time to move their mouse before the hover effect fires.

We also added the mouseleave code to ensure that if the user stopped hovering over a specific character image within 500 milliseconds, the timer would reset.


The Result


Author details

Hilary Stohs-Krause

Co-Owner and Senior Software Developer
@hilarysk