diff --git a/src/tooltip/docs/readme.md b/src/tooltip/docs/readme.md index ca6c92c618..d9fff74d4b 100644 --- a/src/tooltip/docs/readme.md +++ b/src/tooltip/docs/readme.md @@ -41,9 +41,12 @@ provided hide triggers: - `mouseenter`: `mouseleave` - `click`: `click` +- `outsideClick`: `outsideClick` - `focus`: `blur` - `none`: `` +The `outsideClick` trigger will cause the tooltip to toggle on click, and hide when anything else is clicked. + For any non-supported value, the trigger will be used to both show and hide the tooltip. Using the 'none' trigger will disable the internal trigger(s), one can then use the `tooltip-is-open` attribute exclusively to show and hide the tooltip. diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index 74b249a639..8169d20148 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -573,6 +573,31 @@ describe('tooltip', function() { elm.trigger('mouseenter'); expect(tooltipScope.isOpen).toBeFalsy(); })); + + it('should toggle on click and hide when anything else is clicked when trigger is set to "outsideClick"', inject(function($compile, $document) { + elm = $compile(angular.element( + 'Selector Text' + ))(scope); + scope.$apply(); + elmScope = elm.scope(); + tooltipScope = elmScope.$$childTail; + + // start off + expect(tooltipScope.isOpen).toBeFalsy(); + + // toggle + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeTruthy(); + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeFalsy(); + + // click on, outsideClick off + trigger(elm, 'click'); + expect(tooltipScope.isOpen).toBeTruthy(); + angular.element($document[0].body).trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBeFalsy(); + })); }); describe('with an append-to-body attribute', function() { diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index 08dee00e4f..4b97b313d1 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -23,6 +23,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s var triggerMap = { 'mouseenter': 'mouseleave', 'click': 'click', + 'outsideClick': 'outsideClick', 'focus': 'blur', 'none': '' }; @@ -422,13 +423,37 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } } + // hide tooltips/popovers for outsideClick trigger + function bodyHideTooltipBind(e) { + if (ttScope == null || !ttScope.isOpen || tooltip == null) { + return; + } + // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked + if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { + hideTooltipBind(); + } + } + var unregisterTriggers = function() { triggers.show.forEach(function(trigger) { - element.unbind(trigger, showTooltipBind); + if (trigger === 'outsideClick') { + element[0].removeEventListener('click', toggleTooltipBind); + } + else + { + element[0].removeEventListener(trigger, showTooltipBind); + element[0].removeEventListener(trigger, toggleTooltipBind); + } }); triggers.hide.forEach(function(trigger) { trigger.split(' ').forEach(function(hideTrigger) { - element[0].removeEventListener(hideTrigger, hideTooltipBind); + if (trigger === 'outsideClick') { + $document[0].body.removeEventListener('click', bodyHideTooltipBind); + } + else + { + element[0].removeEventListener(hideTrigger, hideTooltipBind); + } }); }); }; @@ -442,7 +467,11 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s if (triggers.show !== 'none') { triggers.show.forEach(function(trigger, idx) { // Using raw addEventListener due to jqLite/jQuery bug - #4060 - if (trigger === triggers.hide[idx]) { + if (trigger === 'outsideClick') { + element[0].addEventListener('click', toggleTooltipBind); + $document[0].body.addEventListener('click', bodyHideTooltipBind); + } + else if (trigger === triggers.hide[idx]) { element[0].addEventListener(trigger, toggleTooltipBind); } else if (trigger) { element[0].addEventListener(trigger, showTooltipBind);