スキップしてメイン コンテンツに移動

公開用 HTML の最小化 : ソースコードの実際に即した空白文字のアグレッシブな除去

本記事は「公開用 HTML の最小化」の後編です.前編は「</p> を省略する」です.公開用 HTML の最小化には、閉じタグの省略、空白文字の除去の他にも、属性値を囲むクォートの省略、開始タグの省略がまず思い浮かびます.前者は記事にするほどの内容ではなく、後者は OGP 情報を組み込む現代的なウェブ開発では殆んど使いどころが無いため扱いません.

1. はじめに

HTML 文書のソースコードは可読性の為に、ブラウザ画面にはあまり反映されない改行等の空白文字を含みます.

ソースコードを minify して公開用 HTML にする際には、これら可読性の為の空白文字は除去したい.一方で必要な空白文字は残したい.そのような空白文字の除去ルールを定義します.

まず、空白文字を除去するルールは、<script>, <style>, <textarea>, <pre> の子孫とその他で異なります.

2. その他の空白文字の除去ルール

まずは、その他について。

2.1. テキストノードの後ろの空白文字

次のソースコードでは、First を含むテキストノードは、First⏎ になる.1つの改行文字がテキストノードの後ろにある.

この改行文字は公開用 HTML では削除されているのが望ましい.

<ol>⏎
<li>Fisrt⏎
<li>Second⏎
<li>Third⏎
</ol>⏎

次のソースコードでは、First を含むテキストノードは、First⏎   になる.1つの改行文字と1文字以上の半角スペースないしタブが続く.

これらの空白文字は公開用 HTML では削除されているのが望ましい.

<ol>⏎
  <li>Fisrt⏎
  <li>Second⏎
  <li>Third⏎
</ol>⏎

2.2. テキストノードの頭の空白文字

春は、あけぼの。 を含むテキストノードは、⏎春は、あけぼの。⏎ になる.1つの改行文字に続いて 春は、あけぼの。 が出現する.

この改行は公開用 HTML では削除されているのが望ましい.

<p>⏎
春は、あけぼの。⏎
<p>⏎
やうやうしろくなりゆく山ぎは、すこし明かりて、紫だちたる雲の、細くたなびきたる。⏎

春は、あけぼの。 を含むテキストノードは、⏎    春は、あけぼの。⏎ になる.1つの改行文字と1文字以上の半角スペースないしタブに続いて 春は、あけぼの。 が出現する.

これらの空白文字は公開用 HTML では削除されているのが望ましい.

<p>⏎
    春は、あけぼの。⏎
<p>⏎
    やうやうしろくなりゆく山ぎは、すこし明かりて、紫だちたる雲の、細くたなびきたる。⏎

2.3. 全角文字の間の一つの改行文字を削除する

全角文字の間の一つの改行文字は仕様でウェブブラウザが無視するが、あらかじめ公開用 HTML から削除した方がファイルサイズが小さくなって好ましい上、Firefox 3(Gecko 1.9.0)の独自の挙動を回避できる.この情報は「ブラウザの問題:半角スペース、全角スペース、改行コード、整形処理」で教えて頂きました.

  1. 全角文字間の改行コード1個は、ブラウザでは半角スーペースが表示されない。
  2. Firefox 3 問題 Firefoxは、Firefox 3 になってから、全角文字間の改行コードを、半角スーペースで表示するようになった。

    対策としては、作成したファイルをアップロードする前に、何らかの対策が必要である。→整形処理を参照

この処理を行うコードです.(html.json/src/js/json2json/json2json.js より)

function removeNewlineBetweenFullWidthChars(string){
  return string.replace(/([\uFF01-\uFF60\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF])\s([\uFF01-\uFF60\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF])/g, "$1$2");
};

2.4. 開始タグの直後の半角スペースやタブ

次の書き方はソースコードでは見ない.意図してこの書き方をしていると考え、先頭の空白文字は1つの半角スペースを残す.

意図が無い場合、ソースコードにこのような空白文字を入れないこと.

