TIPSWEB制作の備忘録

ホーム > WEB制作の備忘録 > クリックしたら開閉する機能をjsなしで作る方法

クリックしたら開閉する機能をjsなしで作る方法

公開:2025.09.07 

JavaScriptなしで、HTMLとCSSでクリックしたら開閉する機能を実装する方法

見出しをクリックしたら開閉する機能を作ります。
よくある質問のQ&Aなどで使うことが多いですね。
たとえば、「ホームページを作るのに何を用意したらいい?」のような質問をクリックすると、それの回答が開く…というイメージです。
次のような仕上がりになります。

Aのコンテンツを表示します。
Bのコンテンツを表示します。
Cのコンテンツを表示します。

使用するHTMLとCSS

では、クリックしたら開閉する機能をどのように実装しているか、HTMLとCSSを見ていきましょう。
簡単に説明すると、チェックボックスを押したらlabel要素に隣接するdiv要素を表示する、という仕組みになっています。
※このホームページでは「html」に設定されている「font-size」が10px程度の状態になっています。
※コードを利用される環境によって、単位が「rem」指定されているもののサイズが変わります。

HTML
<div class="faq-contents-item">
	<input id="faq01" type="checkbox" name="tabs" class="faq-contents-item-input">
	<label for="faq01" class="faq-contents-item-label">
		<span class="faq-contents-item-label-main">こちらはAのタイトルです</span><span class="faq-contents-item-icon"></span>
	</label>
	<div class="faq-contents-item-main">
		Aのコンテンツを表示します。
	</div>
</div>
<div class="faq-contents-item">
	<input id="faq02" type="checkbox" name="tabs" class="faq-contents-item-input">
	<label for="faq02" class="faq-contents-item-label">
		<span class="faq-contents-item-label-main">こちらはBのタイトルです</span><span class="faq-contents-item-icon"></span>
	</label>
	<div class="faq-contents-item-main">
		Bのコンテンツを表示します。
	</div>
</div>
<div class="faq-contents-item">
	<input id="faq03" type="checkbox" name="tabs" class="faq-contents-item-input">
	<label for="faq03" class="faq-contents-item-label">
		<span class="faq-contents-item-label-main">こちらはCのタイトルです</span><span class="faq-contents-item-icon"></span>
	</label>
	<div class="faq-contents-item-main">
		Cのコンテンツを表示します。
	</div>
</div>
CSS
.faq-contents-item {
  margin-bottom: 2px;
}

.faq-contents-item-label {
  display: flex;
  width: 100%;
  margin: 0;
  padding: 0;
  cursor: pointer;
  background-color: #000;
  color: #fff;
  padding: 1rem 1.4rem;
}

.faq-contents-item-label-main {
  width: calc(100% - 4rem);
}

.faq-contents-item-icon {
  width: 4rem;
  position: relative;
}
.faq-contents-item-icon::before, .faq-contents-item-icon::after {
  content: " ";
  width: 1.6rem;
  height: 1px;
  background-color: #fff;
  top: 1.3rem;
  right: 0;
  position: absolute;
  display: block;
  transition: 0.3s;
}

.faq-contents-item-main {
  transition: 0.3s;
  overflow: hidden;
  height: 0;
  opacity: 0;
  visibility: hidden;
  background-color: #fff;
  padding-right: 1.4rem;
  padding-left: 1.4rem;
}

