私は普段MENTAでフリーランスや初学者を教えているのですが、HTMLのカスタムデータ属性について知らない方が多いと感じました。
カスタムデータ属性を扱うことができると、コードが見やすくなったり、できることが増えるのでぜひマスターしましょう。
属性とは
そもそも属性とはなんでしょう。
例えば以下のようなHTMLがあったとします。
<p class="hoge">属性とは</p>
このとき、classが属性名(attribute name)で、hogeが属性値(attribute value)と呼ばれます。
もう1つ例を出しましょう。
以下のようなHTMLがあったとします。
<a href="https://tips-web.net/">詳細はこちら</a>
このとき、属性名はhrefで、属性値はhttps://tips-web.net/ ということになります。
カスタムデータ属性とは
属性名にはhrefやclassやidなど、HTMLでもともと用意されているものだけしか使えない。ということはありません。
自分で属性名をつくることができるのです。
それがカスタムデータ属性です。
カスタムデータ属性の書き方は以下です。
<p class="hoge" data-myattrname="カスタムデータ属性">カスタムデータ属性とは</p>
data-○○="属性値"
のように書きます。
○○の部分はなんでもOKです。
例えば以下のように書くこともできます。
data-index-number="123"
○○の部分はハイフンつなぎで分かれていてもOKということですね。
○○の部分は全部小文字で書きます。
大文字は含めてはいけないので注意しましょう。
カスタムデータ属性を取得する
カスタムデータ属性の仕様がなんとなくわかったところで、どんなふうに使うのか例を見てみましょう。
以下のHTMLを想定し、このHTMLに記述されたカスタムデータ属性を取得する方法について解説します。
<p class="user" data-ruby="やまだたろう" data-index-id="123">山田太郎</p>
JavaScriptから取得する
const user = document.querySelector('.user');
console.log(user.dataset.ruby);// "やまだたろう"
console.log(user.dataset.indexId);// "123"
こんな感じで取得します。
もしjQueryで取得する場合は、以下のように書きます。
const user = $('.user');
console.log(user.data('ruby'));// "やまだたろう"
console.log(user.data('indexId'));// 123
違いはdata-index-idの方ですね。
jQueryの場合は、暗黙の型変換が行われ、Number型として取得されます。
一方、素のJSの場合はString型として取得されます。
もし、jQueryでも素のJSと同じようにString型で取得したいなら、attrメソッドを使用します。
attrメソッドを使う場合は少し書き方が変わるので注意してください。
console.log(user.attr('data-index-id'));// "123"
▼参考記事
jQueryの$elm.data()で取得できる値は暗黙でstringから型変換される
CSSから取得する
続いて、CSSから取得するケースを見てみましょう。
同じくHTMLが以下のようなものだと想定した場合を例に解説します。
<p class="user" data-ruby="やまだたろう" data-index-id="123">山田太郎</p>
CSSで取得する場合、attr関数を使うと取得できます。
[data-ruby]::before {
content: attr(data-ruby);
}
結果、以下のような感じになります。
これを応用すると、以下のようにルビ振りが可能になります。
▼参考記事
CSSでデザイン調整可能なルビ
【本題】カスタムデータ属性を使用してタブ切り替え
カスタムデータ属性の基本がわかったところで本題です。
今回扱うタブ切り替えのイメージは以下のようなものを想定します。
このデザインはよく見ますね!
すべてをクリックしたら、カテゴリー関係なく、全投稿データが表示されます。
また、「ニュース」をクリックしたらカテゴリーのニュースに紐づく投稿だけが表示されます。
その際に、クリックしたカテゴリーは赤くなります。
HTML
HTMLは以下のようにします。
<ul class="p-home_news-category">
<li class="js-tab is-active" data-category="all">すべて</li>
<li class="js-tab" data-category="news">ニュース</li>
<li class="js-tab" data-category="store">店舗情報</li>
</ul>
<div class="js-tab-target p-home_news-lists-wrap is-active" data-target="all">
<ul class="p-home_news-lists">
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">店舗情報</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">店舗情報</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
</ul>
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="news">
<ul class="p-home_news-lists">
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">ニュース</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
</ul>
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="store">
<ul class="p-home_news-lists">
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">店舗情報</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">店舗情報</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
<li class="p-home_news-item">
<a href="/news/01/" class="p-home_news-item-link">
<div class="p-home_news-item-body">
<div class="p-home_news-item-meta">
<time class="time" datetime="2022-01-01">2022/01/01</time>
<span class="c-category">店舗情報</span>
</div>
<p class="p-home_news-item-title">
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</a>
</li>
</ul>
</div>
重要な部分だけ抜き取ると以下のようになります。
<ul class="p-home_news-category">
<li class="js-tab is-active" data-category="all">すべて</li>
<li class="js-tab" data-category="news">ニュース</li>
<li class="js-tab" data-category="store">店舗情報</li>
</ul>
<div class="js-tab-target p-home_news-lists-wrap is-active" data-target="all">
...
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="news">
...
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="store">
...
</div>
js-
から始まるクラス名は、この後jsファイルからアクセスする際に利用するクラスとして設定しています。
カスタムデータ属性として、div.js-tab
とdiv.js-tab-target
それぞれに、data-category
を設定し、属性値にallやnews、storeを設定しています。
JavaScript(jQuery)
jsファイルには以下のように記述します。
const $js_tab = $('.js-tab');
const $js_tab_target = $('.js-tab-target');
const cls = 'is-active';
$js_tab.on('click', function () {
const this_category = $(this).data('category');
$js_tab.removeClass(cls);
$(this).addClass(cls);
$js_tab_target.removeClass(cls);
$js_tab_target.each(function () {
const target_data = $(this).data('target');
if (this_category === target_data) {
$(this).addClass(cls);
}
});
});
まずは扱いやすいように1~3行目で変数を定義しています。
js-tab
やjs-tab-target
が良くわからない場合はHTMLを見返してください。
cssで、is-active
クラスのつけ外しにより、タブの色を赤くしたり、タブ切り替えの対象となる要素の表示・非表示を制御しています。👇
.p-home_news-category > li {
background-color: #f6f6f6;
}
.p-home_news-category > li.is-active {
background-color: #ce0202;
color: #fff;
}
.js-tab-target {
display: none;
}
.js-tab-target.is-active {
display: block;
}
jsの以下の部分についてもう少し解説します。
$js_tab.on('click', function () {
const this_category = $(this).data('category');
$js_tab.removeClass(cls);
$(this).addClass(cls);
$js_tab_target.removeClass(cls);
$js_tab_target.each(function () {
const target_data = $(this).data('target');
if (this_category === target_data) {
$(this).addClass(cls);
}
});
});
まず、タブをクリックします。
$js_tab.on('click', function () {
次に、クリックしたタブのカスタムデータ属性の値を取得し、変数に格納しています。
$js_tab.on('click', function () {
const this_category = $(this).data('category');
次に、タブについているis-active
クラスを全部いったん外してリセットします。
$js_tab.on('click', function () {
const this_category = $(this).data('category');
$js_tab.removeClass(cls);
リセットしたら、クリックしたタブにis-active
クラスを付けます。
$js_tab.on('click', function () {
const this_category = $(this).data('category');
$js_tab.removeClass(cls);
$(this).addClass(cls);
ここまでがタブ側の制御です。
ここからタブをクリックした際に切り替わるdiv.js-tab-target
の制御をします。
まずは、 div.js-tab-target
のis-active
クラスを全部リセットします。
$js_tab.on('click', function () {
(省略)
$js_tab_target.removeClass(cls);
リセットしたら div.js-tab-target
をjQueryのeachメソッドでループさせます。
eachでループさせることで、以下のコードの「...
」の部分では、1つ1つのdiv.js-tab-target
が渡されてきます。
$js_tab.on('click', function () {
(省略)
$js_tab_target.removeClass(cls);
$js_tab_target.each(function () {
... // 1つ1つのdiv.js-tab-targetが渡されてきます。
});
HTMLで言うと、以下のdivが1つ1つ渡されてくるということです。
<div class="js-tab-target p-home_news-lists-wrap is-active" data-target="all">
...
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="news">
...
</div>
<div class="js-tab-target p-home_news-lists-wrap" data-target="store">
...
</div>
これら1つ1つのjs-tab-targetには、カスタムデータ属性が付与されています。
この「1つ1つがもつカスタムデータ属性の属性値」と、「クリックしたタブが持つ属性値」が同じであれば、その属性値をもつjs-tab-targetを表示し、そうじゃなければ表示しないようにすればタブ切り替えが実装できます。
このことをプログラムで書いたのが以下の7~10行目の部分です。
$js_tab.on('click', function () {
(省略)
$js_tab_target.removeClass(cls);
$js_tab_target.each(function () {
const target_data = $(this).data('target');
if (this_category === target_data) {
$(this).addClass(cls);
}
});
});
8行目のthis_category
はタブをクリックしたときの属性値が格納されており、target_data
はeachでループしている対象のdiv.js-tab-target
1つ1つの属性値が格納されています。
this_category
と target_data
をループの中で都度比較して、同じだったら div.js-tab-target
にis-active
を付けているわけですね。
先ほども説明しましたが、cssでis-activeが追加要素をdisplay:blockで表示し、ついてない要素はdisplay:noneで消しています。
これによりタブ切り替えが実装できます。
.p-home_news-category > li {
background-color: #f6f6f6;
}
.p-home_news-category > li.is-active {
background-color: #ce0202;
color: #fff;
}
.js-tab-target {
display: none;
}
.js-tab-target.is-active {
display: block;
}
まとめ
カスタムデータ属性は動的サイトではかなり使います。
また、途中で登場したjQueryのeachもめちゃくちゃ使うのでどちらも必ず使えるようにマスターしましょう!
おまけ
今回紹介した以下のタブ切り替えは私が販売してる教材で扱ってるサイトのキャプチャです。
サイト全体の様子は以下よりご確認いただけますので興味ある方は覗いてみてください。
WordPress化された完成サイトを見る
■ベーシック認証:ID→ demo / Pass→ demo