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!

Tj Gienger
  • 1,395
  • 1
  • 12
  • 17

7 Answers7

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

Tj Gienger
  • 1,395
  • 1
  • 12
  • 17
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

Michael Kang
  • 52,003
  • 16
  • 103
  • 135
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        
};
Rahil Wazir
  • 10,007
  • 11
  • 42
  • 64
  • 1
    The `$timeout` service is recommended over `setTimeout` as it allow testing and also call `$scope.$apply()` automatically which is missing in this code. – runTarm Jul 19 '14 at 01:15
  • 1
    For he answer that I can't post yet I use $timeout and then cancel it on mouseleave. [link](http://jsfiddle.net/tgienger/89RTg/) – Tj Gienger Jul 19 '14 at 01:16
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.

Shomz
  • 37,421
  • 4
  • 57
  • 85
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

Lars335
  • 1,730
  • 2
  • 22
  • 35
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/

Dmidify
  • 378
  • 2
  • 7
  • 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. – commonpike Dec 12 '16 at 14:52
  • 2
    why do you need if else ? Both are doing same right ? – Viswanath Lekshmanan Sep 15 '17 at 08:31
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 );

                    }
                );
yonia
  • 1,681
  • 1
  • 14
  • 12