[AngularJS] Incorrect ng-mouseenter ng-mouseleave and Solution
Old version AngularJS (such as 1.1.3) has incorrect implementation of mouseenter and mouseleave directive:
Demo of incorrect implementation
See also this pull request (fix(jqLite): mouseenter/-leave should not trigger on child elements) on AngularJS Github repo for more details.
The following is correct implementation of mouseenter and mouseleave directive:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 'use strict';
/* Directives */
angular.module('pali.mouseEnterLeave', []).
directive('mouseenter', [function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
/**
* check if element is targer or the parent of target
* @param {DOM Element} target
* @param {DOM Element} element
* @return {boolean} Return true if element is target or the parent of target
* else return false.
*/
function checkParent(target, element) {
if (angular.isUndefined(target)) return false;
// Chrome and Firefox use parentNode, while Opera use offsetParent
while(target.parentNode) {
if( target == element ) return true;
target = target.parentNode;
}
while(target.offsetParent) {
if( target == element ) return true;
target = target.offsetParent;
}
return false;
};
elm.bind('mouseover', function(e) {
var evt = e || window.event;
var targetElement = evt.target || evt.srcElement;
// check if mouse moves inside the element, if yes, return.
var relTarg = evt.relatedTarget || evt.fromElement;
if (checkParent(relTarg, elm[0])) return;
// https://gist.github.com/siongui/a8d9a9003772315e2cba
if (scope.$root.$$phase)
scope.$eval(attrs['mouseenter']);
else
scope.$apply(attrs['mouseenter']);
});
}
};
}]).
directive('mouseleave', [function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
/**
* check if element is targer or the parent of target
* @param {DOM Element} target
* @param {DOM Element} element
* @return {boolean} Return true if element is target or the parent of target
* else return false.
*/
function checkParent(target, element) {
if (angular.isUndefined(target)) return false;
// Chrome and Firefox use parentNode, while Opera use offsetParent
while(target.parentNode) {
if( target == element ) return true;
target = target.parentNode;
}
while(target.offsetParent) {
if( target == element ) return true;
target = target.offsetParent;
}
return false;
};
elm.bind('mouseout', function(e) {
var evt = e || window.event;
var targetElement = evt.target || evt.srcElement;
// check if mouse moves inside the element, if yes, return.
var relTarg = evt.relatedTarget || evt.toElement;
if (checkParent(relTarg, elm[0])) return;
// https://gist.github.com/siongui/a8d9a9003772315e2cba
if (scope.$root.$$phase)
scope.$eval(attrs['mouseleave']);
else
scope.$apply(attrs['mouseleave']);
});
}
};
}]);
|
The usage is the same as ngMouseenter and ngMouseleave, except the name changed to mouseenter and mouseleave. Also do not forget to include this module in your AngularJS application.