古いブラウザ対応の基本とベストプラクティス:CSS・JavaScriptのフォールバック完全ガイド
古いブラウザへの対応は今でも必要なのか?
現在のWeb開発において、古いブラウザへの対応はそれほど神経質になる必要はありません。Internet Explorer 8が姿を消して以降、主要ブラウザの品質は大きく向上しています。
とはいえ、次のような疑問は残ります。Internet Explorer 9などの旧ブラウザにはどう対応すべきか?そもそもIE9をサポートする価値はあるのか?
本記事では、検討すべきポイントを順番に見ていきます。
「ブラウザ」ではなく「機能」で考える
ここで、世界に機能が2つ、ブラウザが2つしか存在しないと仮定してみましょう。
- ブラウザAは機能Aに対応しているが、機能Bには非対応。
- ブラウザBは機能Bに対応しているが、機能Aには非対応。
この場合、各ブラウザがどの機能に対応しているかを判別して、処理を分岐させることができます。
// JavaScript の例
if (Browser A) { // A用のコード }
if (Browser B) { // B用のコード }
しかし、ブラウザCやD、Eも存在する世界ではどうでしょう?ブラウザ単位で考えていると、必要な機能のサポートはどんどん難しくなっていきます。
もっと良い方法があります。それは「機能が存在するかどうかをチェックする」というアプローチです。機能があれば使えばいいし、なければフォールバックコードを用意すればよいのです。
// JavaScript の例
if (feature A) { // 機能Aがある場合のコード } else { // 機能Aがない場合のコード }
こうすれば、ブラウザの種類を気にする必要はまったくありません。この手法はフィーチャーディテクション(機能検出)と呼ばれ、モダンなWeb開発の基本原則となっています。
機能を採用するかどうかの判断基準
多くの人は、「何種類のブラウザがその機能に対応しているか」という数だけで採用を判断します。しかし前述の通り、ブラウザそのものは重要ではありません。
本当に重要なのは、その機能のフォールバックを簡単に書けるかどうかです。
- フォールバックが簡単に書けるなら → 遠慮なくその機能を使う
- フォールバックが難しいなら → 使わない
これだけのシンプルな判断基準で十分です。
どのブラウザをサポートするか決める
とはいえ、どこかで線引きは必要です。どのブラウザをサポートし、どのブラウザを切り捨てるのか。サポート対象外にするなら、そのためのフォールバックコードを書く意味はありません。
最も確実な答えは、自分のサイトを訪れるユーザーの実データを見ることです。アクセス解析で、実際にどんなブラウザが使われているかを確認しましょう。
もちろん、Internet Explorer 6でアクセスしてくる少数派も存在するかもしれません。しかし、ほぼ誰も使っていないブラウザのために追加のコードを書く時間とエネルギーがあるでしょうか?その労力は、別の場所に注ぐ方が価値的ではないでしょうか。
サポートレベルの4段階
サポートには次の4つのレベルがあると考えられます。
- すべてのブラウザで見た目も動作も完全に同じにする
- 見た目は同じだが、機能はブラウザごとに異なってもよい
- 機能は同じだが、見た目はブラウザごとに異なってもよい
- 見た目も機能も、ブラウザごとに異なってもよい
あなたは古いブラウザに対してどのレベルのサポートを提供していますか?そして、その理由は何ですか?
CSS編:古いブラウザへの対応方法
CSSの機能にフォールバックを用意する方法は、主に2つあります。
- プロパティフォールバック(代替宣言)
- フィーチャークエリ(
@supports)
プロパティフォールバックの仕組み
ブラウザが認識できないプロパティや値は、丸ごと無視されます。その結果、ブラウザは直前に宣言された値――つまりフォールバック値――を採用します。
これが最も簡単なフォールバックの方法です。例を見てみましょう。
.layout {
display: block;
display: grid;
}
CSS Gridに対応したブラウザは display: grid を適用し、非対応のブラウザは display: block へフォールバックします。
デフォルト値を省略するテクニック
要素のデフォルトが display: block である場合、その宣言自体を省略できます。つまり、たった1行でCSS Grid対応を実現できるのです。
.layout {
display: grid;
}
CSS Grid対応ブラウザは grid-template-columns などの他のGrid関連プロパティを読み込みますが、非対応ブラウザは読みません。したがって、追加のGridプロパティもフォールバック値を気にせず書けます。
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1em;
}
フィーチャークエリ(@supports)の使い方
フィーチャークエリ(@supports)を使うと、ブラウザが特定のCSSプロパティや値に対応しているかどうかを判定できます。JavaScriptの if/else 文のようなものだと考えると分かりやすいでしょう。
@supports (property: value) {
/* プロパティや値がサポートされている場合のコード */
}
@supports not (property: value) {
/* サポートされていない場合のコード */
}
@supports は、特定のプロパティに対応しているブラウザだけにCSSを読ませたいときに役立ちます。先ほどのCSS Gridの例なら、次のように書けます。
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1em;
padding-left: 1em;
padding-right: 1em;
}
}
この例では、padding-left と padding-right は、@supports とCSS Gridの両方に対応したブラウザだけが読み込みます。
Jen Simmons氏は @supports のより優れた使用例を紹介しています。彼女はフィーチャークエリを使って、-webkit-initial-letter のようなプロパティへの対応を検出しています。
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
p::first-letter {
-webkit-initial-letter: 4;
initial-letter: 4;
color: #FE742F;
font-weight: bold;
margin-right: 0.5em;
}
}

