krzm.jp
黒住浩司 Webサイト

フレキシブルボックスにおけるOperaの問題点

内容

最近投稿したいくつかのページをOperaで表示したところ、フレキシブルボックスによるレイアウトが崩れていることに気付きました。

Operaの不正表示1
Opera 12でフレキシブルボックスレイアウトが崩れた状態

フレックスアイテム内にある子要素が、アイテムのサイズを超えた場合に問題が起きます。

Operaの不正表示2
div要素articles内のpre要素/code要素に記述した“改行されない長い文字列”によって、overflow-x:auto(スクロールバー表示)を指定してあるにもかかわらず、拡張したpre要素/code要素がarticlesの幅も広げてしまい、結果としてフレキシブルボックス構成を崩している。

簡略化した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ピクセルは維持されています。

Opera フレキシブルボックス表示結果
Operaの表示結果。レイアウトが崩れている。

box3が自身を表示させる最大限の幅を“要求”し、それに引きずられるかたちでbox2も伸び、割を食ったbox1は、その内容(p要素「box1」の3文字)が表示可能な最小限の幅まで、狭められてしまったようです。

frameの幅が維持されているのは、overflow:hiddenのおかげです。これが無ければ、frameも幅が広がってしまいます。

これは、現状ではOpera 12だけで発生し、IE10とChrome 26は、同じスタイル指定のままでbox1、2の幅は維持されます。もちろん、それぞれのベンダー接頭辞は必要ですが、定義内容に変わりはありません。

IE10の表示結果
IE10の表示結果
Chrome 26の表示結果
Chrome 26の表示結果

これは、Operaのバグなのでしょうか? しかし、フレキシブルボックスレイアウトの最新仕様にOpera 12は最も適合しているはずです。

この問題がさらに厄介なのは、box3に幅を指定しなかった場合です。幅指定が無ければ、divのようなブロックレベル表示の要素は、親と同じ幅になり、問題は無いように見えます。今回は、marginを上下左右に10ピクセル取っていますから、その分の隙間もきちんと入っています。

幅指定無しのOperaの表示結果
box3に対する幅指定無しのOperaの表示結果。この状態では、問題は無い。

ところが、以下のように、途中で改行されない長い単語が入ると、ボックスサイズは変更されてしまいます。ふだんは上手くいっていも、内容によっては、問題が起きてしまいます。油断禁物、というわけです。

非改行文字列を含んだOperaの表示結果
非改行の長い文字列が入ると、再びレイアウトが崩れる。

まあ、通常のテキストであれば、ここまで長い単語はめったになく、欧文でも単語間スペースがあれば、テキストは改行されます。しかし、当サイトの実例のように、pre要素を使って途中改行されない長い行を入れる場合もあります。また、画像などは、当然、途中改行などできません。

結局のところ、box3にbox2を超えないサイズの幅を明示的に指定するか、そうでない場合は、box3の内容にbox2の幅を超えるようなものは入れない、という回避策しかありません。pre要素にソースコードを入れる場合は明示的に幅を指定し、画像やさらにそれを囲むdiv等には、幅を90~100%にします。

画像は、WordPressのメディアライブラリ機能を使う場合、アップロードと共に自動作成される大・中・サムネール画像のサイズを、記事の幅に収まるように設定することで、問題回避は可能です。

メディアサイズ設定画面
WordPressのメディアサイズ設定画面

しかし、当サイトでは画面に応じて3種類のサイズを用意し、最大の幅に合わせて大サイズのメディアを配置しています。この結果、小さな画面サイズになると、問題が再発します。

そこで、(CSS3メディアクエリー内の)幅を狭める設定の中で、メディアライブラリ画像を特定し、これにスタイルシートで幅90%を強制的に指定しています。メディアライブラリ画像は、サイトのwp-content/uploadsフォルダー以下にアップロードされますから、img要素のsrc属性値には、必ずこの文字列が含まれます。そこで、CSS3で追加された、「値に含まれる文字列との一致」で特定できる属性セレクターを利用しています。

要素[属性名*="含まれる文字列"]


img[src*="/wp-content/uploads/"]

このように回避策はあるものの、これが仕様というなら、どうしようもない仕様としか言いようがありません。Operaの解釈の仕方が間違っていてると思うのですが、どうなのでしょう。まあ、いずれOperaは、レンダリングエンジンをChromeと同じWebkitに乗り換えるとのことですから、それによってこの問題も葬り去られるかもしれませんね。