フレキシブルボックスにおけるOperaの問題点
内容
最近投稿したいくつかのページをOperaで表示したところ、フレキシブルボックスによるレイアウトが崩れていることに気付きました。
フレックスアイテム内にある子要素が、アイテムのサイズを超えた場合に問題が起きます。
簡略化したdivボックス構成で、この問題を再現してみましょう。まず、問題の起きないfloatレイアウトから試していきます。
- all_frame
- 全体を囲むボックス。幅は600ピクセル。子ボックスとして、box1とbox2を入れる。
- box1
- all_frameの子ボックス。幅は200ピクセル。
- box2
- all_frameの子ボックス。幅は400ピクセル。子ボックスとしてbox3を入れる。
- box3
- box2の子ボックス。幅は1200ピクセル。マージンを10ピクセル。
そして、すべてのdiv要素にはoverflow:hiddenを指定し、box1とbox2にはfloat:leftを指定して、横並びにします。
<div id="frame">
<div id="box1">
<p>box1</p>
</div>
<div id="box2">
<p>box2</p>
<div id="box3">
<p>box3</p>
</div>
</div>
</div>
<style>
p {
padding:0.5em;
}
div {
overflow:hidden;
}
#frame {
width:600px;
}
#box1 {
width:200px;
background-color:rgb(255,0,0);
float:left;
}
#box2 {
width:400px;
background-color:rgb(0,255,0);
float:left;
}
#box3 {
width:1200px;
margin:10px;
color:rgb(255,255,255);
background-color:rgb(0,0,255);
}
</style>
frameの幅600ピクセルに対し、box1とbox2の幅が収まる(200+400=600)ようにしてあるので、問題なくフロートのレイアウトができます。
そして、box3は、マージン分を含めて、box2には収まらない幅(1200ピクセル)にしてありますが、overflow:hiddenがすべてのdivにかかっているので、box2の外にはみ出した部分は表示されません。
では、これをフレキシブルボックスレイアウトに変更してみましょう。frameにdisplay:flexを指定し、box1、box2からfloat:leftを削除します。
div {
overflow:hidden;
}
#frame {
width:600px;
display:flex;
}
#box1 {
width:200px;
background-color:rgb(255,0,0);
}
#box2 {
width:400px;
background-color:rgb(0,255,0);
}
#box3 {
width:1200px;
margin:10px;
color:rgb(255,255,255);
background-color:rgb(0,0,255);
}
すると、box1の幅が縮み、box2は逆に伸びてしまいます。これらのボックスの指定サイズは無視されたことになります。一方、frameの幅600ピクセルは維持されています。
box3が自身を表示させる最大限の幅を“要求”し、それに引きずられるかたちでbox2も伸び、割を食ったbox1は、その内容(p要素「box1」の3文字)が表示可能な最小限の幅まで、狭められてしまったようです。
frameの幅が維持されているのは、overflow:hiddenのおかげです。これが無ければ、frameも幅が広がってしまいます。
これは、現状ではOpera 12だけで発生し、IE10とChrome 26は、同じスタイル指定のままでbox1、2の幅は維持されます。もちろん、それぞれのベンダー接頭辞は必要ですが、定義内容に変わりはありません。
これは、Operaのバグなのでしょうか? しかし、フレキシブルボックスレイアウトの最新仕様にOpera 12は最も適合しているはずです。
この問題がさらに厄介なのは、box3に幅を指定しなかった場合です。幅指定が無ければ、divのようなブロックレベル表示の要素は、親と同じ幅になり、問題は無いように見えます。今回は、marginを上下左右に10ピクセル取っていますから、その分の隙間もきちんと入っています。
ところが、以下のように、途中で改行されない長い単語が入ると、ボックスサイズは変更されてしまいます。ふだんは上手くいっていも、内容によっては、問題が起きてしまいます。油断禁物、というわけです。
まあ、通常のテキストであれば、ここまで長い単語はめったになく、欧文でも単語間スペースがあれば、テキストは改行されます。しかし、当サイトの実例のように、pre要素を使って途中改行されない長い行を入れる場合もあります。また、画像などは、当然、途中改行などできません。
結局のところ、box3にbox2を超えないサイズの幅を明示的に指定するか、そうでない場合は、box3の内容にbox2の幅を超えるようなものは入れない、という回避策しかありません。pre要素にソースコードを入れる場合は明示的に幅を指定し、画像やさらにそれを囲むdiv等には、幅を90~100%にします。
画像は、WordPressのメディアライブラリ機能を使う場合、アップロードと共に自動作成される大・中・サムネール画像のサイズを、記事の幅に収まるように設定することで、問題回避は可能です。
しかし、当サイトでは画面に応じて3種類のサイズを用意し、最大の幅に合わせて大サイズのメディアを配置しています。この結果、小さな画面サイズになると、問題が再発します。
そこで、(CSS3メディアクエリー内の)幅を狭める設定の中で、メディアライブラリ画像を特定し、これにスタイルシートで幅90%を強制的に指定しています。メディアライブラリ画像は、サイトのwp-content/uploadsフォルダー以下にアップロードされますから、img要素のsrc属性値には、必ずこの文字列が含まれます。そこで、CSS3で追加された、「値に含まれる文字列との一致」で特定できる属性セレクターを利用しています。
要素[属性名*="含まれる文字列"]
img[src*="/wp-content/uploads/"]
このように回避策はあるものの、これが仕様というなら、どうしようもない仕様としか言いようがありません。Operaの解釈の仕方が間違っていてると思うのですが、どうなのでしょう。まあ、いずれOperaは、レンダリングエンジンをChromeと同じWebkitに乗り換えるとのことですから、それによってこの問題も葬り去られるかもしれませんね。