<p> 春は、あけぼの。⏎

2.5. 空白文字を残すパターン

/ の前後の半角スペースは残るのが適切.改行文字がテキストノードの前後に無いため、半角スペースを残す判断が出来る.

Page: <b>1</b> / <b>8</b>
<!--          ^^^     -->

3. <script>, <style> 内の空白文字の除去

3.1. 開始タグの直後と、閉じタグの直前の改行文字を削除する

これ以上の minify は、clean-css 等の CSS Minifier や JavaScript Minifier を使うこと.

4. <textarea> 内の空白文字の除去

4.1. 先頭の改行文字を削除する

先頭の改行文字は仕様でウェブブラウザが無視するが、あらかじめ公開用 HTML から削除した方がファイルサイズが小さくなって好ましい.

A single newline may be placed immediately after the start tag of pre and textarea elements. This does not affect the processing of the element. The otherwise optional newline must be included if the element's contents themselves start with a newline (because otherwise the leading newline in the contents would be treated like the optional newline, and ignored).

<pre> 要素や <textarea> 要素の開始タグの直後には、改行を1つだけ置くことができます。これはその要素の処理には影響しません。ただし、要素の内容自体が改行で始まる場合には、その改行(本来は省略可能な改行)を含める必要があります。そうしないと、内容の先頭の改行が省略可能な改行として扱われて無視されてしまうからです。

ES2 HTML Parser / Test: URI Attribute value & Raw Text Elements」は、この動作を確認できるページです.

4.2. 改行文字の前の半角スペース・タブを削除する

<textarea>⏎
10 PRINT "HELLO, WORLD!"    ⏎
20 END    </textarea>
↓
<textarea>10 PRINT "HELLO, WORLD!"⏎
20 END</textarea>

</textarea> の直前にある半角スペースとタブも同様に削除します.

5. <pre> 内の空白文字の除去

<pre> 下のテキストノードには、特別なルールで最小化を行う.

5.1. 開始タグの直後と、閉じタグの直前の改行文字を削除

これらの改行文字は仕様でウェブブラウザが無視するが、あらかじめ公開用 HTML から削除した方がファイルサイズが小さくなって好ましい.

<pre>⏎
10 PRINT "HELLO, WORLD!"⏎
20 END⏎
</pre>
↓
<pre>10 PRINT "HELLO, WORLD!"⏎
20 END</pre>

5.2. 改行文字の前の半角スペース・タブを削除する

改行文字の直前にある半角スペースとタブを削除する.

<pre>⏎
10 PRINT "HELLO, WORLD!"    ⏎
20 END⏎
</pre>
↓
<pre>10 PRINT "HELLO, WORLD!"⏎
20 END</pre>

5.3. 単一の改行文字を前の要素のテキストノードに含める

改行文字を前の要素(</i>)に含めてしまった方が、テキストノードを節約できる

<pre><i>10 PRINT "HELLO, WORLD!"</i>⏎
<b>20 END</b></pre>
↓
<pre><i>10 PRINT "HELLO, WORLD!"⏎
</i><b>20 END</b></pre>

この節約効果は html.json 形式にすると分かりやすい.

["pre",
    ["i", "10 PRINT \"HELLO, WORLD!\""],
    "⏎",
    ["b", "20 END"]
]
//↓
["pre",
    ["i", "10 PRINT \"HELLO, WORLD!\"⏎"],
    ["b", "20 END"]
]

5.4 インライン要素下のテキストノードの後ろに半角スペースやタブがある場合

  1. インライン要素の後続が改行文字の場合に、インライン要素下のテキストノードの後ろの半角スペース・タブも削除する
  2. </pre> の直前の行の後ろの半角スペース・タブも削除する.
<pre><i>10 PRINT "HELLO, WORLD!"  </i>⏎
<b>20 END  </b></pre>
↓
<pre><i>10 PRINT "HELLO, WORLD!"⏎
</i><b>20 END</b></pre>