From 7556beda486f26b40fb860448316e8a32457e9e9 Mon Sep 17 00:00:00 2001 From: Wesley Cho Date: Tue, 1 Sep 2015 07:00:17 -0700 Subject: [PATCH] fix(tooltip): switch to use raw DOM event bindings - Switch to use `addEventListener` and `removeEventListener` to prevent jqLite/jQuery bug where the events are swallowed on disabled elements Closes #4322 Fixes #4060 --- src/tooltip/test/tooltip-template.spec.js | 14 +- src/tooltip/test/tooltip.spec.js | 202 +++++++++++++--------- src/tooltip/test/tooltip2.spec.js | 34 ++-- src/tooltip/tooltip.js | 7 +- 4 files changed, 154 insertions(+), 103 deletions(-) diff --git a/src/tooltip/test/tooltip-template.spec.js b/src/tooltip/test/tooltip-template.spec.js index 6071baa17c..96561c9f1b 100644 --- a/src/tooltip/test/tooltip-template.spec.js +++ b/src/tooltip/test/tooltip-template.spec.js @@ -30,8 +30,14 @@ describe('tooltip template', function() { tooltipScope = elmScope.$$childTail; })); + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + it('should open on mouseenter', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect( tooltipScope.isOpen ).toBe( true ); expect( elmBody.children().length ).toBe( 2 ); @@ -41,7 +47,7 @@ describe('tooltip template', function() { scope.templateUrl = null; scope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); expect(elmBody.children().length).toBe(1); @@ -51,7 +57,7 @@ describe('tooltip template', function() { scope.myTemplateText = 'some text'; scope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1).text().trim()).toBe('some text'); @@ -63,7 +69,7 @@ describe('tooltip template', function() { })); it('should hide tooltip when template becomes empty', inject(function($timeout) { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); scope.templateUrl = ''; diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index 1ff150630f..73774d168c 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -24,6 +24,12 @@ describe('tooltip', function() { tooltipScope = elmScope.$$childTail; })); + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + it('should not be open initially', inject(function() { expect(tooltipScope.isOpen).toBe(false); @@ -33,7 +39,7 @@ describe('tooltip', function() { })); it('should open on mouseenter', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); // We can only test *that* the tooltip-popup element was created as the @@ -42,8 +48,8 @@ describe('tooltip', function() { })); it('should close on mouseleave', inject(function() { - elm.trigger('mouseenter'); - elm.trigger('mouseleave'); + trigger(elm, 'mouseenter'); + trigger(elm, 'mouseleave'); expect(tooltipScope.isOpen).toBe(false); })); @@ -52,7 +58,7 @@ describe('tooltip', function() { })); it('should have default placement of "top"', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.placement).toBe('top'); })); @@ -64,7 +70,7 @@ describe('tooltip', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.placement).toBe('bottom'); })); @@ -77,7 +83,7 @@ describe('tooltip', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.placement).toBe('bottom'); scope.place = 'right'; @@ -132,12 +138,12 @@ describe('tooltip', function() { var tt_1 = angular.element(elm.find('li > span')[0]); var tt_2 = angular.element(elm.find('li > span')[1]); - tt_1.trigger('mouseenter'); - tt_1.trigger('mouseleave'); + trigger(tt_1, 'mouseenter'); + trigger(tt_1, 'mouseleave'); $timeout.flush(); - tt_2.trigger('mouseenter'); + trigger(tt_2, 'mouseenter'); expect(tt_1.text()).toBe(scope.items[0].name); expect(tt_2.text()).toBe(scope.items[1].name); @@ -146,7 +152,7 @@ describe('tooltip', function() { expect(tooltipScope.content).toBe(scope.items[1].tooltip); expect(elm.find('.tooltip-inner').text()).toBe(scope.items[1].tooltip); - tt_2.trigger('mouseleave'); + trigger(tt_2, 'mouseleave'); })); it('should only have an isolate scope on the popup', inject(function($compile) { @@ -164,17 +170,17 @@ describe('tooltip', function() { elm = elmBody.find('span'); elmScope = elm.scope(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(elm.attr('alt')).toBe(scope.alt); ttScope = angular.element(elmBody.children()[1]).isolateScope(); expect(ttScope.placement).toBe('top'); expect(ttScope.content).toBe(scope.tooltipMsg); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); //Isolate scope contents should be the same after hiding and showing again (issue 1191) - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); ttScope = angular.element(elmBody.children()[1]).isolateScope(); expect(ttScope.placement).toBe('top'); @@ -192,7 +198,7 @@ describe('tooltip', function() { })); it( 'should close the tooltip when its trigger element is destroyed', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); elm.remove(); @@ -202,20 +208,20 @@ describe('tooltip', function() { it('issue 1191 - scope on the popup should always be child of correct element scope', function() { var ttScope; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); ttScope = angular.element( elmBody.children()[1] ).scope(); expect(ttScope.$parent).toBe( tooltipScope ); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); // After leaving and coming back, the scope's parent should be the same - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); ttScope = angular.element(elmBody.children()[1]).scope(); expect(ttScope.$parent).toBe(tooltipScope); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); }); describe('with specified enable expression', function() { @@ -231,7 +237,7 @@ describe('tooltip', function() { })); it('should not open ', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBeFalsy(); expect(elmBody.children().length).toBe(1); })); @@ -239,7 +245,7 @@ describe('tooltip', function() { it('should open', inject(function() { scope.enable = true; scope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBeTruthy(); expect(elmBody.children().length).toBe(2); })); @@ -259,7 +265,7 @@ describe('tooltip', function() { })); it('should open after timeout', function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); $timeout.flush(); @@ -267,23 +273,23 @@ describe('tooltip', function() { }); it('should not open if mouseleave before timeout', function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); $timeout.flush(); expect(tooltipScope.isOpen).toBe(false); }); it('should use default popup delay if specified delay is not a number', function() { - scope.delay='text1000'; + scope.delay = 'text1000'; scope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); }); it('should not open if disabled is present', function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); $timeout.flush(500); @@ -296,7 +302,7 @@ describe('tooltip', function() { }); it('should open when not disabled after being disabled - issue #4204', function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); $timeout.flush(500); @@ -309,7 +315,7 @@ describe('tooltip', function() { elmScope.disabled = false; elmScope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); $timeout.flush(); expect(tooltipScope.isOpen).toBe(true); @@ -338,9 +344,9 @@ describe('tooltip', function() { }); it( 'should update the controller value', function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(elmScope.isOpen).toBe(true); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); expect(elmScope.isOpen).toBe(false); }); }); @@ -352,7 +358,7 @@ describe('tooltip', function() { scope = $rootScope; })); - it( 'should use it to show but set the hide trigger based on the map for mapped triggers', inject(function($compile) { + it('should use it to show but set the hide trigger based on the map for mapped triggers', inject(function($compile) { elmBody = angular.element( '
' ); @@ -363,13 +369,13 @@ describe('tooltip', function() { tooltipScope = elmScope.$$childTail; expect(tooltipScope.isOpen).toBeFalsy(); - elm.trigger('focus'); + trigger(elm, 'focus'); expect(tooltipScope.isOpen).toBeTruthy(); - elm.trigger('blur'); + trigger(elm, 'blur'); expect(tooltipScope.isOpen).toBeFalsy(); })); - it( 'should use it as both the show and hide triggers for unmapped triggers', inject(function($compile) { + it('should use it as both the show and hide triggers for unmapped triggers', inject(function($compile) { elmBody = angular.element( '
' ); @@ -380,9 +386,9 @@ describe('tooltip', function() { tooltipScope = elmScope.$$childTail; expect(tooltipScope.isOpen).toBeFalsy(); - elm.trigger('fakeTriggerAttr'); + trigger(elm, 'fakeTriggerAttr'); expect(tooltipScope.isOpen).toBeTruthy(); - elm.trigger('fakeTriggerAttr'); + trigger(elm, 'fakeTriggerAttr'); expect(tooltipScope.isOpen).toBeFalsy(); })); @@ -410,7 +416,7 @@ describe('tooltip', function() { expect(tooltipScope2.isOpen).toBeFalsy(); // mouseenter trigger is still set - elm2.trigger('mouseenter'); + trigger(elm2, 'mouseenter'); expect(tooltipScope2.isOpen).toBeTruthy(); })); @@ -424,15 +430,15 @@ describe('tooltip', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; - expect( tooltipScope.isOpen ).toBeFalsy(); - elm.trigger('focus'); - expect( tooltipScope.isOpen ).toBeTruthy(); - elm.trigger('blur'); - expect( tooltipScope.isOpen ).toBeFalsy(); - elm.trigger('fakeTriggerAttr'); - expect( tooltipScope.isOpen ).toBeTruthy(); - elm.trigger('fakeTriggerAttr'); - expect( tooltipScope.isOpen ).toBeFalsy(); + expect(tooltipScope.isOpen).toBeFalsy(); + trigger(elm, 'focus'); + expect(tooltipScope.isOpen).toBeTruthy(); + trigger(elm, 'blur'); + expect(tooltipScope.isOpen).toBeFalsy(); + trigger(elm, 'fakeTriggerAttr'); + expect(tooltipScope.isOpen).toBeTruthy(); + trigger(elm, 'fakeTriggerAttr'); + expect(tooltipScope.isOpen).toBeFalsy(); })); it( 'should not show when trigger is set to "none"', inject(function($compile) { @@ -474,7 +480,7 @@ describe('tooltip', function() { tooltipScope = elmScope.$$childTail; var bodyLength = $body.children().length; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(1); @@ -505,7 +511,7 @@ describe('tooltip', function() { elm = elmBody.find('input'); elmScope = elm.scope(); - elm.trigger('fooTrigger'); + trigger(elm, 'fooTrigger'); tooltipScope = elmScope.$$childTail.$$childTail; })); @@ -528,9 +534,15 @@ describe('tooltipWithDifferentSymbols', function() { // configure interpolate provider to use [[ ]] instead of {{ }} beforeEach(module( function($interpolateProvider) { - $interpolateProvider.startSymbol('[['); - $interpolateProvider.startSymbol(']]'); - })); + $interpolateProvider.startSymbol('[['); + $interpolateProvider.startSymbol(']]'); + })); + + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } it('should show the correct tooltip text', inject(function($compile, $rootScope) { elmBody = angular.element( @@ -539,7 +551,7 @@ describe('tooltipWithDifferentSymbols', function() { $compile(elmBody)($rootScope); $rootScope.$apply(); var elmInput = elmBody.find('input'); - elmInput.trigger('focus'); + trigger(elmInput, 'focus'); expect(elmInput.next().find('div').next().html()).toBe('My tooltip'); })); @@ -573,8 +585,14 @@ describe('tooltip positioning', function() { tooltipScope = elmScope.$$childTail; })); + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + it('should re-position when value changes', inject(function($timeout) { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); scope.$digest(); $timeout.flush(); @@ -624,28 +642,35 @@ describe('tooltipHtml', function() { tooltipScope = elmScope.$$childTail; })); + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + + it('should render html properly', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect( elmBody.find('.tooltip-inner').html()).toBe(scope.html); })); it('should not open if html is empty', function() { scope.safeHtml = null; scope.$digest(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); }); it('should show on mouseenter and hide on mouseleave', inject(function($sce) { expect(tooltipScope.isOpen).toBe(false); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); expect($sce.getTrustedHtml(tooltipScope.contentExp())).toEqual(scope.html); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); expect(tooltipScope.isOpen).toBe(false); expect(elmBody.children().length).toBe(1); })); @@ -678,25 +703,32 @@ describe('tooltipHtmlUnsafe', function() { tooltipScope = elmScope.$$childTail; })); + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + + it('should warn that this is deprecated', function() { expect(logWarnSpy).toHaveBeenCalledWith(jasmine.stringMatching('deprecated')); }); it('should render html properly', inject(function() { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(elmBody.find('.tooltip-inner').html()).toBe(scope.html); })); it('should show on mouseenter and hide on mouseleave', inject(function() { expect(tooltipScope.isOpen).toBe(false); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); expect(tooltipScope.content).toEqual(scope.html); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); expect(tooltipScope.isOpen).toBe(false); expect(elmBody.children().length).toBe(1); })); @@ -709,6 +741,12 @@ describe('$tooltipProvider', function() { elmScope, tooltipScope; + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + describe('popupDelay', function() { beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider) { $tooltipProvider.options({popupDelay: 1000}); @@ -731,7 +769,7 @@ describe('$tooltipProvider', function() { })); it('should open after timeout', inject(function($timeout) { - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(false); $timeout.flush(); @@ -744,14 +782,14 @@ describe('$tooltipProvider', function() { beforeEach(module('template/tooltip/tooltip-popup.html')); beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider) { - $tooltipProvider.options({ appendToBody: true }); + $tooltipProvider.options({ appendToBody: true }); })); afterEach(function() { $body.find('.tooltip').remove(); }); - it( 'should append to the body', inject(function($rootScope, $compile, $document) { + it('should append to the body', inject(function($rootScope, $compile, $document) { $body = $document.find('body'); elmBody = angular.element( '
Selector Text
' @@ -765,7 +803,7 @@ describe('$tooltipProvider', function() { tooltipScope = elmScope.$$childTail; var bodyLength = $body.children().length; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(1); @@ -784,7 +822,7 @@ describe('$tooltipProvider', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); scope.$broadcast('$locationChangeSuccess'); @@ -793,8 +831,8 @@ describe('$tooltipProvider', function() { })); }); - describe( 'triggers', function() { - describe( 'triggers with a mapped value', function() { + describe('triggers', function() { + describe('triggers with a mapped value', function() { beforeEach(module('ui.bootstrap.tooltip', function($tooltipProvider) { $tooltipProvider.options({trigger: 'focus'}); })); @@ -802,7 +840,7 @@ describe('$tooltipProvider', function() { // load the template beforeEach(module('template/tooltip/tooltip-popup.html')); - it( 'should use the show trigger and the mapped value for the hide trigger', inject(function($rootScope, $compile) { + it('should use the show trigger and the mapped value for the hide trigger', inject(function($rootScope, $compile) { elmBody = angular.element( '
' ); @@ -815,13 +853,13 @@ describe('$tooltipProvider', function() { tooltipScope = elmScope.$$childTail; expect(tooltipScope.isOpen).toBeFalsy(); - elm.trigger('focus'); + trigger(elm, 'focus'); expect(tooltipScope.isOpen).toBeTruthy(); - elm.trigger('blur'); + trigger(elm, 'blur'); expect(tooltipScope.isOpen).toBeFalsy(); })); - it( 'should override the show and hide triggers if there is an attribute', inject(function($rootScope, $compile) { + it('should override the show and hide triggers if there is an attribute', inject(function($rootScope, $compile) { elmBody = angular.element( '
' ); @@ -834,9 +872,9 @@ describe('$tooltipProvider', function() { tooltipScope = elmScope.$$childTail; expect(tooltipScope.isOpen).toBeFalsy(); - elm.trigger('mouseenter'); + trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBeTruthy(); - elm.trigger('mouseleave'); + trigger(elm, 'mouseleave'); expect(tooltipScope.isOpen).toBeFalsy(); })); }); @@ -850,7 +888,7 @@ describe('$tooltipProvider', function() { // load the template beforeEach(module('template/tooltip/tooltip-popup.html')); - it( 'should use the show trigger and the mapped value for the hide trigger', inject(function($rootScope, $compile) { + it('should use the show trigger and the mapped value for the hide trigger', inject(function($rootScope, $compile) { elmBody = angular.element( '
' ); @@ -863,9 +901,9 @@ describe('$tooltipProvider', function() { tooltipScope = elmScope.$$childTail; expect(tooltipScope.isOpen).toBeFalsy(); - elm.trigger('customOpenTrigger'); + trigger(elm, 'customOpenTrigger'); expect(tooltipScope.isOpen).toBeTruthy(); - elm.trigger('customCloseTrigger'); + trigger(elm, 'customCloseTrigger'); expect(tooltipScope.isOpen).toBeFalsy(); })); }); @@ -878,7 +916,7 @@ describe('$tooltipProvider', function() { // load the template beforeEach(module('template/tooltip/tooltip-popup.html')); - it( 'should use the show trigger to hide', inject(function($rootScope, $compile) { + it('should use the show trigger to hide', inject(function($rootScope, $compile) { elmBody = angular.element( '
Selector Text
' ); @@ -890,11 +928,11 @@ describe('$tooltipProvider', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; - expect( tooltipScope.isOpen ).toBeFalsy(); - elm.trigger('fakeTrigger'); - expect( tooltipScope.isOpen ).toBeTruthy(); - elm.trigger('fakeTrigger'); - expect( tooltipScope.isOpen ).toBeFalsy(); + expect(tooltipScope.isOpen).toBeFalsy(); + trigger(elm, 'fakeTrigger'); + expect(tooltipScope.isOpen).toBeTruthy(); + trigger(elm, 'fakeTrigger'); + expect(tooltipScope.isOpen).toBeFalsy(); })); }); }); diff --git a/src/tooltip/test/tooltip2.spec.js b/src/tooltip/test/tooltip2.spec.js index 5d91d15898..96ae59962e 100644 --- a/src/tooltip/test/tooltip2.spec.js +++ b/src/tooltip/test/tooltip2.spec.js @@ -44,18 +44,24 @@ describe('tooltip directive', function() { return fragment; } - function closeTooltip(hostEl, trigger, shouldNotFlush) { - hostEl.trigger(trigger || 'mouseleave'); + function closeTooltip(hostEl, triggerEvt, shouldNotFlush) { + trigger(hostEl, triggerEvt || 'mouseleave'); if (!shouldNotFlush) { $timeout.flush(); } } + function trigger(element, evt) { + evt = new Event(evt); + + element[0].dispatchEvent(evt); + } + describe('basic scenarios with default options', function() { it('shows default tooltip on mouse enter and closes on mouse leave', function() { var fragment = compileTooltip('Trigger here'); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).toHaveOpenTooltips(); closeTooltip(fragment.find('span')); @@ -64,7 +70,7 @@ describe('tooltip directive', function() { it('should not show a tooltip when its content is empty', function() { var fragment = compileTooltip(''); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).not.toHaveOpenTooltips(); }); @@ -72,7 +78,7 @@ describe('tooltip directive', function() { $rootScope.content = 'some text'; var fragment = compileTooltip(''); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).toHaveOpenTooltips(); $rootScope.content = ''; @@ -88,7 +94,7 @@ describe('tooltip directive', function() { $rootScope.content = ''; $rootScope.$digest(); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).not.toHaveOpenTooltips(); }); }); @@ -112,7 +118,7 @@ describe('tooltip directive', function() { describe('placement', function() { it('can specify an alternative, valid placement', function() { var fragment = compileTooltip('Trigger here'); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); var ttipElement = fragment.find('div.tooltip'); expect(fragment).toHaveOpenTooltips(); @@ -126,7 +132,7 @@ describe('tooltip directive', function() { describe('class', function() { it('can specify a custom class', function() { var fragment = compileTooltip('Trigger here'); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); var ttipElement = fragment.find('div.tooltip'); expect(fragment).toHaveOpenTooltips(); @@ -143,7 +149,7 @@ describe('tooltip directive', function() { it('should show even after close trigger is called multiple times - issue #1847', function() { var fragment = compileTooltip('Trigger here'); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).toHaveOpenTooltips(); closeTooltip(fragment.find('span'), null, true); @@ -153,7 +159,7 @@ describe('tooltip directive', function() { closeTooltip(fragment.find('span'), null, true); expect(fragment).toHaveOpenTooltips(); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); expect(fragment).toHaveOpenTooltips(); $timeout.flush(); @@ -163,8 +169,8 @@ describe('tooltip directive', function() { it('should hide even after show trigger is called multiple times', function() { var fragment = compileTooltip('Trigger here'); - fragment.find('span').trigger('mouseenter'); - fragment.find('span').trigger('mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); + trigger(fragment.find('span'), 'mouseenter'); closeTooltip(fragment.find('span')); expect(fragment).not.toHaveOpenTooltips(); @@ -173,10 +179,10 @@ describe('tooltip directive', function() { it('should not show tooltips element is disabled (button) - issue #3167', function() { var fragment = compileTooltip(''); - fragment.find('button').trigger('mouseenter'); + trigger(fragment.find('button'), 'mouseenter'); expect(fragment).toHaveOpenTooltips(); - fragment.find('button').trigger('click'); + trigger(fragment.find('button'), 'click'); $timeout.flush(); // One needs to flush deferred functions before checking there is no tooltip. expect(fragment).not.toHaveOpenTooltips(); diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index b4e98914ce..dbc183948b 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -394,11 +394,12 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.b if (triggers.show !== 'none') { triggers.show.forEach(function(trigger, idx) { + // Using raw addEventListener due to jqLite/jQuery bug - #4060 if (trigger === triggers.hide[idx]) { - element.bind(trigger, toggleTooltipBind); + element[0].addEventListener(trigger, toggleTooltipBind); } else if (trigger) { - element.bind(trigger, showTooltipBind); - element.bind(triggers.hide[idx], hideTooltipBind); + element[0].addEventListener(trigger, showTooltipBind); + element[0].addEventListener(triggers.hide[idx], hideTooltipBind); } }); }