この例から、「サイトはすべてのブラウザで同じ見た目にすべきか?」という疑問が浮かびます。これは後ほど取り上げますが、まずはフィーチャークエリについてもう少し詳しく見てみましょう。
フィーチャークエリのブラウザ対応状況
@supports は現在、主要なモダンブラウザすべてで利用可能です。安心して使える機能といえます。

「機能は対応済みだが、フィーチャークエリが使えない」ケース
かつてこれは厄介な問題でした。Jen Simmons氏ら専門家も、この落とし穴について警告していました。
私の見解はこうです。私はIE11をもうサポートしていないので、先述の方法で素直にフィーチャークエリを使っています。サポート方針によって判断が変わる、好例といえるでしょう。
プロパティフォールバックと@supportsを併用するときの注意点
次のコードを見てください。ブラウザはどちらのpadding値を適用するでしょうか?
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1em;
padding-left: 1em;
padding-right: 1em;
}
}
.layout {
padding-left: 2em;
padding-right: 2em;
}
答えは、すべてのブラウザで左右のpaddingは2emになります。
なぜなら、padding-left: 2em と padding-right: 2em がCSSファイル内で後から宣言されており、CSSのカスケードの仕様上、後から宣言されたものが前のものを上書きするからです。
CSS Grid非対応のブラウザのみに2emのpaddingを適用したい場合は、宣言の順序を入れ替えます。
.layout {
padding-left: 2em;
padding-right: 2em;
}
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1em;
padding-left: 1em;
padding-right: 1em;
}
}
注意: CSSはカスケードする性質を持つため、フォールバックコードを必ず先に宣言するのが良い習慣です。
同様に、@supports と @supports not を両方使う場合は、@supports not を先に書くと一貫性が保てます。
/* @supports not を使う場合は常に先に書く */
@supports not (display: grid) {
.layout {
padding-left: 2em;
padding-right: 2em;
}
}
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1em;
padding-left: 1em;
padding-right: 1em;
}
}
サイトは全ブラウザで同じ見た目にすべきか?
「サイトはどのブラウザでも同じ見た目であるべき」と考える人がいます。ブランディングは重要であり、ブランドイメージを守るために見た目の一貫性が必要だという主張です。
一方で、「そうではない」という人もいます。プログレッシブエンハンスメント(段階的拡張)の精神を受け入れ、より優れたブラウザのユーザーにより良い体験を提供すべきだと考えるのです。
実は両者の主張は正しく、見ている角度が違うだけです。
最も重要な視点は、ユーザー側にあります。あなたのサイトは、ユーザーが求めてきたものを提供できていますか?
提供できているのであれば、見た目の厳密な一致にこだわりすぎる必要はありません。むしろ、優れたブラウザを使うユーザーへさらに素晴らしい体験を届けましょう!
JavaScript編:ポリフィルによる古いブラウザ対応
JavaScriptの機能を古いブラウザで動かすのは意外と簡単です。多くの場合、ポリフィル(polyfill)を使えば解決します。ただし、それ以外にもできることはあります。
ポリフィルとは何か?
ポリフィルとは、ブラウザに対して「あるJavaScript機能をどう実装するか」を教えるコード断片です。一度ポリフィルを追加すれば、対応状況を心配する必要はなくなります。動くようになるのです。
ポリフィルの動作原理はシンプルです。
- その機能がサポートされているかチェックする
- 未対応であれば、機能を実現するコードを追加する
以下はMDNで公開されている実際のポリフィル例です。Array.prototype.find が未対応の場合、その実装方法をブラウザに教えます。
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
// 1. O = ? ToObject(this value)
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. len = ? ToLength(? Get(O, "length"))
var len = o.length >>> 0;
// 3. IsCallable(predicate) が false なら TypeError を投げる
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. thisArg が渡されたら T = thisArg、そうでなければ undefined
var thisArg = arguments[1];
// 5. k = 0
var k = 0;
// 6. k < len の間繰り返す
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
k++;
}
// 7. undefined を返す
return undefined;
},
configurable: true,
writable: true
});
}
補足: ポリフィルはシム(shim)の一部です。シムとは、古い環境に新しいAPIをもたらすライブラリの総称です。
ポリフィルの2つの使い方
ポリフィルの導入方法には、次の2つがあります。
- 手動で個別のポリフィルを追加する(上記の例のように)
- ライブラリ経由でまとめて多数のポリフィルを追加する
手動でポリフィルを追加する
まずは必要なポリフィルを検索します。Googleで調べれば、大抵のものは見つかるはずです。賢い開発者たちが、必要になり得るほぼすべてのポリフィルを作成済みだからです。
見つかったら、上記の手順に従って古いブラウザ向けのサポートを実装します。
ライブラリでまとめて追加する
多数のポリフィルを含むライブラリも存在します。「ES6-shim」はその代表例で、古いブラウザ上でES6の全機能を利用可能にしてくれます。
最新のJavaScript機能を使いたいときはBabel
最先端のJavaScript機能を使いたい場合は、ビルドプロセスにBabelを組み込むことを検討しましょう。
BabelはJavaScriptをコンパイルするツールで、コンパイル時に次のことができます。
- 必要なシム/ポリフィルを自動的に追加する
- プリプロセッサをJavaScriptへトランスパイルする
2点目について補足すると、Babelはビルドプロセス内でオフライン動作します。渡されたファイルを読み込み、ブラウザが理解できるJavaScriptへ変換します。
つまり、FlowやTypeScriptなど、話題の最新技術もすべて使えます。Babelを通せば、どんな環境のブラウザでも動くのです!
ポリフィルでも足りないときは?
ポリフィルだけでは対応できない機能の場合、そのブラウザへのサポートレベルを見直すべきかもしれません。
- 本当にすべてのブラウザで同じ機能を提供する必要があるか?
- プログレッシブエンハンスメントへの転換を検討できないか?
- その機能を使わない設計にできるか?
選択肢はいくつもあります。要は、柔軟に考えるということです。
ブラウザの機能対応状況の調べ方
まずは caniuse.com を確認しましょう。調べたいJavaScript機能の名前を入力すれば、ブラウザごとの対応状況が一目瞭然です。
下図は「AbortController」の例です。

