インライン要素に改行いれたら、予期せぬ空白が入ってしまった!
背景/事象
<span class="text">私が<span class="text--red">来た!!</span></span>
ソースコードが一列に並び過ぎて読みづらいなと思い、
<span class="text">
私が
<span class="text--red">
来た!!
</span>
</span>
こうした。
すると、表示が私が来た!!
から私が 来た!!
になってしまった、、(テキストに空白が入ってデザイン崩れ)
改行によってホワイトスペースが生まれたことに気付かず
謎のホワイトスペースに翻弄された2021年春。
本記事は上記の事象がなぜ起きるのか?の調査まとめになります。
なぜそうなるの?
上記の事象には2つの処理が関わってくることが分かりました。
1.CSSの空白処理
CSSでは、空白、タブ、改行は空白として解釈されます。そして、これはレンダリング時に適応されます。
これはwhite-spaceプロパティで制御されており、
white-space:normal が初期値(空白、タブ、改行は空白と解釈される)とされています。
ブラウザエンジンにはホワイトスペースの有要不要を決める規則があり、それを詳しく記載している記事がありますが、まとめると時間かかるのでリンクを載せておきます。
(詳細:cssのwhite-spaceプロパティ、cssのホワイトスペース処理の詳細)
2.インライン整形コンテキスト
インライン整形コンテキストは、視覚整形モデルの一つ。
視覚整形モデルとは、端的に言うとブラウザがどのように文書ツリー(HTML)を受け取り、人が視覚できる状態(webサイト)に処理・表示すればよいか説明するものです。
つまり、インライン整形コンテキストはインライン要素がどうレイアウトされていくのかの規則を決めています。
インライン要素の、
- 幅は要素内のコンテキストに依存する
- インライン要素は水平に配置される
という性質もインライン整形コンテキストで決められています。
(ブロック要素を表示するには、ブロック整形コンテキストが用いられる)
二つ関連してくるものをあげたが、結局何がどうなって半角スペースがはいるのかが不明瞭なので、下記の例を用いて具体的に見ていきます。
具体例
下記のコードを使用します。
<h1> 私が
<span> 来た!!</span> </h1>
人間の目から見るとH1要素はspan要素しか含んでいないように見えるが、
ソースコード視点では、
<h1>◦◦◦私が◦⏎
⇥⇥⇥⇥<span>◦来た!!</span>⇥◦◦</h1>
◦:空白
⏎:改行
⇥:タブ
- 1 つのテキストノード (いくつかの空白、”私が” という語、いくつかのタブから成る)
- 1 つのインライン要素 (<span> 中に空白と “来た!!” という語を含む)
- もう 1 つのテキストノード (タブと空白のみから成る)
を含んでいると解釈します。
処理の流れ
解釈を基にCSSの空白処理+インライン整形コンテキストを適応し、下記の順番でレンダリングします。
- 改行の直前と直後の空白とタブは全て無視される。
<h1>◦◦◦私が⏎
<span>◦来た!!</span>⇥◦◦</h1>
- タブ文字がずべて空白として扱われる
<h1>◦◦◦私が⏎
<span>◦来た!!</span>◦◦◦</h1>
- 改行が空白に変換される
<h1>◦◦◦私が◦<span>◦来た!!</span>◦◦◦</h1>
- 空白の直後に空白がある場合、それを無視する(別々なインライン要素をまたぐ場合も含む ※Worldの前の空白が対象になる)
<h1>◦私が◦<span>来た!!</span>◦</h1>
- 行頭と行末の空白が無視される
<h1>私が◦<span>来た!!</span></h1>
上記のレンダリング方法により、改行を含めると私が 来た!!
のように半角スペースができるようになる。
半角スペースを出さない方法
インライン要素に半角スペースを生まないようにするには下記の方法がある。
- インライン要素間にスペース、改行を設けない
- 改行した場合、要素間をコメントアウトする
- インライン要素ないで改行する
<span>改行</span
><span>こんな感じ</span>
ちなみに
ブロック要素・ブロック整形コンテキストではどう処理されるのか?
ブロック要素の特徴として、
- ブロック要素内のコンテキストに依存せず、横幅いっぱい占有する
- 垂直に積み重なるように配置される
とある。
これにより、ブロック要素の処理・表示の仕方が変わってきます。
具体例
下記を例にして表示の仕組みをみていく。
<body>⏎
⇥<div>◦◦私が◦◦⏎
<p>◦◦来た!!◦◦</p>⏎
</div>⏎
</body>
①ブロック要素は幅いっぱいに配置される+互いに積み重なるので下のようにな配置になる
<block>⏎⇥</block>
<block>◦◦私が◦◦⏎</block>
<block>◦◦来た!!◦◦</block>
<block>⏎</block>
<block>⏎</block>
②インライン要素の処理同様、空白処理をする(ホワイトスペース・改行・タブ)※ここはCSSによるものなので同じ処理になる。
<block></block>
<block>私が</block>
<block></block>
<block>来た!!</block>
<block></block>
③コンテキストがないものはレンダリング後のスペースは占領しないので、下記の内容のみ表示されるようになる
<block>私が</block>
<block>来た!!</block>
これにより、
私が
来た!!
と表示されるようになる。
さいごに
調べるまではインライン要素に改行いれると半角スペースできちゃうものなんだ〜と思っていたが、
画面に表示されるまでに様々な過程を経てできるものと知れた。仕様と一言で言えるものと、そうでないものを見極めてそうでないものは徹底して調べて根拠を得るようにしていきたいな〜と思いました。
参考文献
- 空白処理ルール
https://www.w3.org/TR/css-text-3/#white-space-phase-1 - CSS white-space
http://www.htmq.com/style/white-space.shtml - インライン整形コンテキスト
https://momdo.github.io/css2/visuren.html#inline-formatting - 空白の扱われ方(HTML,CSS,DOM)
https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Whitespace
Discussion