IE8まで頑張るwebサービス開発で使えるCSS, HTMLに関するTIPS 10個

普段業務で、レガシーブラウザ IE8対応のWebサービスを開発＆運用しています。

開発に関わっておよそ１年半ですが、
2014年11月現在 運用中のWebサービスで使っている
IE8でも使えるCSSに関わるTIPSを、備忘録がてら記録しておこうと思います。

（IE7以下はサポートを切ってしまったので、先輩方の更なる苦労は存じ上げません。ごめんなさい。）


対応ブラウザ（動作確認環境）
-------------------------

この記事の前提として、動作確認環境です。

* IETesterで IE8, IE9, IE10 / Windows（IETesterとか信じられない方はすみません）
* IE11 / Windows
* Firefox（ほぼ最新） / Windows, Mac
* Google Chrome（ほぼ最新） / Windows, Mac
* Safari（ほぼ最新） / Mac

IE以外をお使いの方はほぼアップデートかかる＆できるだろうということで、実際細かいバージョンまでは見ていません。
Operaやちょっと珍しいブラウザは顧客層として1%未満のため非対応です。


開発＆運用方針
-------------

「プログレッシブ エンハンスメント」的な考えで、
IE8でも動くことが最低リリース基準で、可愛いとかかっこいいとか便利な機能は**できるだけがんばる**。

* ほんとはこう書いた方がシンプルだけど、IE8のためにちょっとめんどくさい書き方をする
* IE8でも四角形見えるけど、Chromeで見たら実は角丸だった

大体こういう感じです。

そのため、IE8で「できない」こともたくさんありますし、容認しています。
**どこまでがんばるか**、の参考程度に見ていただければと思います。



1. last-child, nth-childは使えない
---------------------------------

IE8では```last-child```や```nth-child(n)```が使えません。
よくある横並びリストで最後の要素だけ・３つ目の要素だけmarginを0pxにしたい…とかができません。
ただし```first-child```は使えます。

ので、「何番目か」を考慮しないマークアップにするか、デザイナーと交渉するか、first-childにあてるしかありません。

### 1-1. 縦並びのリスト

> リストの各要素の**間にだけ**点線を入れたい

```first-child``` を使うように頑張ります。

```html:html
<ul class="list">
  <li>要素１</li>
  <li>要素２</li>
  <li>要素３</li>
</ul>
```
```scss:scss
.list li {
  border-top: dashed 1px gray;
  &:first-child { border-top: none; }
}
```

### 1-2. 横並びの３カラムリスト

> 例：幅650pxのスペースに、縦横200pxの６つの要素を３個ずつ２段に並べる。隙間はそれぞれ10px。

* 1-2-1. ulリストの右側に要素間の隙間以上のスペースがある

リストに全て ```margin-right: 10px``` を付加してfloatで並べます。


* 1-2-2. ulリストの横に十分な隙間がない

> 幅650pxじゃなくて620pxだったとき

出力するときに「3n+1個目の要素にだけ当てるクラス」を付加します。
jsとか、初期表示の描画時にeach文とかfor文でindex使って割り当てたり。

```html:html
<ul class="list clearfix">
  <li>要素１</li>
  <li>要素２</li>
  <li>要素３</li>
  <li class="clear">要素４</li>
  <li>要素５</li>
  <li>要素６</li>
</ul>
```
```scss:css
.list li {
  float: left;
  width: 200px;
  margin-left: 10px;
  &:first-child, .clear { margin-left: 0; }
}
.clear { both: clear; }
.clearfix { // 略 }
```


2. 疑似要素に```position: absolute``` + ```z-index```を使うときは注意
-------------------------------------------------------------------