caniuse.comで情報が見つからない場合は、MDN を参照します。ほとんどの記事の下部にブラウザ対応表が記載されています。
こちらもAbortControllerの例です。

JavaScriptのコストに注意する
ポリフィルを使うということは、JavaScriptのコード量が増えるということです。
JavaScriptが増えると、問題も増えます。
- 古いブラウザは古いコンピュータ上で動いていることが多く、処理能力が不足している可能性がある
- JavaScriptのバンドルサイズ増大はページ読み込みを遅延させる。詳しくはAddy Osmani氏の「The cost of JavaScript」を参照
場合によっては、その機能を思い切って諦めるのも賢明な判断です。
なぜ古いブラウザをサポートするのか?
ここで少し立ち止まって、根本的な問いを考えてみましょう。
古いブラウザを使っているのは誰か?おそらく、古いコンピュータを使っているユーザーでしょう。古いコンピュータを使っているなら、新しいものを買う余裕がないのかもしれません。買う余裕がないなら、あなたの商品も買わないだろう。買わないなら、なぜその人のブラウザをサポートする必要があるのか?
ビジネスパーソンとしては、極めて理にかなった思考です。では、なぜ私たち開発者は古いブラウザのサポートにこだわるのでしょうか?
前提を分解してみる
先ほどの思考の流れには、多くの仮定が積み重なっています。
「古いブラウザを使う人は古いコンピュータを使っている。古いコンピュータを使っているなら、新しいものを買うお金がないのだろう」――古いコンピュータを使う人が古いブラウザを使うのは事実ですが、新しいものを買えないと決めつけることはできません。
- 会社が新しいPCを購入してくれないのかもしれない
- 今のPCに満足していて、アップグレードする気が無いのかもしれない
- アップグレードの方法を知らないのかもしれない
- 新しいコンピュータにアクセスできない環境にいるのかもしれない
- まともなブラウザを持たないモバイル端末しか使えないのかもしれない
決めつけてはいけないのです。
では、「買わないユーザーのためにサポートする意味はない」という論理についてはどうでしょうか?この議論を深めるために、視点を広げてみましょう。
車椅子アクセシビリティから学ぶ
シンガポールを訪れたことがあれば、ほぼすべての階段のそばにスロープかエレベーターが設置されていることに気づくでしょう。
なぜでしょうか?政府や民間企業は、なぜエレベーターやスロープにお金をかけるのでしょうか?階段さえあれば高低差の移動はできるのに、なぜ建てるのでしょうか?
答えは簡単です。階段を使えない人たちがいるからです。自分の足で歩けず、車椅子で階段を上がることもできない人たちのために、エレベーターとスロープは存在します。
さらに重要なのは、エレベーターやスロープの恩恵を受ける人がそれだけではないという点です。
- 膝が弱っている人
- 自転車やキックボードを押している人
- ベビーカーを押す親御さん
車輪の付いたものを押しているなら、迷わずスロープやエレベーターを使うでしょう。あなた自身も恩恵を受けているのです。
ところが問題は、スロープやエレベーターの運営では誰も1セントも稼げないという点です。それでもなぜ建てるのか?
それには価値があるからです。そして「価値」は、必ずしも金銭を意味しません。
地球温暖化という問題設定
あなたは地球に住んでいます。地球温暖化についてどう感じますか?
気にしない人もいます。森が焼かれても構わない、企業が川を汚し大量の二酸化炭素を排出しても関係ない、と。
しかし、気にかける人たちもいます。私たちが暮らすこの惑星を愛し、子どもたちにより良い場所を残したいと思う人たちです。彼らにはさまざまな理由があり、できる限り資源を守りたいと考えています。
あなたはどちら側ですか?運営の中で地球を破壊する企業にお金を払いますか?
払うかもしれないし、払わないかもしれない。そもそも気にしないかもしれない。3つの選択肢は、すべて正当です。
そしてまた気づくのです。すべてがお金の問題ではないということを。
Webはすべての人のためのもの
Webの背後にある夢は、情報を共有することでコミュニケーションを行う共通の情報空間である。
— Tim Berners-Lee(Tim Berners-Lee卿)
私たちフロントエンド開発者は、Webの管理人です。Webがどうなるかは、私たち次第です。すべての人にスロープやエレベーターを建てさせることはできませんが、少なくとも自分たちが建てることはできます。
選択は、本当にあなた次第です。したくなければ、気にかけなくても構いません。
ですが、私が知る優れたフロントエンド開発者たちは皆、気にかけています。インクルーシブであることを選んでいるのです。それこそが、私たちをフロントエンド開発者たらしめているものです。
私たちは、ユーザーのことを気にかけています。
とはいえ、制約や限界もあるのが現実です。その限界の中で、私たちはベストを尽くすのです。
この記事の要点をおさらいします。
- なぜその古いブラウザをサポートしようとしているのか、目的を明確にする
- 4段階のうち、どのレベルのサポートを提供するのかを決める
- 割り当てたリソースに見合う価値があるかを見極める
CSS対応には「プロパティフォールバック」と「フィーチャークエリ」、JavaScript対応には「ポリフィル」と「Babel」を活用しましょう。そして忘れないでください。Webは、すべての人のためのものなのです。
-
Chrome 92の新プライバシー機能とは?知っておきたい4つのセキュリティ強化ポイント
インターネットを利用する際には、常に一定のリスクが伴います。ブラウザはオンライン体験の中核を担う存在であるため、各社ともサイバー脅威への対策に日々力を注いでいます。そんな中、Google Chrome 92のアップデートでは、新たなプライバシー保護機能が多数搭載され、脅威アクターからユーザーを守る体制がさらに強化されました。本記事では、最新のセキュリティ対策について詳しく解説します。1. フィッシング検出機能の大幅な強化フィッシング攻撃は最も一般的なサイバー攻撃の一つであり、Googleもその撲滅に全力を挙げています。新版Chrome 92では、フィッシングの検出・ブロック速度が従来の50倍に
-
YouTubeを見ながらWebサイトを閲覧する方法|Chrome拡張機能「Sideplayer」の使い方
別のタブに切り替えてもYouTube動画を見続けたいと思ったことはありませんか?そんなときに便利なのがChrome拡張機能「Sideplayer」です。この拡張機能を使えば、YouTube動画が小さなポップアップウィンドウで再生され、タブやウィンドウを移動しても表示が維持されます。 Sideplayerの導入方法と基本的な使い方 まず、ChromeウェブストアからSideplayerをインストールし、Chromeを再起動しましょう。その後、YouTubeにアクセスして任意の動画を開き、動画下部にある「Play In Sideplayer」ボタンをクリックします。これだけで、動画がコンパクトなプ