15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-02-16

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

WAI-ARIAについて

@h_plum さんの記事がとても参考になるので、こちらでご確認いただくと良いと思います。
アクセシビリティを意識したWAI-ARIA実装について

デモ

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

See the Pen wai-aria accordion by かぶきち (@cubkich) on CodePen.

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

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()とか使っても良いかもです。)

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

初期設定

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

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

パネルの開閉に合わせて属性の変更

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

// パネルを開いたときの属性に変更
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とかにしても良かったかもですね。

// パネルを開くときの動き
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にするときは、何かしら変数とかに代入して関数を実行するみたいになりそうですね。

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

実行

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

acd_action();

まとめ

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

15
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?