個人的によく使用する、button要素をクリックした際に別要素のclassをtoggleするという処理を汎用的に使えるように実装してみます。
サンプルコード
まずはシンプルに実装してみます。
HTMLは以下の内容で、data属性を使ってクリック要素ととtoggle要素を紐づける想定です。
<button data-toggle-switch="target01">ボタン1</button> <div class="contents" data-toggle-target="target01">コンテンツ1</div> <hr> <a href="#" data-toggle-switch="target02">ボタン2</a> <div class="contents" data-toggle-target="target02">コンテンツ2</div>
toggle要素にclassが付与された時に表示されるように設定します。
.contents {
display: none;
}
.contents.is-open {
display: block;
}
JavaScriptは以下の通りです。
toggleOnClick();
/**
* 要素をクリック時に、別の要素のclassをtoggle
* @param {object} [options] オプション設定
* @param {string} [options.triggerDataAttr='toggle-switch'] クリック対象要素のdata属性
* @param {string} [options.targetDataAttr='toggle-target'] toggle対象要素のdata属性
* @param {string} [options.openClass='is-open'] toggle要素に付与するclass名
*/
function toggleOnClick({
triggerDataAttr = 'toggle-switch',
targetDataAttr = 'toggle-target',
openClass = 'is-open'
} = {}) {
document.querySelectorAll(`[data-${triggerDataAttr}]`).forEach(trigger => {
const targetVal = trigger.getAttribute(`data-${triggerDataAttr}`);
trigger.addEventListener('click', (e) => {
e.preventDefault();
const target = document.querySelector(`[data-${targetDataAttr} = ${targetVal}]`);
if (target) {
target.classList.toggle(openClass);
}
});
});
}
使い方はコメントに記載の通りですが、引数はオブジェクト形式で、クリック対象とtoggle対象のdata属性、付与するclass名を変更できるようにしています。
シンプルなtoggle処理のデモページ
toggle処理の際にクリック対象のスタイルも変更したいということがあるので、toggle対象要素と同じくクリック対象要素にもclassを付け外しできるようにしてみます。
toggleOnClick();
/**
* 要素をクリック時に、別の要素のclassをtoggle
* @param {object} [options] オプション設定
* @param {string} [options.triggerDataAttr='toggle-switch'] クリック対象要素のdata属性
* @param {string} [options.activeClass] クリック対象要素に付与するclass名
* @param {string} [options.targetDataAttr='toggle-target'] toggle対象要素のdata属性
* @param {string} [options.openClass='is-open'] toggle要素に付与するclass名
*/
function toggleOnClick({
triggerDataAttr = 'toggle-switch',
activeClass = 'is-active',
targetDataAttr = 'toggle-target',
openClass = 'is-open',
} = {}) {
document.querySelectorAll(`[data-${triggerDataAttr}]`).forEach(trigger => {
const targetVal = trigger.getAttribute(`data-${triggerDataAttr}`);
trigger.addEventListener('click', (e) => {
e.preventDefault();
const target = document.querySelector(`[data-${targetDataAttr} = ${targetVal}]`);
if (target) {
const toggled = target.classList.toggle(openClass);
if (toggled) {
trigger.classList.add(activeClass);
} else {
trigger.classList.remove(activeClass);
}
}
});
});
}
openClassのオプションを追加して、クリック対象要素にもclassが付与されるようになりました。
クリック対象要素にもclassを付与するデモページ
最後に、クリック対象とtoggle対象のセットをグルーピングできるようにして、そのグループ内では1つだけしかclassを付与できない(1つしか開けない)ようにしてみます。
HTMLに確認用の要素を追加します。
<button data-toggle-switch="target01">ボタン1</button> <div class="contents" data-toggle-target="target01">コンテンツ1</div> <hr> <button data-toggle-switch="target02">ボタン2</button> <div class="contents" data-toggle-target="target02">コンテンツ2</div> <hr> <button data-toggle-switch="acc01" data-toggle-group="group01">アコーディオンボタン1</button> <div class="contents" data-toggle-target="acc01">アコーディオンコンテンツ1</div> <hr> <button data-toggle-switch="acc02" data-toggle-group="group01">アコーディオンボタン2</button> <div class="contents" data-toggle-target="acc02">アコーディオンコンテンツ2</div>
オプションを追加して、クリックしたセットのtoggle処理の前に同じグループのclassを除去する処理を追加します。
toggleOnClick();
/**
* 要素をクリック時に、別の要素のclassをtoggle
* @param {object} [options] オプション設定
* @param {string} [options.triggerDataAttr='toggle-switch'] クリック対象要素のdata属性
* @param {string} [options.activeClass] クリック対象要素に付与するclass名
* @param {string} [options.targetDataAttr='toggle-target'] toggle対象要素のdata属性
* @param {string} [options.openClass='is-open'] toggle要素に付与するclass名
* @param {string} [options.groupDataAttr='toggle-group'] クリック対象要素に付与する、複数のセットをグルーピングするdata属性
*/
function toggleOnClick({
triggerDataAttr = 'toggle-switch',
activeClass = 'is-active',
targetDataAttr = 'toggle-target',
openClass = 'is-open',
groupDataAttr = 'toggle-group',
} = {}) {
document.querySelectorAll(`[data-${triggerDataAttr}]`).forEach(trigger => {
const targetVal = trigger.getAttribute(`data-${triggerDataAttr}`);
const groupVal = trigger.getAttribute(`data-${groupDataAttr}`);
trigger.addEventListener('click', (e) => {
e.preventDefault();
// グループ設定がある場合
if (groupVal) {
// 同じグループの他のセットを取得して、classを除去
document.querySelectorAll(`[data-${triggerDataAttr}][data-${groupDataAttr}="${groupVal}"]`).forEach(groupTrigger => {
if (groupTrigger !== trigger) {
const otherTargetVal = groupTrigger.getAttribute(`data-${triggerDataAttr}`);
const otherTarget = document.querySelector(`[data-${targetDataAttr} = ${otherTargetVal}]`);
if (otherTarget) {
otherTarget.classList.remove(openClass);
}
groupTrigger.classList.remove(activeClass);
}
});
}
const target = document.querySelector(`[data-${targetDataAttr} = ${targetVal}]`);
if (target) {
const toggled = target.classList.toggle(openClass);
if (toggled) {
trigger.classList.add(activeClass);
} else {
trigger.classList.remove(activeClass);
}
}
});
});
}
これで想定した実装ができました。
グルーピング機能追加デモページ
コメントが承認されるまで時間がかかります。