diff --git a/js/src/button.js b/js/src/button.js index fcf805502a..6225137019 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -81,15 +81,16 @@ class Button { $(activeElement).removeClass(ClassName.ACTIVE) } } + } else if (input.type === 'checkbox') { + if (this._element.tagName === 'LABEL' && input.checked === this._element.classList.contains(ClassName.ACTIVE)) { + triggerChangeEvent = false + } + } else { + // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input + triggerChangeEvent = false } if (triggerChangeEvent) { - if (input.hasAttribute('disabled') || - rootElement.hasAttribute('disabled') || - input.classList.contains('disabled') || - rootElement.classList.contains('disabled')) { - return - } input.checked = !this._element.classList.contains(ClassName.ACTIVE) $(input).trigger('change') } @@ -99,13 +100,15 @@ class Button { } } - if (addAriaPressed) { - this._element.setAttribute('aria-pressed', - !this._element.classList.contains(ClassName.ACTIVE)) - } + if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) { + if (addAriaPressed) { + this._element.setAttribute('aria-pressed', + !this._element.classList.contains(ClassName.ACTIVE)) + } - if (triggerChangeEvent) { - $(this._element).toggleClass(ClassName.ACTIVE) + if (triggerChangeEvent) { + $(this._element).toggleClass(ClassName.ACTIVE) + } } } @@ -140,15 +143,24 @@ class Button { $(document) .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { - event.preventDefault() - let button = event.target if (!$(button).hasClass(ClassName.BUTTON)) { - button = $(button).closest(Selector.BUTTON) + button = $(button).closest(Selector.BUTTON)[0] } - Button._jQueryInterface.call($(button), 'toggle') + if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) { + event.preventDefault() // work around Firefox bug #1540995 + } else { + const inputBtn = button.querySelector(Selector.INPUT) + + if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) { + event.preventDefault() // work around Firefox bug #1540995 + return + } + + Button._jQueryInterface.call($(button), 'toggle') + } }) .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { const button = $(event.target).closest(Selector.BUTTON)[0] diff --git a/js/tests/unit/button.js b/js/tests/unit/button.js index 724545a532..324e940113 100644 --- a/js/tests/unit/button.js +++ b/js/tests/unit/button.js @@ -61,6 +61,22 @@ $(function () { assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true') }) + QUnit.test('should not toggle aria-pressed on buttons with disabled class', function (assert) { + assert.expect(2) + var $btn = $('') + assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false') + $btn.bootstrapButton('toggle') + assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is still false') + }) + + QUnit.test('should not toggle aria-pressed on buttons that are disabled', function (assert) { + assert.expect(2) + var $btn = $('') + assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false') + $btn.bootstrapButton('toggle') + assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is still false') + }) + QUnit.test('should toggle aria-pressed on buttons with container', function (assert) { assert.expect(1) var groupHTML = '