.faq-contents-item-input {
  display: none;
}
.faq-contents-item-input:checked + .faq-contents-item-label .faq-contents-item-icon::before {
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.faq-contents-item-input:checked + .faq-contents-item-label .faq-contents-item-icon::after {
  -webkit-transform: rotate(130deg);
          transform: rotate(130deg);
}
.faq-contents-item-input:checked + .faq-contents-item-label + .faq-contents-item-main {
  height: auto;
  opacity: 1;
  visibility: visible;
  padding-top: 1rem;
  padding-bottom: 1rem;
}

HTMLの解説

「faq-contents-item」というクラスがついた要素の中に、チェックボックスの「input(faq-contents-item-inputのクラス付き)」と「label(faq-contents-item-labelのクラス付き)」を入れています。「id」と「for」が同じになっているものがセットです。
そのlabelに隣接するように、「faq-contents-item-main」というクラスが付いたdiv要素を配置しました。
「input」~「faq-contents-item-main」が1つのグループです。

★ポイント

このとき、それぞれの「input」の「id」と、inputに隣接する「label」の「for」が同じになるようにしておきます。これで、どのinputとlabelがセットになっているか関連付けています。
この開閉機能をもつコンテンツは、同じページ内に複数設置されることもあると思います。1つのinputと、それに隣接するlabelがセットです。セットになっている「id」と「for」は同じにし、ページ内にある他のセットと、idとforが被らないように注意してください。
今回のHTMLの場合、Aのコンテンツは「faq01」、Bのコンテンツは「faq02」…と変更しています。

ちなみに、「タイトルをクリックしてもコンテンツが開かない~!」ということが多々あります。そういうときは、だいたい「input」の「id」と、inputに隣接する「label」の「for」が同じセットがページ内に複数ある状態になっています。
コンテンツが開かないときは、この設定を見直してみましょう。

HTML
<input id="faq01" type="checkbox" name="tabs" class="faq-contents-item-input" checked>

最初は開いた状態で表示したいときは、「input」には「checked=”checked”」を指定します。
そうすると、これは選択されているチェックボックスだよ~ということになります。

CSSの解説

CSSが適用されていない状態のスクリーンショットがこちらです。

が「input(faq-contents-item-inputのクラス付き)」
が「label(faq-contents-item-labelのクラス付き)」
が「faq-contents-item-main」
です。
この①~③が1つのグループになっています。

CSS
.faq-contents-item {
  margin-bottom: 2px;
}

.faq-contents-item-label {
  display: flex;/* label内でタイトルとアイコンを並ばせるために指定 */
  width: 100%;
  margin: 0;
  padding: 0;
  cursor: pointer;
  background-color: #000;
  color: #fff;
  padding: 1rem 1.4rem;
}

.faq-contents-item-label-main {
  width: calc(100% - 4rem);
}
/* 開閉状態のアイコン */
.faq-contents-item-icon {
  width: 4rem;
  position: relative;
}
.faq-contents-item-icon::before, .faq-contents-item-icon::after {
  content: " ";
  width: 1.6rem;
  height: 1px;
  background-color: #fff;
  top: 1.3rem;
  right: 0;
  position: absolute;
  display: block;
  transition: 0.3s;
}

.faq-contents-item-main {
  transition: 0.3s;
  overflow: hidden;/* はみ出す要素を消す */
  height: 0;/* 高さなし */
  opacity: 0;/* 透明にする */
  visibility: hidden;/* 見えないように、中の要素を選択できないように */
  background-color: #fff;
  padding-right: 1.4rem;
  padding-left: 1.4rem;
}

まずは基本のコンテンツが閉じている状態をCSSで指定します。

label要素の「faq-contents-item-label」には「display: flex」を指定しています。これは、label内でタイトルと開閉状態のアイコンを横並びにさせるための設定です。
アイコンはいらないよ~というときなどは、「display: block」とかで大丈夫です。

「faq-contents-item-icon」で開閉状態のアイコンを作成しています。
閉じている時は「-」、開いている時は「✖」のように切り替わるようにします。

「faq-contents-item-main」はcheckedになっていないときに非表示にするため、「opacity:0」と「visivility:hidden」、更に「height:0」を指定します。さらに、「height:0」にすると中の要素がはみ出してしまうため、一応「overflow: hidden;」を指定しました。

★ポイント

「checked」になっていないときの「.faq-contents-item-main”」には、「padding-top」「padding-bottom」「margin-top」「margin-bottom」は指定しないでください。
コンテンツは非表示の状態ですが、paddingやmargin分の高さが出てしまい、謎の隙間ができてしまいます。ですので、上下のpaddingやmarginは「checked」になったときに指定してください。「padding-right」「padding-left」は高さに影響しないので、「checked」になっていなくても指定できます。

CSS
.faq-contents-item-input {
  display: none;
}

「input」はデザイン的にいらないので「display:none」を指定して消します。

CSS
.faq-contents-item-input:checked + .faq-contents-item-label .faq-contents-item-icon::before {
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.faq-contents-item-input:checked + .faq-contents-item-label .faq-contents-item-icon::after {
  -webkit-transform: rotate(130deg);
          transform: rotate(130deg);
}

次に、タイトル部分をクリックしたら開閉する部分のCSSを詳しく見ていきましょう。

inputに付けたクラス「.tab-contents-input」に「:checked」を追加して、チェックボックスが選択された状態になったとき、という指定をします。
これを利用して、チェックボックスが選択されているときのボタンの見た目を変更します。

ボタンとして利用するのがlabel要素になるため、チェックボックスが選択されたときのlabel要素に対してCSSが適用されるようにする指定が、「.faq-contents-item-input:checked + .faq-contents-item-label」部分ですね。
「+」の記号は、「Aの要素に隣接する要素B」のように、どの要素同士が隣り合う状態になっているか指定できます。

このコードは、チェックボックスが選択されているとき、「:checked」が付いたinput要素に隣接するlabel要素内にある「.faq-contents-item-icon」の擬似要素に対しての指定です。
視覚的に開いているコンテンツがわかりやすいように、選択されたコンテンツのアイコンの表示が変わるようにしています。

CSS
.faq-contents-item-input:checked + .faq-contents-item-label + .faq-contents-item-main {
  height: auto;
  opacity: 1;
  visibility: visible;
  padding-top: 1rem;
  padding-bottom: 1rem;
}

チェックボックスが選択されたらコンテンツを開く指定が、
「.faq-contents-item-input:checked + .faq-contents-item-label + .faq-contents-item-main」
の部分です。
先ほど指定したlabel要素に隣接する「.faq-contents-item-main」と指定して、チェックボックスが選択されたときに「.faq-contents-item-main」を表示するよう指定しました。

最後に、このHTMLとCSSで作った開閉機能がこちら

Aのコンテンツを表示します。
Bのコンテンツを表示します。
Cのコンテンツを表示します。

以上がjsを使用せずに、コンテンツの開閉機能を実装する方法です。

今回のまとめ

  • jsなしで開閉機能を実装するために、チェックボックスを利用する
  • ①「input」②「label」③「開閉するコンテンツ」の順番で隣り合わせに配置する
    ※これで1つのグループになる
  • ①「input」の「id」と②「label」の「for」は同じものを入力する
    ※これで1セット
    他のセットと「id」と「for」が被らないようにする
  • 「input」要素はデザイン的に不要なので「display:none」を指定
  • ③「開閉するコンテンツ」は、初期状態で非表示になるよう「opacity:0」「visivility:hidden」「height:0」を指定する
  • 最初に表示しておくコンテンツの①「input」には、「checked=”checked”」を指定する
  • チェックボックスがチェックされたときのCSS指定は、①「input」要素に「:checked」を付ける
  • 「:checked」がついた①「input」要素に隣接する②「label」、更にその要素に隣接する③「開閉するコンテンツ」と指定して、コンテンツの開閉を実装する

私はjsを利用するのが億劫な人間なので、この方法をよく利用します。
ですが、開閉させるコンテンツが多すぎるとidとforを揃えるのが面倒になってきます。そういうときはjsを使ってクラスの着脱をした方が早いかもしれませんね。

ちなみに、ラジオボタンを利用して似たような機能を作る方法もあります。ぜひご覧ください。