```:before``` ```:after``` などの疑似要素はがしがし使って大丈夫です。
ただし、**IE8はz-indexが苦手**で、疑似要素に```position: absolute;```と```z-index```両方を適用すると、z-indexが効かず、背面に配置されてしまいます。（参考：[ie8 absolute z-index](https://www.google.co.jp/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=ie8+z-index+absolute)）

とは言えどうしてもその組み合わせで使いたいときもあるので、疑似要素は、飾り程度に。
逆に言うと、**マークアップ上・文書構造上必要な要素を疑似要素には使わない**。

飾りは見えないときは見えない。と諦めてもなんとかそれらしいデザインになるように他をマークアップします。
たとえば飾りが表示されているときには気づかない余白を、飾りが見えないブラウザのためにつけておく、とか。（案外飾りがなくても違和感が激減します）


3. display: tableを積極的に使う
-----------------------------

IE8までなら使えるというのが（個人的に）意外でした。
困ったときにはテーブルレイアウト。

横並びの要素を全部均等かつ、中身のテキストを縦方向に中央に配置したい、とかくれば、迷わず```display: table;```を。


4. CSS3アニメーションは使えない
--------------------------

* 顧客層におけるIE8のパーセンテージを引き合いにして交渉する
* アニメーションがなくても最低限分かるデザインになるよう交渉する
* どうしてもIE8でも動かしたい場合はjsでがんばってみるけど工数を多めにもらった上でできないかもしれないと進言する

実際アニメーションは付加要素でしかないので、開発コストに見合わければIE8でも動くようには実装しません。
そういうデザインを作って下さっているデザイナーさんに感謝。

```js:唯一実装したアニメーション
$list.animate({
  left: pos + "px"
}, 250, "linear");
```
（リストをスライド表示させたかった。）

※他ブラウザではCSS3アニメーションで色々動かしてます。


5. webfontはeotファイルを用意する
----------------------------

もしかして使えないんじゃ？と言われがちですが実は使えます。
ただかなり裏技的な使い方なのでご注意ください。

compassを使って「myfont」というオリジナルwebfontを作っているとします。（webfontの作り方は別途…）

```scss:myfont.css
@font-face {
  font-family: 'myfont';
  src: url(myfont.eot?#iefix") format('embedded-opentype'),
       url(myfont.woff") format('woff'),
       url(myfont.ttf") format('truetype');
}
```

* IE8では複数fontの指定ができない
* ?以降の読み込みがエラーになり、それ以後読み込まれなくなる（→結果的にeotファイルが適用される）

ということを使ったハックだそうです。


うちではスマホ向けでも同じwebfontを使っているので、
WOFFがiOSのChromeとかMacとか向けで、TTFがAndroidとかSafari向け、EOTがIE8向けに用意しています。


ちなみにwebfontって.html拡張子を最後につけてもフォントとして認識されるみたいです。（もちろんIE8でも）
```eot```などfontの拡張子を許可されていないサーバでも、```myfont.eot.html```ならアップロードできる可能性はあります。（やっていいかどうかは別の話で）


6. 要素を半透明にする
-------------------

要素を透明度70%に透かせたい。

```scss:opacity.scss
.opacity {
  -webkit-opacity: 0.7;
     -moz-opacity: 0.7;
      -ms-opacity: 0.7;
  filter: alpha(opacity=70);  // for IE8
```

うちではmixin化して

```scss:opacity.mixin.scss
// 対応したいvendor prefixのリスト
$set_prefix: -webkit-, -moz-, -ms-, '';

// プロパティにvendor-prefixをつけて返す
@mixin set-prefix($property, $val) {
  @each $prefix in $set_prefix {
    #{$prefix}#{$property}: $val;
  }
}

@mixin opacity($a) {
  @include set-prefix(opacity, $a);
  $IEalpha: $a * 100;
  filter: alpha(opacity=$IEalpha); /* IE 8 */
}
```

こんな感じで、使うときは ```@include opacity(0.7);``` と使っています。


### NGパターン：透過PNG + hover + 半透明 はギザる

> 透過PNGの画像リンクをつけて、マウスオーバー時に薄く半透明にしたい！

とか複雑なことをすると、ギザります。
画像の淵に黒い点線が浮いて見えてしまいます。

対処法は２つあり、

* 透過PNG画像に背景色を設定する：
透過の意味がなくなる（そもそも透過でなくていいなら透過しない）

* jQueryの専用のプラグインを読み込む：
大多数のユーザにとって無駄なjsを読ませるほど重大な「欠点」か？リンクとしての機能は果たしていないか？
また、3%ほどのIE8ユーザにとっても「見た目のための」「＋アルファの」要素のためのjsファイルを一個追加する？

うちではIE8でギザるのはデザイナーさんに目をつぶってもらいました。
たとえIE8でギザっても、他97%のブラウザで透かせたい。のです。



7. アイコンの隣に複数行のテキストを置く
------------------------------------

> * よくある箇条書きテキストのイメージです。項目が長くなって複数行になったときに、アイコン「・」は１行目にあるけどテキストは複数行に折り返してほしいしテキストは「・」の下に回り込んでほしくない。という状況です。キャンペーン用の一発もののページでよく目にするんですが意外に悩むマークアップだと思っています。


↑こんなかんじのやつ。

```html:html
<i class="icn-arrow"></i><span class="text">長い文章</span>
```
```scss:css
.icn-arrow { // webfontでよく使う
  display: block;
  float: left;
  margin-right: 4px;
}
.text {
  float: left;
  display: block;
}
```

書いてみると普通でした。


8. ３点リーダー「…」
------------------

固定幅の要素の中に幅ギリギリまで長文を出したいけど、足りなかったら省略表示したいとき。
それぞれのプロパティの説明は[CSS3リファレンス](http://www.htmq.com/css3)を参考にしています。正しく知りたい方は調べてみて下さい。


### 8-1. 領域が1行のとき、文末を３点リーダーに

> 長い文章長い文章長い文章長い文章…

みたいなやつ。

```html:html
<p class="elps">長い文章</p>
```
```scss:css
.elps {
  display: block;
  overflow: hidden;  // 領域からはみ出るものを見えなくする
  text-overflow: ellipsis;  // 省略記号を表示する設定。clipで表示しない
  white-space: nowrap;  // 領域からはみ出ても自動的に改行しないようにする
}
```

### 8-2. 複数行のとき、指定行数の最後を３点リーダーに

> 長い文章長い文章長い文章長い文章
長い文章長い文章長い文章長い文章
長い文章長い文章長い文章長い文…

こういうの。

```scss:css
.elps {
  -webkit-line-clamp: 3;  // 指定行数
     -moz-line-clamp: 3;
          line-clamp: 3;
  -webkit-box-orient: vertical;  // 領域内の子要素を、上から下へ垂直に積み重ねて表示
     -moz-box-orient: vertical;
          box-orient: vertical;
  display: -webkit-box;
  display: -moz-box;
  display: box;
  overflow: hidden;
  white-space: normal;  // 領域に応じて自動的に改行する
  text-overflow: ellipsis;
}
```

※ こちらIEで正しく動作していませんでした。
使った経緯としては、IEで適用しても不具合にならない、という判断でした。 大変失礼しました。（2015/4/17現在）

### NGパターン：複数行の３点リーダーをインライン要素に使うと行の途中で「…」が出ることがある

IE8に限らずですが、最近ハマったので。
基本的に３点リーダーを使うときは領域の幅や高さが固定…つまりブロック（orインラインブロック）要素だと思いますが、うっかりインライン要素に複数行の３点リーダーを適応すると、行末ではなく行の真ん中あたりに「…」が現れてしまいます。謎。


9. background-sizeが使えないので
-------------------------------

普段スマホ向けサービスを開発しているので、何も考えずにRetina対応用の画像をPCに当てはめて、```backgrdound-size```を指定してしまって、IE8で見てぎょっとすることがあります。

顧客層にもよると思いますが、PCのRetina対応をする必要がなければ、PCでは100%表示にしています。
もしくは、背景で縦横中央に配置して、「それらしく」みえるようにデザインを調整します。


うちのサービスでは、キャンペーン用ページなどの「一発もの」のページでは、SP, PCともに基本的に同じマークアップを使っています。たくさん書きたくないので。
そこで、SPにはbodyタグにSP専用のクラスを付加しておき、PCでは反映させたくないスタイル設定をSP向けだけに記述しています。

マークアップ量を最低限にし、stylesheetでSP, PCの差を吸収することで、文言変更や画像差し替えなどの運用に対応するコスト（＆対応漏れのリスク）を下げています。

イメージはこんな感じです。

```html:html(SP)
<body class="sp">
  <div class="bg-img">スマホ向けページ</div>
  <p class="hoge">hogefuga</p>
</body>
```

```html:html(PC)
<body>
  <div class="bg-img">PC向けページ</div>
  <p class="hoge">hogefuga</p>
</body>
```

```scss:scss
.hoge {
  // SP, PC共通の設定など
}
.sp {
  .bg-img {
    background: image-url("hoge.png") 50% 50% no-repeat;
    background-size: 50% 50%;
  }
}
.bg-img {
  background: image-url("hoge-pc.png") 50% 50% no-repeat;
}
```

敢えてSP側に切り替え用のクラスを付加しているのは、IE8以外の、ChromeやFireFoxなどのモダンブラウザでは```background-size```が適用されてしまうため、**PCではすべて**100%表示にする必要があるためです。


10. HTML5要素を使うときの注意
---------------------------

sectionタグやarticle, timeタグなど、HTML5から使えるようになったタグは基本的にはIE8では使えません。
```<section></section>```タグや```<time></time>```タグはただのテキストと同じになります。
IE8には「タグ」として認識されないため、jsでtimeタグのvalueを取得しようとかtimeタグのデータを書き換えようとかの操作をすることができないので注意が必要です。


※[html5shiv](https://github.com/aFarkas/html5shiv)などを使うことで、擬似的に要素を適応させることはできます。うちでは使っていません。```div```とか```span```で問題ないです。



まとめ
-----

勢いで書いたけど7と8はIE8関係なかったですね。

今回はあくまで「IE8はそこまで頑張らない」視点での紹介でしたので、メインターゲット層がIE6〜8というサービスではなんとしてでも対応しなければ、という点もあるかと思います。

ただwebサービスを作っている以上、「この環境でこれはできない」というのは必ず存在すると思います。
そのとき、「こうすればなんとか動く」に飛びつくのではなく、「できないならどう見せるか」という視点で折衷案を織り込んでいくのもフロントエンジニアの仕事なのかなぁと思っています。


でもやっぱIE8対応外したいなぁ〜