dskmori.github.io

これは 49.99999% of a CSS width is? の抄訳です。


Chintai は、たくさんの Webkit の問題 がある、とある日本の Web サイトですが、今回、CSS の width 値の四捨五入にフォーカスします。 Android 版 Firefox (Gecko) でこのナビゲーションを確認しましょう。

navbar as seen in Gecko

Blink (Chrome、Opera) の場合。

navbar as seen in Blink

何が起こっているのでしょうか。

<ul class="btn_read tapon">
    <li><p><a href="/info/inquiry.html" 
              target="_blank" 
              class="ui-link">ご意見・お問い合わせ</a></p></li>
    <li><p><a href="/info/kiyaku.html" 
              target="_blank" 
              class="ui-link">利用規約</a></p></li>
    <li><p><a href="/info/privacy.html" 
              target="_blank" 
              class="ui-link">個人情報の取り扱い</a></p></li>
    <li><p><a id="pcLink" href="javascript:void(0);" 
              class="ui-link">PC版 マイナビ賃貸</a>
            <input id="pcUrl" 
                   value="https://chintai.mynavi.jp/" type="hidden"></p></li>
</ul>

この部分に使用されている CSS は以下の通りです:

ul.btn_read li:nth-of-type(2n+1) {
    width: 49.99999%;
    border-right: 1px solid #CCC;
}

ul.btn_read li {
    display: table;
    float: left;
    width: 50%;
    height: 50px;
    background: gradient(linear, left top, left bottom, color-stop(0%,rgb(0,165,231)), color-stop(100%,rgb(0,142,217)));
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,white), color-stop(100%,#EFEFEF));
    border-bottom: 1px solid #CCC;
}

li 要素が内側にフィットするように、width (幅) を注意深く調整しようとしていることが分かります。 50% を使用するのに比べて 49.99999% という値は、既に危険な香りがしますね。 また、正しくない gradient プロパティが使用されていることにも注意しましょう。 gradient の正しい値は (CSS Fix me tool で簡単に直せます):

ul.btn_read li {
  /* … */
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,white), color-stop(100%,#EFEFEF));
  background: linear-gradient(to bottom, white 0%, #EFEFEF 100%);
}

では、width の四捨五入に戻りましょう。何が起きているのでしょうか。 viewport は、360px であると仮定します。devtool では両方とも ul は viewport サイズの 100% となっています。 問題ありません。

ul width in CSS

(左がblink、右が gecko)

li:nth-of-type(2n+1) のスタイル (width: 49.99999%;) が適用される最初の li を見てみましょう。

first li width in CSS

(左がblink、右が gecko)

2 番目の li (width: 50%;) は

second li width in CSS

(左がblink、右が gecko)

おっと… Gecko では、179.983px + 1px + 180px = 360.983px となります。viewport より広いですね。

数学は難しい

Update 2016-05-26: How CSS width is computed in Gecko?

少なくともブラウザにとっては、難しいようです。電卓を使って確認しましょう。

360*(49.99999/100) = 179.999964. このような結果になると思います。 うーん、Gecko が値をどのように四捨五入しているか逆算して確認しましょう。

(179.983/360)*100 = 49.995277778. おや!?

もしかすると devtool が UI 上で四捨五入しているのかもしれません。Gecko のコンソールではどうなるか確認してみましょう。

width in the Firefox console

この値から、Firefox では width の値として 49.995371501% を使用するとよいことを意味します。

width in the Blink console

両方の liwidth: 180 になります。少なくとも、おかしな四捨五入には対応させることができました。

CSS の width は、数学の幅ではありません

この width を正しく計算する方法

フロントエンド開発者は、これまで、この問題に対してはパーセント値を使うべきではないと思っていたはずです。 では、この問題を解決するにはどうしたらよいのでしょう?

最も単純なのは、calc() を使用する ことでしょう。

ul.btn_read li:nth-of-type(2n+1) {
    width: calc(50% - 1px);
    border-right: 1px solid #CCC;
}

しかし、この Web サイトが作成されたとき、このオプションは利用できなかったのだと思います。

他の回避策は、box-sizing: border-box; と設定することです。 border が、width の計算に含まれるようになります。これで、49.99999% を使用する必要がなくなります。

ul.btn_read li {
    display: table;
    float: left;
    width: 50%;
    height: 50px;
    background: linear-gradient(to bottom, white 0%, #EFEFEF 100%);
    border-bottom: 1px solid #CCC;

    /* set box-sizing on li */

    box-sizing: border-box;
}

ul.btn_read li:nth-of-type(2n+1) {
    /* width: 49.99999%; */
    border-right: 1px solid #CCC;
}

Otsukare! (おつかれ!)