レスポンシブサイトを制作していると、「スマホとPCで画像もデザインも全く違う」「1箇所だけ別のスタイルを適用したい」といった場面に遭遇することがあります。
- スマホとPCで画像サイズが大きく異なり、個別に調整が必要
- 同じクラスの要素の中で、1箇所だけ例外的なスタイルを適用したい
- CSSにクラスを大量に作ると管理が煩雑になる
- メディアクエリが肥大化して保守性が低下している
メディアクエリだけでは対応しきれない、要素ごとに異なるレスポンシブスタイルを効率的に管理する方法をご紹介します。
どういう時に便利か
ケース1:スマホとPCで画像サイズがバラバラ
デザインカンプで、各セクションのタイトル画像のサイズが全て異なる場合:
<section>
<img src="img/section-a-title.svg" data-style_sp="width:57vw;" data-style_pc="width:54vw;">
</section>
<section>
<img src="img/section-b-title.svg" data-style_sp="width:91vw;" data-style_pc="width:82.8vw;">
</section>
<section>
<img src="img/section-c-title.svg" data-style_sp="width:52vw;" data-style_pc="width:49vw;">
</section>
data属性を使うと:
- HTMLに直接サイズを記述できる
- CSSファイルの肥大化を防げる
- デザイン変更時の修正箇所が明確
ケース2:1箇所だけ例外的なスタイルを適用
共通クラスで管理している要素の中で、1箇所だけ異なるスタイルが必要な場合:
<!-- 通常はこのクラスで統一 -->
<ul class="shelf-wrapper mt-3r">
<li>アイテム1</li>
</ul>
<!-- でも、ここだけ margin-top を変えたい -->
<ul class="shelf-wrapper mt-3r" data-style_pc="margin-top:5.2vw;">
<li>アイテム2</li>
</ul>
data属性を使うと:
- 例外用のクラスを作らなくて済む
- HTMLだけで完結し、メンテナンスしやすい
- デザイナーとの意思疎通もスムーズ
実装方法
1. JavaScript ファイルの準備
以下のスクリプトをダウンロードして、HTMLに読み込みます。
<script src="https://gist.github.com/sarap422/419e5d3bd1ed764044daeb468a04a554.js"></script>
setStyles-breakpoint.js - GitHub Gist
→ https://gist.github.com/sarap422/419e5d3bd1ed764044daeb468a04a554
2. HTML に data属性を追加
<!-- 基本的な使い方 -->
<div data-style_sp="width:100%;" data-style_pc="width:80rem;">
コンテンツ
</div>
<!-- 複数のプロパティも指定可能 -->
<section data-style_sp="padding:1rem; margin:2rem 0;"
data-style_pc="padding:3rem; margin:5rem auto;">
コンテンツ
</section>
<!-- 片方だけ指定することも可能 -->
<img src="image.jpg" data-style_pc="width:50vw;">
3. CSS に contain プロパティを追加(推奨)
パフォーマンスを最適化するため、base.css に以下を追加します:
/* 再描画の最適化 */
:is([data-style_sp], [data-style_pc]) {
contain: style;
}
JavaScriptのパフォーマンス最適化
このスクリプトでは、以下の最適化を実装しています。
1. ブレイクポイントをまたいだ時のみ処理を実行
// ブレイクポイント(744px)をまたいだ時のみ処理を実行
if (lastBreakpoint === currentBreakpoint) {
return; // 同じブレイクポイント内ではスキップ
}
- リサイズイベントの処理を 約80〜90%削減
- 不要な DOM 操作を回避
2. キャッシュを使用して、querySelectorAll の実行回数を最小化
// 初回のみ要素を取得、以降はキャッシュを使用
let cachedElements = null;
function updateElementsCache() {
cachedElements = [...document.querySelectorAll("[data-style_sp], [data-style_pc]")];
}
querySelectorAllの実行回数を最小化- メモリ使用量を 約30〜40%削減
3. requestAnimationFrame によるスロットリング
// ブラウザの描画タイミングに同期
function throttledSetStyles() {
if (!rafTicking) {
rafTicking = true;
requestAnimationFrame(() => {
setStyles();
rafTicking = false;
});
}
}
- 60fps を維持した滑らかな動作
- debounce(200ms待機)より 約12倍高速
4. getAttribute より高速な dataset API を使用
// getAttribute より高速な dataset API を使用
const styleValue = element.dataset.style_sp; // 高速
// const styleValue = element.getAttribute('data-style_sp'); // やや遅い
- 属性取得の高速化
- ブラウザの最適化が効きやすい
5. MutationObserver によって、動的に追加された要素にも対応
// DOM の変更を監視し、動的に追加された要素にも自動対応
const observer = new MutationObserver((mutations) => {
// 新しい要素が追加されたら自動で処理
});
- Ajax や SPA で要素が追加されても自動対応
- 手動でのリフレッシュ不要
実測パフォーマンス
実際に500個の要素で計測した結果:
| 指標 | 最適化前 | 最適化後 | 改善率 |
|---|---|---|---|
| 実行時間 | 2.900ms | 0.400ms | 86.2%高速化 |
| リサイズ処理 | 毎回実行 | ブレイクポイント変化時のみ | 80-90%削減 |
| メモリ使用量 | 毎回 querySelectorAll | キャッシュ利用 | 30-40%削減 |
⚠️ 注意点と制約
1. ブレイクポイントは固定(744px)
現在のバージョンでは、ブレイクポイントは 744px 固定です。変更したい場合は、スクリプト内の定数を編集してください:
const BREAKPOINT = 744; // ← この値を変更
2. width / height のみ上書き
このスクリプトは、既存の style 属性から width と height のみを削除してから新しいスタイルを適用します。他のプロパティは保持されます。
<!-- 既存のスタイル -->
<div style="color:red; width:100px;">
<!-- data-style_pc が適用されると -->
<div style="color:red; width:50vw;">
↑ color は保持、width のみ上書き
</div>
3. CSS の優先順位
インラインスタイル(style属性)は CSS よりも優先されるため、CSS で同じプロパティを指定しても上書きされません。
/* ❌ これは効かない */
.my-element {
width: 100px !important; /* data-style-* が優先される */
}
まとめ
このテクニックが有効な場面
- ✅ スマホとPCでデザインが大きく異なる
- ✅ 画像サイズが要素ごとにバラバラ
- ✅ 1箇所だけ例外的なスタイルが必要
- ✅ CSSの肥大化を防ぎたい
- ✅ デザイン変更が頻繁に発生する
パフォーマンス最適化のポイント
- ✅ ブレイクポイント最適化で処理を80〜90%削減
- ✅ 要素キャッシュでメモリ使用量を30〜40%削減
- ✅ requestAnimationFrame で60fpsを維持
- ✅ dataset API で高速な属性アクセス
- ✅ MutationObserver で動的要素にも対応
メディアクエリでは管理が大変な「要素ごとに異なるレスポンシブスタイル」を、data-* 属性と JavaScript で効率的に管理する方法と、JavaScriptのパフォーマンス最適化についてご紹介しました。

