javascripthtmlaccessibilitydisabled-input

How to disable a button properly in HTML?


It is possible to disable a <button> element by setting the disabled attribute, for example with

document.querySelector("button").disabled = true;

However, this has some unfavourable usability implications such as causing the button to lose focus and become inaccessible via keyboard navigation. Some users will see the visual cue that the button is disabled, for example because something may be missing from the form. For users who rely on keyboard navigation and screen readers, this information will be much harder to get.

What is a good way to disable a button?


Solution

  • It is possible to make the button behave as if it were disabled without actually disabling it. You can use the aria-disabled attribute to do this:

    /**
     * Adds eventListeners to the buttons.
     * Here, not the action is added but the guard.
     */
    document.querySelectorAll("button").forEach(button => {
      button.addEventListener("click", actionGuard(action))
    });
    
    /**
     * This guard prevents the action if the button
     * is disabled or event handling was prevented.
     * @param {EventListener} action
     * @returns {EventListener}
     */
    function actionGuard(action) {
      return (evt) => {
        if (evt.target?.ariaDisabled) evt.preventDefault();
        else if (!evt.defaultPrevented) action(evt)
      }
    }
    
    /**
     * The action you want your button to perfom.
     * @type {EventListener}
     */
    function action(evt) {
      console.log("Event callback triggered:", evt.type)
    }
    button[aria-disabled="true"] {
      opacity: .65;
      cursor: not-allowed;
    }
    <button type="button">Enabled</button>
    <button type="button" aria-disabled="true">Disabled</button>

    Based on the aria-disabled attribute, the styling of the button is changed so that you still get the visual cue that this button is inactive. In addition, the guard will only allow the action to be performed if the aria-disabled attribute is not true and the event hasn't already been prevented.

    Disabling a button will just be:

    document.querySelector("button").ariaDisabled = true
    

    To improve usability, you might consider adding a tooltip to this disabled button, informing the user why the button is disabled. When adding the tooltip, be sure to help screen reader users by adding an aria-live element.