Marginalia

Angular MaterialのdisabledInteractive

近頃、HTMLの <button> タグの disabled 属性と aria-disabled 属性、Webアクセシビリティを絡めた発信や言説を見ることが増えているように感じる。

個別のケースにおいて disabled を使うべきか aria-disabled を使うべきかという是非についてはここでは扱わないが、どちらの方法も選択可能な技術的オプションとして持っておきたい。その点で、今回の記事では2023年あたりから Angular Material のコンポーネントに実装されはじめた disabledInteractive 機能について解説をする。

Angular Materialの disabledInteractive

Angular MaterialのMatButtonコンポーネントには disabledInteractive プロパティがある。このプロパティを追加したプルリクエストには次のように説明が書かれている。

Native disabled buttons don't allow focus and prevent the button from dispatching events. In some cases this can be problematic, because it prevents the app from showing to the user why the button is disabled.

ここで解決すべき問題とされているのは、ネイティブのボタン要素が非活性状態だとフォーカスを受け付けないことと、イベントを発火できないことである。それによって「なぜボタンが非活性なのか」を表示することを阻害してしまうことがある。

オプトインで追加された disabledInteractive プロパティは、非活性状態のスタイルを適用しつつ、aria-disabled=”true” 属性を有効にするが、disabled 属性はセットしない。これにより、ボタンに対してユーザーがインタラクト可能である状態を保つ。

公式ドキュメントのサンプルコードを見てみよう。この例では、ひとつめのボタンは disabled 属性が指定された上で disabledInteractive も併用されている。コンポーネントのテンプレートHTML上ではdisabled 属性がついているが、レンダリングされたDOM上ではdisabled属性はなくaria-disabled=”true” だけになっており、開発者に直接WAI-ARIA属性を扱わせることなく実装の詳細を隠蔽してある。

<button
  mat-raised-button
  disabled
  disabledInteractive
  matTooltip="This is a tooltip!">Disabled button allowing interactivity</button>

<button
  mat-raised-button
  disabled
  matTooltip="This is a tooltip!">Default disabled button</button>

disabledInteractive プロパティは、そのMatButtonのdisabled プロパティがtrue のときにインタラクト可能にするかどうかを制御する。インタラクト可能な状態では、あらゆるイベントは通常状態と同じく発火される。この例ではdisabledInteractiveがついているボタンの方だけマウスオーバーによりツールチップを表示できる。

当然、この状態では非活性状態でもクリック可能なので、フォームの送信などに使う場合はボタンの状態と同期してアプリケーション側でのバリデーション機構が必要になる。公式ドキュメントでも使用上の注意が書かれている。

Note: Using the disabledInteractive input can result in buttons that previously prevented actions to no longer do so, for example a submit button in a form. When using this input, you should guard against such cases in your component.

ともあれ、開発者の間でのニーズが高まっていることを受けてAngular Materialではこのような機能が実装されている。MatButtonに限らず、フォームコントロール系の要素では徐々に実装が広がっており、チェックボックスやラジオボタン、テキストインプットなどいくつかのコンポーネントで同様のdisabledInteractive プロパティを使えるようになっている。バージョンアップを重ねるごとに少しずつ増えており、今後も拡充されるだろう。

あくまでもオプトインであり、この機能は「使うべきベストプラクティス」として用意されているわけではない。システムの要件次第ではこういった対応が必要になるケースがあり、そういうときにAngular Materialでは実現できないと判断されることを避けるためのものだろう。必要だと思えば使えばよいし、そうでないなら使わなくてよい。しかし存在は知っておくことで実装の幅は広がるはずだ。