第8回: 実践!よく使うWebコンポーネント実装
この回の目標
- ハンバーガーメニューを実装できる
- アコーディオンやスライダーを組み込める
- Webサイトで即使えるパーツを自分で調整できる
- HTML / CSS / JSの連携が理解できている
コンポーネントとは?
コンポーネントは「再利用可能なパーツ」
コンポーネントとは、特定の機能や見た目を持った再利用可能なパーツのことです。
ボタンや入力フォーム、ナビゲーションバーを「パーツ(コンポーネント)」として独立させて作ることで、色々な画面で使い回せるようにすします。
特徴
-
再利用できる:
一度「かっこいいボタン」を作っておけば、他の画面で使いたい時にまた1からコードを書かなくて済む -
メンテナンス性:
例えば「サイト全体のボタンの色を青から赤に変えたい」ってなった時、コンポーネント化されていれば、そのパーツ1箇所を直すだけで全ての画面に反映される -
コードが読みやすくなる:
パーツごとに適切な設計を行うことで「ここはヘッダー」「ここはカード」って一目で理解しやすくなる
今回作るコンポーネント
- ハンバーガーメニュー
- アコーディオンメニュー
- スライダー
Web制作でよく使われるパーツ3つを実装してみましょう。
ハンバーガーメニューを実装してみよう
完成イメージ
PC時
サイトロゴとメニューを横並び(左右配置)で表示
SP時
横3本の線(ハンバーガーメニュー)をクリックするとメニューが表示される
もう一度クリックするとメニューが閉じる
「≡」をクリック
「×」をクリック
コード
HTML
<header class="site-header">
<div class="header-container">
<div class="site-logo">
<a href="#">ロゴ</a>
</div>
<nav class="nav-menu" id="js-nav-menu">
<ul class="nav-list">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<div class="hamburger" id="js-hamburger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</header>
<header>タグを使って、その中にロゴ・ナビゲーション・ハンバーガーメニューパーツを入れる。
CSS
/* --- 共通・PC向けスタイル --- */
.site-header {
width: 100%;
background-color: #fff;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: fixed; /* ヘッダーを上部に固定 */
top: 0;
left: 0;
z-index: 1000;
}
.header-container {
max-width: 1200px; /* コンテンツの最大幅 */
margin: 0 auto; /* 中央寄せ */
padding: 30px 20px;
display: flex;
justify-content: space-between; /* 両端に配置 */
align-items: center; /* 上下中央寄せ */
}
.hamburger {
display: none; /* PCでは隠す */
}
.nav-list {
display: flex; /* 横並び */
gap: 30px;
}
.nav-list li {
list-style: none;
}
.nav-list li a {
text-decoration: none;
color: #666;
}
/* --- スマホ向けスタイル(画面幅768px以下) --- */
@media screen and (max-width: 768px) {
.header-container {
padding: 15px 20px;
}
.hamburger {
display: block; /* ボタンを表示 */
position: relative; /* 位置固定を解除 */
width: 30px;
z-index: 100;
}
/* ハンバーガー線の調整 */
.hamburger span {
display: block;
width: 100%;
height: 2px;
background-color: #1e90ff;
transition: all 0.3s;
}
.hamburger span:not(:last-child) {
margin-bottom: 6px;
}
.hamburger.is-active span:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
.hamburger.is-active span:nth-child(2) {
opacity: 0;
}
.hamburger.is-active span:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
/* メニューのスタイル(全画面スライド) */
.nav-menu {
position: fixed;
top: 0;
right: -100%;
width: 100%;
height: 100vh;
background-color: #e0ffff;
transition: all 0.3s;
display: flex;
justify-content: center;
align-items: center;
z-index: 90; /* ロゴとボタンより下に配置 */
}
.nav-list {
flex-direction: column; /* 縦並び */
gap: 20px;
text-align: center;
}
.nav-menu.is-active {
right: 0;
}
}
Flexboxによるレイアウト(PC・SP共通)
-
display: flex;とjustify-content: space-between;
これを使うことで、左側に「ロゴ」、右側に「メニュー」を自動で振り分けている.header-container { max-width: 1200px; /* コンテンツの最大幅 */ margin: 0 auto; /* 中央寄せ */ padding: 30px 20px; display: flex; justify-content: space-between; /* 両端に配置 */ align-items: center; /* 上下中央寄せ */ } -
position: fixed;
画面をスクロールしてもヘッダーが一番上にずっと残るように固定している.site-header { width: 100%; background-color: #fff; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); position: fixed; /* ヘッダーを上部に固定 */ top: 0; left: 0; z-index: 1000; }
ハンバーガーボタンのアニメーション(スマホ)
-
span:nth-child()とtransform
3本の線のうち、1番目を45度、3番目をマイナス45度回転させて、2番目を opacity: 0 で消すことで「三」から「×」に変化させている.hamburger.is-active span:nth-child(1) { transform: translateY(8px) rotate(45deg); } .hamburger.is-active span:nth-child(2) { opacity: 0; } .hamburger.is-active span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); } -
transition: all 0.3s;
これがあるおかげで、パッと切り替わるのではなくて滑らかに形が変わるアニメーションになる.hamburger span { display: block; width: 100%; height: 2px; background-color: #1e90ff; transition: all 0.3s; }
メディアクエリとメニューのスライド(スマホ)
-
@media screen and (max-width: 768px)
「画面幅が768px以下の時だけこのスタイルを適用する」という命令で、これでPCとスマホの出し分けをしている -
right: -100%;からright: 0;へ
メニュー本体は最初、画面の右外側(-100%)に隠れている
ボタンが押されて.is-activeがついた瞬間に、画面内にスッと滑り込んでくる(right: 0)仕組み.nav-menu { position: fixed; top: 0; right: -100%; width: 100%; height: 100vh; background-color: #e0ffff; transition: all 0.3s; display: flex; justify-content: center; align-items: center; z-index: 90; /* ロゴとボタンより下に配置 */ } .nav-menu.is-active { right: 0; }
JavaScript
const btn = document.getElementById('js-hamburger');
const nav = document.getElementById('js-nav-menu');
btn.addEventListener('click', () => {
btn.classList.toggle('is-active');
nav.classList.toggle('is-active');
});
1. 要素を見つける(取得)
document.getElementByIdを使って、HTML側で付けておいたid="js-hamburger"(ボタン)とid="js-nav-menu"(メニュー本体)を探し出して、変数(btn と nav)に格納する
2. 動きを待ち構える(イベントリスナー)
-
btn.addEventListener('click', () => { ... });は「ボタンがクリックされたら、{ } の中の命令を実行してね!」という機能
ユーザーがアクションを起こすまで、JSはじっと待機している状態
3. クラスを入れ替える(トグル処理)
-
toggle(トグル)というのは、スイッチのON/OFFのようなもの
.is-activeクラスがついていない時: クラスを追加する(ON)
.is-activeクラスがついている時: クラスを削除する(OFF)
ポイント
クラスの付け外しをJSでやって、見た目の変化は全部CSSで制御する
アコーディオンメニューを実装してみよう
完成イメージ
再利用ができること、修正が楽になること、そしてコードが読みやすくなることです。
FAQ(よくある質問)や、スマホで情報量が多いページをスッキリ見せたい時によく使われます。
コード
HTML
<div class="accordion">
<div class="accordion-item">
<button class="accordion-header js-accordion-trigger">
Q1. コンポーネント化のメリットは何ですか?
</button>
<div class="accordion-content">
<p>再利用ができること、修正が楽になること、そしてコードが読みやすくなることです。</p>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header js-accordion-trigger">
Q2. どんな時にアコーディオンを使いますか?
</button>
<div class="accordion-content">
<p>FAQ(よくある質問)や、スマホで情報量が多いページをスッキリ見せたい時によく使われます。</p>
</div>
</div>
</div>
「質問」と「回答」を1つのセット parentNode(親要素)を辿って簡単に操作できるようにしている
CSS
/* --- アコーディオン全体 --- */
.accordion {
width: 100%;
margin: 20px auto;
border-top: 1px solid #ddd;
}
/* 各アイテムの区切り線 */
.accordion-item {
border-bottom: 1px solid #ddd;
}
/* クリックするヘッダー部分 */
.accordion-header {
width: 100%;
padding: 20px;
text-align: left;
background: none;
border: none;
font-size: 16px;
font-weight: bold;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
position: relative; /* 疑似要素の基準点 */
}
/* +アイコンの共通スタイル(横線と縦線) */
.accordion-header::before,
.accordion-header::after {
content: '';
position: absolute;
right: 20px;
width: 15px; /* 線の長さ */
height: 2px; /* 線の太さ */
background-color: #333;
transition: all 0.3s; /* アニメーションの速度 */
}
/* 縦線:90度回転させて「+」を作る */
.accordion-header::after {
transform: rotate(90deg);
}
/* --- 開閉の中身(重要:高さのアニメーション) --- */
.accordion-content {
display: grid;
grid-template-rows: 0fr; /* 初期状態は高さを0にする */
transition: grid-template-rows 0.3s;
overflow: hidden;
}
.accordion-content p {
min-height: 0; /* gridでのアニメーションを正しく動かすための記述 */
padding: 0 20px;
}
/* --- 開いた時(.is-open クラスがついた時)のスタイル --- */
/* 縦線を0度に戻して横線と重ねる(=マイナスに見える) */
.accordion-item.is-open .accordion-header::after {
transform: rotate(0deg);
}
/* 中身の表示:高さを 1fr(中身に合わせる)に変更 */
.accordion-item.is-open .accordion-content {
grid-template-rows: 1fr;
padding-bottom: 20px;
}
疑似要素で作る「+」と「ー」
- 疑似要素とは、HTMLソースに手を加えることなくCSSで特定の要素の一部や仮想的な要素を追加・装飾できる機能
- 画像を使わずに、CSSの
::before(横線)と::after(縦線)だけでアイコンを作っている -
開いた時に
transform: rotate(0deg);で縦線を横線と全く同じ向きに重ねることで、消えたように見せて「ー」を表現する/* +アイコンの共通スタイル(横線と縦線) */ .accordion-header::before, .accordion-header::after { content: ''; /* 必ずcontentを指定 */ position: absolute; right: 20px; width: 15px; /* 線の長さ */ height: 2px; /* 線の太さ */ background-color: #333; transition: all 0.3s; /* アニメーションの速度 */ } /* 縦線:90度回転させて「+」を作る */ .accordion-header::after { transform: rotate(90deg); } /* 縦線を0度に戻して横線と重ねる(=マイナスに見える) */ .accordion-item.is-open .accordion-header::after { transform: rotate(0deg); }
高さのアニメーション手法
-
display: grid;とgrid-template-rowsを使うことで「中身の量に関わらず、高さを0から1倍(1fr)まで滑らかに変化させる」 ことができる
状態の管理(is-open)
.is-openという「状態を表すクラス」を1つ付け外しするだけで、アイコンの形も中身の表示も一気に切り替える
JavaScript
const triggers = document.querySelectorAll('.js-accordion-trigger');
triggers.forEach(trigger => {
trigger.addEventListener('click', () => {
// 親要素(accordion-item)を取得してクラスを切り替える
const parent = trigger.parentNode;
parent?.classList.toggle('is-open');
});
});
1. 操作したい要素を「全部」つかまえる
-
querySelectorAll:
ページ内にある.js-accordion-triggerというクラスがついた要素を、全部まとめて持ってくる -
getElementByIdは1つしか捕まえられないけど、querySelectorAllとクラス(.js-accordion-trigger)を使えば、ページ内にアコーディオンが何個あっても、この1通りのコードだけで全部動くようになる
2. 一つひとつのボタンに「見張り役」をつける
-
forEach:
「全部まとめて」持ってきたボタンたちを、1つずつ順番に処理する(ループ処理) -
addEventListener('click', ...):
それぞれのボタンに対して、「クリックされたら、この中の命令を実行してね!」という見張り役(イベントリスナー)をセット
3. 親要素の「状態(クラス)」を切り替える
-
parentNode:
クリックされたボタンのすぐ外側にある親要素(.accordion-item)を探しに行く -
classList.toggle('is-open'):
.is-openというクラスが「なければ付ける、あれば外す」という切り替え(トグル)をやる
実はHTML/CSSだけでも作れる!
完成イメージ
Q1. detailsタグのメリットは?
JSが不要で、HTMLだけで完結するのが最大の魅力だね!
コード
<div class="html-accordion">
<details class="html-accordion-item">
<summary class="html-accordion-header">
Q1. detailsタグのメリットは?
</summary>
<div class="html-accordion-content">
<p>JSが不要で、HTMLだけで完結するのが最大の魅力だね!</p>
</div>
</details>
</div>
<details> :折りたたみの「親玉」
- これがアコーディオン全体の「箱」になる
- クリックされると、ブラウザが自動的に open という属性を付け外ししてくれる
<summary> :クリックする「見出し」
detailsの中の一番上に書くことで、常に表示される「タイトル」になる- ここをクリックすると、箱が開いたり閉じたりするスイッチの役割を果たす
隠される「コンテンツ」
summaryより下に書いた要素は、すべて「開いた時だけ見える中身」になる-
divで囲むことで、余白をつけたりなどCSSの調整がしやすくなる
/* アイテム全体の枠線(薄いグレー) */
.html-accordion-item {
border-bottom: 1px solid #eee;
}
/* --- 【通常時】の見出し --- */
.html-accordion-header {
padding: 20px;
font-weight: bold;
cursor: pointer;
list-style: none;
background: #f9f9f9;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 擬似要素で「+」を作る */
.html-accordion-header::after {
content: '+';
font-size: 1.2rem;
color: #1e90ff;
/* +だけ青くして目立たせる */
}
/* 「+」を「ー」に変える */
.html-accordion-item[open] .html-accordion-header::after {
content: 'ー';
}
/* 中身の余白とデザイン */
.html-accordion-content {
padding: 20px 20px;
}
/* デフォルトの矢印を消す */
.html-accordion-header::-webkit-details-marker {
display: none;
}
details[open]というセレクタを使うことで、「開いている時だけ」のスタイルを指定できるのが最大の特徴detailsタグはパッと切り替わるのがデフォルト
JavaScript方式とdetailsタグ方式の比較
JavaScript方式
- 難易度:少し高い(JSの知識が必要)
- 自由度:複雑な動きも自由自在
- 推奨シーン:高度な演出や、特殊な連動をさせたい時
detailsタグ方式
- 難易度:かんたん(HTMLタグだけ)
- 自由度:シンプルな開閉がメイン
- 推奨シーン:FAQなど、標準的なアコーディオンを作る時
スライダーを実装してみよう
完成イメージ
スライダーの実装は「ライブラリ」を使う
実務では「Swiper(スワイパー)」というライブラリ(読み込んで使える便利なコード部品)を使用
なぜライブラリを使う?
スライダーをゼロから作るのは、実はとても大変
- スワイプ・フリック操作に対応させるのが難しい
- 無限ループさせようとするとバグりやすい
既に完成されたパーツ(ライブラリ)を借りてくることで、品質も安定し開発スピードも上がる
Swiperを使うまでの流れ
① ライブラリを読み込む
② HTMLを書く
③ JavaScriptで初期化
④ CSSで見た目を調整する
① ライブラリを読み込む
CDN読み込み
Swiperを使うため、今回は CDN を使用して CSS と JS の2つのコードを読み込む
CDNは「ネット上にあるお店から、必要なパーツをレンタルしてくるURL」というイメージ
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
- CSS(見た目): <head> タグの中に貼る
- JS(動き): </body> の直前に貼る
② HTMLを書く
HTML
<div class="swiper js-008swiper">
<div class="swiper-wrapper">
<div class="swiper-slide slide01">スライド1</div>
<div class="swiper-slide slide02">スライド2</div>
<div class="swiper-slide slide03">スライド3</div>
</div>
<!-- ナビゲーション -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- ページネーション -->
<div class="swiper-pagination"></div>
</div>
Swiperには「この順番でタグを書いてね」という厳格なルールがある
- .swiper → スライダー全体
- .swiper-wrapper → スライドの親
- .swiper-slide → 1枚ずつ
上記はSwiperが動くために変えてはいけないクラス、最低限この3つが必須
.js-008swiper は、自分たちがJSで操作するための目印なので変えてもOK
③ JavaScriptで初期化
JavaScript
Swiperを使用するためには、初期化が必要
const swiper = new Swiper(".js-008swiper", { ... });で定義する
const swiper = new Swiper(".js-008swiper", {
loop: true, // 最後まで行ったら最初に戻る(無限ループ)
// ナビゲーションボタン
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// ページネーション
pagination: {
el: ".swiper-pagination", // 表示する場所を指定
clickable: true, // ドットをクリックして飛ばせるようにする
},
slidesPerView: 1, // スライドの枚数
});
よく使う基本オプション
const swiper = new Swiper(".swiper", {
loop: true, // 無限ループ
// 自動再生
autoplay: {
delay: 3000, // 3秒ごとに次のスライドへ
},
speed: 800, // スライド間の遷移時間(ミリ秒)
slidesPerView: 1, // 表示されるスライドの数
spaceBetween: 20, // スライド間の余白
// 画面幅によって枚数を変える(レスポンシブ)
breakpoints: {
768: { // 768px以上の場合
slidesPerView: 2,
},
},
});
オプションを増やす際のポイント
- オプションを増やす時は、前の設定の最後に必ずカンマ( , )を付ける
- オプションを全部暗記するのは難しいので、必要に応じてコードを調べて使う
④ CSSで見た目を調整する
Swiperのデフォルトのデザインを、CSSで「上書き」していく
ライブラリが用意してくれている「決まったクラス名」を狙い撃ちして、自分好みにアレンジする
左右の「矢印(ナビゲーション)」の色を変える
/* 矢印のデザインを変える */
.swiper-button-prev,
.swiper-button-next {
background-color: #fff;
color: #ff6347;
border-radius: 50%;
box-sizing: border-box;
padding: 13px;
}
下の「ドット(ページネーション)」の色を変える
/* ドット全体のベース色 */
.swiper-pagination-bullet {
background: #fff;
opacity: 1;
transition: all .3s;
}
/* 「今表示されているスライド」のドット色 */
.swiper-pagination-bullet-active {
background: #ff6347;
width: 20px;
border-radius: 4px;
}
-active がついているのが、今表示されているスライド
CSSで調整する際のポイント
- Swiperがどんなクラス名を使っているかは、検証ツールで確認できるので探してみる
-
Swiperの元のデザインが強すぎて色が変わらない時は、クラス名を
.js-008swiper .swiper-button-nextのように、自分の付けたクラス名を頭に添えて『より詳しく』指定してあげる
Webサイトでの使いどころ
ハンバーガーメニュー
- スマホサイトのメインナビゲーション
- ほぼ必須、どのサイトでも使われている
アコーディオンメニュー
- FAQ(よくある質問)や、製品の仕様書などの補足情報の整理
- 情報量は多いけど、最初から全部見せるとユーザーが疲れてしまうような場所に最適
スライダー
- メインビジュアルや、実績・商品の紹介
- 限られたスペースで大量の情報(写真など)を効率よく見せられる
ミニ課題
課題①:ハンバーガーのメニュー色を変えてみよう
- 背景を自分の好きな色に、文字を白に変えてみよう
- 画面の「右から」ではなく「上から」メニューが降ってくるように、CSSの top や right の数値をいじってみよう
課題②:アコーディオンに「矢印」をつけてみよう
- + ではなく、下向き矢印 v が回転して上向きになるように transform: rotate() を調整してみよう
課題③:Swiperを「2枚表示」にしてみよう
- slidesPerView を使って、PCの時だけスライドが2枚見えるように設定してみよう
この回のまとめ
-
コンポーネント = 再利用できる部品(1つ作れば一生モノ)
今日作ったコードは、これからの制作で何度でも使い回せる武器(コンポーネント庫)になる -
JSでクラスをつけて、見た目はCSSで調整する
役割を完全に分けることで、エラーが起きた際や修正する際にメンテナンスしやすい -
必要に応じてライブラリを使用する
簡単なものは自分で作り(アコーディオン)、複雑なものはプロの道具を借りる(Swiper)