WAI-ARIAを使用したJSアコーディオンの実装

JSでアコーディオンを作成するときに、対象の要素に対して`.on`クラスをつけて状態を判別したりしていますが、なんだかなーと思ったので変更してみました。
WAI-ARIAを使用すれば要素の状態を明示できるので、余計なクラスがつかずにいい感じかなーと。

# WAI-ARIAについて

@h_plum さんの記事がとても参考になるので、こちらでご確認いただくと良いと思います。
[アクセシビリティを意識したWAI-ARIA実装について](https://qiita.com/h_plum/items/1436526474672c14af1c)


# デモ

まずは出来上がったものをどうぞ。

<p class="codepen" data-height="500" data-theme-id="0" data-default-tab="js,result" data-user="cubkich" data-slug-hash="omaodx" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="wai-aria accordion">
  <span>See the Pen <a href="https://codepen.io/cubkich/pen/omaodx/">
  wai-aria accordion</a> by かぶきち (<a href="https://codepen.io/cubkich">@cubkich</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

ではそれぞれ解説していきます、


# HTML

```html
<div class="wrapper" role="tablist">
	<h3 id="acdTab01"
		data-acd="tab"
		role="tab"
		aria-expanded="true"
		aria-controls="acdPnl01"
		aria-selected="true">大見出し<span class="icon"></span>
	</h3>
	<div id="acdPnl01"
		data-acd="tabpanel"
		role="tabpanel"
		aria-labelledby="acdTab01"
		aria-hidden="false">
		<dl>
			<dt>中見出し</dt>
			<dd>テキストテキストテキスト</dd>
		</dl>
	</div><!-- /#acdPnl01 -->
</div><!-- /.wrapper -->
```

## カスタムデータ属性
`data-acd="tab", data-acd="tabpanel"`
特に意味はないのでここは単純にclassでも良いかもです。

## role属性
`role="tab", role="tabpanel"`
アコーディオンのタブ部分であること、パネル部分であることを明記しています。
hタグにrole属性つけるのってどうなんでしたっけ。`div`につけるのは開発者、クローラーの両方にとって良いことだと思います。

## WAI-ARIA属性
### aria-controls
`aria-controls="acdPnl01"`
アコーディオンのタブが、どのパネルに紐付いたものかを明示しています。
今回は`#acdPnl01`に対するタブですよーってことです。アコーディオンに限らず、タブアクションで使えそう。

### aria-selected
`aria-selected="true"`
フォーカスされているかの状態ですが、アコーディオンでいうと開いているかどうか、みたいなところです。

### aria-labelledby
`aria-labelledby="acdTab01"`
ラベルの明示です。今回は大見出しがラベルにあたるので、`#acdTab01`の要素に向けられています。

### aria-expanded
`aria-expanded="true"`
対象の要素が展開されているかどうかの状態を表します。
今回は読み込み時点で展開されているので`true`となっています。
開閉するたびに切り替わります。

### aria-hidden
`aria-hidden="false"`
こちらは要素が隠れているかどうかの状態です。
`aria-expanded`と反対の値が入ります。

## 感想
これだけ指定するものが多いのであれば、従来どおりにクラス振ったほう楽じゃね？って思います。
が、WAI-ARIA属性が振られていることでレンダリングされていない状態のHTMLを見ただけでもどういう状態なのかがわかるので、HTMLだけ読むひととか、クローラーに対しては優しいのかなーと。


# SCSS
そんなに特殊なことはしていなかったの割愛します。
今回`[data-acd="tab"]`とか`[data-acd="tabpanel"]`に直接スタイル当てちゃったけど、他の見た目のアコーディオン作るときに困りそうだからやっぱりクラス振ったほうが良いな、と思いました。


# jQuery
まずはアコーディオンに使用する要素を変数に入れます。タブを`$_trg`、パネルを`$_pnl`とします。
HTMLのときと同様ですが、ここでdata属性を使用してしまうと他の動きするアコーディオン作るとき大変かもです。

その他に、アニメーションの制御に使用するフラグも用意します。
`anm_flg`はアニメーションが連続で実行されないように、アニメーション中はtrue、それ以外はfalseを返すように設定し、trueが返ってきたときはアニメーションを実行しません。(`stop()`とか使っても良いかもです。)

```javascript
const $_trg = $("[data-acd=tab]"),
      $_pnl = $("[data-acd=tabpanel]");
let anm_flg;
```

## 初期設定
ページを読み込んだタイミングでアコーディオンが開いた状態にします。
デモはPC表示だけですが、実際に作ったものはSPのとき閉じているという仕様だったので、その名残です。
JS使わないでも大丈夫な部分ですね。

```javascript
const acd_init = () => {
  anm_flg = false;
  $_pnl.show();
  add_pnl_attr($_pnl);
  $_trg.attr("aria-selected", "true");
};
```

## パネルの開閉に合わせて属性の変更
アコーディオンを開いたときのWAI-ARIAの属性を変更します。
動きには直接関係ないですが、開いてますよー、閉じてますよーっていう情報を変更しています。

```javascript
// パネルを開いたときの属性に変更
const add_pnl_attr = $_elem => {
  if (!$_elem) return;
  $_elem.attr({
    "aria-hidden": "false"
  });
};

// パネルを閉じたときの属性に変更
const rm_pnl_attr = $_elem => {
  if (!$_elem) return;
  $_elem.attr({
    "aria-hidden": "true"
  });
};
```

## パネルの動き
タブ要素の`aria-controls`でパネルの要素が指定されているので、紐付いているパネルを`slideDown()`させると同時に、タブ要素の`aria-selected`属性の状態を変更します。
開く/閉じるでそれぞれ逆の動きをしているので、開く/閉じるを引数のtrue/falseとかにしても良かったかもですね。

```javascript
// パネルを開くときの動き
const open_action = $_tab_elem => {
  if (anm_flg) return;
  anm_flg = true;

  const $_pnl_elem = $("#" + $_tab_elem.attr("aria-controls"));

  $_tab_elem.attr({
    "aria-expanded": "false",
    "aria-selected": "true"
  });
  $_pnl_elem.slideDown(function() {
    anm_flg = false;
  });
  rm_pnl_attr($_pnl_elem);
};

// パネルを閉じるときの動き
const close_action = $_tab_elem => {
  if (anm_flg) return;
  anm_flg = true;

  const $_pnl_elem = $("#" + $_tab_elem.attr("aria-controls"));

  $_tab_elem.attr({
    "aria-expanded": "false",
    "aria-selected": "false"
  });
  $_pnl_elem.slideUp(function() {
    anm_flg = false;
  });
  rm_pnl_attr($_pnl_elem);
};
```

## クリックしたときの指示出し
タブ要素をクリックしたとき、`aria-selected`属性の状態をもとに開く/閉じるどちらの関数を実行するか判別しています。
関数を一つにしてtrue/falseにするときは、何かしら変数とかに代入して関数を実行するみたいになりそうですね。

```javascript
// トリガーをクリックしたとき
$_trg.on("click", function() {
  const _self = $(this);
  _self.attr("aria-selected") === "true" ? close_action(_self) : open_action(_self);
});
```

## 実行 
あとはページを読み込んだタイミングとかで実行すれば、クリックしたタイミングで動いてくれるはずです。

```javascript
acd_action();
```

# まとめ
WAI-ARIAなんてものがあるんだなーと試しに使ってみましたが、意味を覚えて使わないと普通に作るより時間かかるし、違う意味で捉えられる可能性もあるので、ちゃんと調べて覚えてからじゃないと使えこなせそうだなーといった印象です。
使ったほうがセマンティック的に良いかなーだけど、そんなに意識しないサイトだったらただのコストなので使う必要ないか、というところでしょうか。
あ・・間違いなどあったら指摘していただけるとありがたいです(´・ω・｀)