12

I have an element:

    <span ng-mouseenter="showIt()" ng-mouseleave="hideIt()">Hover Me</span>
    <div class="outerDiv" ng-show="hovering">
        <p>Some content</p>
        <div class="innerDiv">
            <p>More Content</p>
        </div>
    </div>

Here is the JS:

// mouseenter event
$scope.showIt = function () {
    $scope.hovering = true;
};

// mouseleave event
$scope.hideIt = function () {
    $scope.hovering = false;
};

And I want to be able to set a 500ms delay on the hover event.

I already had an answer to this question, but I can't post it for another 8 hours. I'll be back!

7 Answers 7

16

Like what most have mentioned on here already, I added a timer to the mouseenter event.

// create the timer variable
var timer;

// mouseenter event
$scope.showIt = function () {
    timer = $timeout(function () {
        $scope.hovering = true;
    }, 500);
};

The problem I had was that if I was scrolling past the item and the mouse cursor hit it, the popup would still occur half a second later. I want to be able to scroll past an item without the popup happening by accident.

Putting the timeout in a variable allowed me to cancel the timeout. Which I do on a mouse leave event to ensure users don't accidentally triggering the popup.

// mouseleave event
$scope.hideIt = function () {
    $timeout.cancel(timer);
    $scope.hovering = false;
};

Here is a fiddle in case anyone wants to see it in action: jsfiddle

Sign up to request clarification or add additional context in comments.

Comments

9

I recommend using CSS transitions and angular-animate:

JS

var app = angular.module('app', ['ngAnimate']);

CSS

.outerDiv.ng-hide-remove {
    -webkit-transition: 0.5s linear all; 
    transition: 0.5s linear all;
    transition-delay: 0.5s;
    opacity: 0;
}
.outerDiv.ng-hide-remove.ng-hide-remove-active {
    opacity: 1;
}

HTML

<span ng-mouseenter="hovering=true" ng-mouseleave="hovering=false">Hover Me</span>
<div class="outerDiv" ng-show="hovering">
    <p>Some content</p>
    <div class="innerDiv">
        <p>More Content</p>
    </div>
</div>

Demo Plunker

Comments

3

Use $timeout:

$scope.showIt = function () {
    $timeout(function(){
        $scope.hovering = true;
    }, 500);
};

Don't forget to add it as a dependency.

And if you wish to play with it some more, you can make your own directive like delayedMouseEnter that would include the delay and use it instead.

Comments

3

window.setTimeout Calls a function or executes a code snippet after a specified delay.

$scope.hideIt = function () {
    window.setTimeout(function() {
        $scope.hovering = false;
        $scope.$apply();
    }, 500);  // 500ms delay        
};

Or the Angular $timeout service:

$scope.hideIt = function () {
    $timeout(function() {
        $scope.hovering = false;
    }, 500);  // 500ms delay        
};

2 Comments

The $timeout service is recommended over setTimeout as it allow testing and also call $scope.$apply() automatically which is missing in this code.
For he answer that I can't post yet I use $timeout and then cancel it on mouseleave. link
3

I wrote a simple directive for this.

(function () {
    'use strict';
    angular
        .module('app')
        .directive('scHover', scHoverDirective);

    function scHoverDirective($timeout) {
        return {
            link: function(scope, element, attrs, modelCtrl) {
                    var inTimeout = false;
                    var hoverDelay = parseInt(attrs.scHoverDelay, 10) | 1000;

                    element.on('mouseover', function () {
                      inTimeout = true;
                      $timeout(function () {
                        if (inTimeout) {
                          scope.$eval(attrs.scHover);
                          inTimeout = false;
                        }
                      }, hoverDelay);
                    });

                    element.on('mouseleave', function () {
                      inTimeout = false;
                      scope.$apply(function () {
                        scope.$eval(attrs.scHoverEnd);
                      });
                    });
            }
        }
    }
})();

Example usage (sc-hover-delay is optional):

<div sc-hover='vm.title="Hovered..."' sc-hover-end='vm.title=""' sc-hover-delay="800">Hover me!  {{ vm.title }}</div>

Here is a plunker: http://plnkr.co/edit/iuv604Mk0ii8yklpp6yR?p=preview

Comments

3

Thank you for asking this question, as this example helped me understand how $timeout works much better than AngularJS's documentation did. However, I did improve the operation slightly on the correct answer and wanted to share it here.

You never have to create an empty var called timer. In fact, doing so is using up memory that you didn't have to. You have a variable and two functions to handle what is actually a single operation.

So, what I did was create a single function called 'toggleHover' that accepts a Boolean argument named 'bool'. Then an if/else statement determines which $timeout function you need to run.

AngularJS In Controller

$scope.hovering = false; //Sets the initial state of hover

$scope.toggleHover = function (bool) {
    if (bool === true) {
        $timeout(function () {
            $scope.hovering = !$scope.hovering;
        }, 500);
    } else {
        $timeout(function() {
            $scope.hovering = !$scope.hovering;
        }, 500);
    };
}

HTML/VIEW

<span ng-mouseenter="toggleHover(true)" ng-mouseleave="toggleHover(false)">Hover Me</span>

EXAMPLE

http://jsfiddle.net/89RTg/12/

2 Comments

sorry if I dont get this .. looking at your code, you wouldn't even need the boolean, unless you'd want to change the timing on enter vs leave. but if you do that, and leave is quicker than enter, you need to clear the timeout for enter (or it may fire after you've left). that's why you'd need the variable.
why do you need if else ? Both are doing same right ?
2

Hi there Great answer above Just wanted to add don't forget to cancel your timer if needed when you hover out and it still didnt fire or when you destroy the directive

 $timeout.cancel( timer );
 $scope.$on("$destroy",
                    function( event ) {

                        $timeout.cancel( timer );

                    }
                );

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.