diff --git a/dist/js/bootstrap.js b/dist/js/bootstrap.js index 4ce288b22ef3..ac305a13d6ae 100644 --- a/dist/js/bootstrap.js +++ b/dist/js/bootstrap.js @@ -1220,7 +1220,7 @@ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript re var $tip = this.tip() this.setContent() - + this.setAriaDescribedBy() if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? @@ -1342,6 +1342,7 @@ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript re var $tip = this.tip() var e = $.Event('hide.bs.' + this.type) + this.removeAriaDescribedBy() function complete() { if (that.hoverState != 'in') $tip.detach() that.$element.trigger('hidden.bs.' + that.type) @@ -1441,6 +1442,25 @@ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript re return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow') } + Tooltip.prototype.uid = function (prefix) { + var id = prefix + Math.floor( Math.random() * 1000000 ) + + while ($('#' + id).length) { + id += Math.floor( Math.random() * 1000000 ) + } + + return id + } + + Tooltip.prototype.setAriaDescribedBy = function() { + var tipId = this.$tip.attr('id', this.uid(this.type)) + this.$element.attr('aria-describedby', tipId) + } + + Tooltip.prototype.removeAriaDescribedBy = function() { + this.$element.removeAttr('aria-describedby') + } + Tooltip.prototype.validate = function () { if (!this.$element[0].parentNode) { this.hide() diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js index e579a9ef7f98..e95b91cbb18b 100644 --- a/js/tests/unit/tooltip.js +++ b/js/tests/unit/tooltip.js @@ -41,6 +41,36 @@ $(function () { equal(tooltip.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute') }) + test('should add set set aria describedby to the element called on show', function() { + var tooltip = $('').bootstrapTooltip() + .appendTo('#qunit-fixture') + .bootstrapTooltip('show') + ok(tooltip.attr('aria-describedby'), 'has the right attributes') + var id = $('.tooltip').attr('id') + + ok($('#' + id).length == 1, 'has a unique id') + ok($('.tooltip').attr('aria-describedby') === tooltip.attr('id'), 'they match!') + ok(tooltip.attr('aria-describedby') !== undefined, 'has the right attributes') + }) + + test('should remove the aria-describedby attributes on hide', function() { + var tooltip = $('').bootstrapTooltip() + .appendTo('#qunit-fixture') + .bootstrapTooltip('show') + ok(tooltip.attr('aria-describedby'), 'has the right attributes') + tooltip.bootstrapTooltip('hide') + ok(!tooltip.attr('aria-describedby'), 'removed the attributes on hide') + }) + + test('should assign a unique id tooltip element', function () { + $('') + .appendTo('#qunit-fixture') + .bootstrapTooltip('show'), + id = $('.tooltip').attr('id') + + ok( $('#' + id).length == 1 && id.indexOf('tooltip') === 0, 'generated prefixed and unique tooltip id') + }) + test('should place tooltips relative to placement option', function () { $.support.transition = false var tooltip = $('') diff --git a/js/tooltip.js b/js/tooltip.js index f688b3020923..037f8870bdea 100644 --- a/js/tooltip.js +++ b/js/tooltip.js @@ -149,7 +149,7 @@ var $tip = this.tip() this.setContent() - + this.setAriaDescribedBy() if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? @@ -271,6 +271,7 @@ var $tip = this.tip() var e = $.Event('hide.bs.' + this.type) + this.removeAriaDescribedBy() function complete() { if (that.hoverState != 'in') $tip.detach() that.$element.trigger('hidden.bs.' + that.type) @@ -370,6 +371,25 @@ return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow') } + Tooltip.prototype.uid = function (prefix) { + var id = prefix + Math.floor( Math.random() * 1000000 ) + + while ($('#' + id).length) { + id += Math.floor( Math.random() * 1000000 ) + } + + return id + } + + Tooltip.prototype.setAriaDescribedBy = function() { + var tipId = this.$tip.attr('id', this.uid(this.type)) + this.$element.attr('aria-describedby', tipId) + } + + Tooltip.prototype.removeAriaDescribedBy = function() { + this.$element.removeAttr('aria-describedby') + } + Tooltip.prototype.validate = function () { if (!this.$element[0].parentNode) { this.hide()