2つのHTMLファイルの違いを見つける方法

2つのHTMLファイルはブラウザでまったく同じようにレンダリングされても、 ソースコードでは何十箇所も異なることがあります。タグ周辺の空白、 属性の順序、HTMLエンティティ、自己終了構文が主な原因です。 プレーンテキストdiffはそれらすべてを変更としてフラグを立てるため、 本当に重要な変更が埋もれてしまいます。このガイドでは、クリーンな結果を 得る方法を説明します。

ツールにすぐ進みたい場合は、 HTML比較ページで2つのファイルを 貼り付けてブラウザ上でdiffを確認できます。インストール不要です。 残りの記事では、出力結果を確認する際に何を見るべきかを説明します。

HTMLのdiffがノイズを生みやすい理由

HTMLは行指向のフォーマットではありません。段落が1行の長い行として 書かれていても、10行に分割されていても、どちらも有効です。ほとんどの エディタ、コードフォーマッタ、CMSは保存時にHTMLを再フォーマットします。 そのため、1単語の変更が何十行もの再フォーマットされた行に波及する 可能性があります。diffはそれらを「1単語の変更」ではなく変更された行 として認識します。

WHATWG HTML Living Standard はブラウザがHTMLを解析する方法を規定していますが、それをテキストに シリアライズする方法は規定していません。2つのツールが構造的に同一の HTMLを生成しても、生テキストとしてはまったく異なる場合があります。

ノイズの大部分を占める4つの要因:

  • 空白 タグの間と内部
  • 属性の順序(ブラウザは特定の順序を要求しない)
  • HTMLエンティティ vs リテラル文字
  • 空要素の構文<br> vs <br />

空白:誤検知の最大原因

ほとんどの状況で、HTML内の連続する空白はブラウザがレンダリングする際に 1つのスペースに折り畳まれます。つまり、この2つのスニペットは同じように 表示されます:

<!-- Version 1 -->
<p>Free text comparison tool.</p>

<!-- Version 2 -->
<p>
  Free text comparison tool.
</p>

プレーンテキストdiffは両方の行を変更済みとしてマークします。しかし 意味のある意味では変更されていません。比較する前に、ユースケースで 空白が重要かどうかを判断してください。メールテンプレートやPDF レンダラーでは重要な場合があります。ほとんどのウェブページでは 重要ではありません。

最善の対処法は、diffを実行する前に両方のファイルを同じ設定で Prettier のようなフォーマッタに通すことです。これにより、インデント、行の長さ、 スペースを一度に正規化できます。両方のファイルが同じフォーマットスタイルを 共有すれば、プレーンテキストdiffは本当のコンテンツ変更だけをフラグします。

属性の順序

HTML仕様は属性を特定の順序で表示することを要求していません。 <div id="main" class="container"><div class="container" id="main"> は同じ要素です。 しかし行diffはそれらを異なる行として扱います。これは、テンプレートが 異なるツールで生成される場合や、属性をアルファベット順に並べ替える 自動フォーマッタを誰かが実行した場合に特によく見られます。

WHATWGの解析仕様 によれば、同じ要素の属性は定義上順序なしです。属性の並び替えを 変更として報告するdiffは技術的には正しいですが、ほとんど役に立ちません。 ノイズが発生している場合は、比較前に両方のファイルで属性の順序を 正規化してください。

HTMLエンティティ

&amp;&lt;&gt;&nbsp;&#8212; のような数値参照 — これらはすべてHTMLソースで文字をエンコードする方法です。2つの ファイルが同じ文字を異なる方法でエンコードしても、同じページを レンダリングできます。テキストdiffは &amp;& を異なる文字列として見ますが、どちらもブラウザでは アンパサンドを生成します。

エンティティの違いがdiffをごちゃごちゃにしている場合は、比較前に エンティティを正規化するHTMLパーサーに両方のファイルを通してください。 ブラウザ自身の DOMParser API はJavaScriptでこれを行う信頼性の高い方法です:両方の文字列を解析し、 innerHTML でシリアライズし直して、結果をdiffします。

空要素

HTML5では、空要素(子要素を持たない要素)は閉じるスラッシュが不要です。 <br><br/><br /> はすべて有効で、すべて同じようにパースされます。 <img><input><meta> なども同様です。一方のファイルがXHTMLスタイルの 自己終了スラッシュを使用し、もう一方が素のHTML5構文を使用している場合、 テキストdiffはそれらの要素すべてをフラグします。

MDNの空要素の完全リスト には14個すべてが含まれています。diff結果で /> をクイック 検索すると、ノイズのどのくらいが自己終了構文によるものかがわかります。

実践的な例

サイトのヘッダーのリアルな変更前後の例を示します。3つのことが変わりました: ヘッダーにCSSクラスが追加され、ホームリンクのターゲットが修正され、 コンタクトリンクが追加されました。

<!-- Version 1 -->
<header class="site-header">
  <nav>
    <a href="/home" class="nav-link active">Home</a>
    <a href="/about" class="nav-link">About</a>
  </nav>
</header>

<!-- Version 2 -->
<header class="site-header sticky-top">
  <nav>
    <a href="/" class="nav-link active">Home</a>
    <a href="/about" class="nav-link">About</a>
    <a href="/contact" class="nav-link">Contact</a>
  </nav>
</header>

両方をHTML比較ツールに貼り付けると、 diffはまさにその3行をハイライトします:ヘッダーのclassアトリビュート、 ホームのhref、新しいアンカータグです。2つのバージョン間でインデントと フォーマットが一致しているため、ノイズはありません。

上記の例で変更された内容
要素 バージョン1 バージョン2 変更の種類
<header> class="site-header" class="site-header sticky-top" クラスを追加
ホームリンク href="/home" href="/" パスを修正
コンタクトリンク 存在しない <a href="/contact">Contact</a> 追加

比較前に正規化すべき場合

すべてのHTML比較が正規化を必要とするわけではありません。同じエディタ設定で 自分で書いた2つのファイルを比較する場合は、プレーンテキストdiffで 通常十分です。正規化が効果的なのは:

  • 一方のファイルがCMSエクスポートで、もう一方がエディタからの場合
  • ビルドツールが一方のファイルを再フォーマットしたが、もう一方はしていない場合
  • 最小化されたHTMLと整形されたHTMLを比較している場合
  • 外部送信者のHTMLメールテンプレートをレビューしている場合

W3Cマークアップ検証サービス は、比較前に両方のファイルが正しく解析されることを確認するのに役立ちます。 タグ構造が壊れているファイルは誤解を招くdiffを生成します。パーサーが 独自の方法でエラーから回復するため、2つのパーサーが異なる方法で回復する 可能性があるからです。

生成されたHTMLの比較

サーバーレンダリングフレームワーク(Angular、Next.js、Rails)は、HTML出力に タイムスタンプ、nonce、またはランダムな識別子を埋め込むことがよくあります。 同じページを2回レンダリングすると、コンテンツが同じでもそれらの行でdiffが 生じます。生成されたHTMLを比較する場合は、diffを実行する前にそれらの フィールドを除去または正規化してください。

このサイトの基盤となるdiffエンジンは GoogleのDiff-Match-Patch (Apache 2.0)で、生テキストで動作します。HTMLを解析しないため、コンテンツの 違いと一緒にフォーマットの違いもフラグします。だからこそ、先に正規化する ことが重要です。ただし、ほとんどの場合、2つのファイルを直接貼り付けるだけで 数秒以内に十分役立つ結果が得られます。HTMLがXHTMLとして整形式である場合は、 XML比較ツールも試してみてください。 XML対応のdiffは名前空間と属性の順序を正しく処理します。

よくある質問

1行しか変更していないのにHTML diffが何百もの変更を表示するのはなぜですか?
ほとんどの場合、フォーマットの変更が原因です。エディタやビルドツールが保存時にファイルを再フォーマット(インデントの変更、長い行の折り返し、属性の並び替え)した場合、プレーンテキストdiffは再フォーマットされたすべての行を変更として見ます。まず両方のファイルを同じフォーマッタに通してから比較してください。本当の変更だけが残ります。
HTMLで属性の順序は重要ですか?
ブラウザにとっては重要ではありません。HTML Living Standardは属性を特定の順序で表示することを要求しておらず、ブラウザは順序に関係なく正しく解析します。一部のリンターやフォーマッタはスタイルルールとして属性をアルファベット順に並べ替えるため、意味的に同一の2つのファイルがテキストdiffでは異なって見える場合があります。属性の順序がノイズの原因になっている場合は、比較前に同じフォーマッタで両方のファイルを正規化してください。
HTMLソースコードの &amp;amp; と & の違いは何ですか?
ブラウザがページをレンダリングすると、どちらもアンパサンド文字を生成します。HTMLソースコードでは、&amp;はエンティティでエンコードされた形式で、&はリテラル文字です。技術的には、属性値の&は仕様に従って&amp;としてエンコードされるべきですが、ブラウザはどちらも受け入れます。テキストdiffはそれらを異なる文字列として扱います。エンティティエンコードがノイズを生み出している場合は、ライブラリまたはブラウザのDOMParserを使用して両方のファイルを解析し、比較前にシリアライズし直してください。
最小化されたHTMLを整形されたHTMLと比較できますか?
はい、ただし最初に最小化されたファイルを整形すべきです。最小化されたものと整形されたものを比較すると、最小化によってフォーマッタが追加したすべての空白が削除されるため、ほぼすべての行が変更されているように見える結果になります。最小化されたファイルをPretierまたは同等のフォーマッタに通してから比較してください。空白のノイズなしで意味のある変更が見えるようになります。
特定のコンポーネントなど、HTMLファイルの一部だけを比較するにはどうすればよいですか?
両方のファイルから関連セクションを抽出して、そこだけをdiffツールに貼り付けてください。たとえば、ナビゲーションコンポーネントの変更をレビューしている場合は、各ファイルから<nav>ブロックだけをコピーしてください。1つのセクションだけを気にしているのにドキュメント全体を比較すると、ページの無関係な部分からのノイズが増えます。
HTMLコメントはdiffに表示されますか?
はい。テキストベースのdiffはコメントを含むファイルのすべてを含みます。一方のバージョンにコメントブロックがあって他方が削除した場合、または開発者がコメントを更新した場合、diffはそれを表示します。これは通常役に立ちます:削除されたコメントは意図的なクリーンアップを示すことが多いからです。コメントを無視したい場合は、比較前に両方のファイルからコメントを削除してください。

HTMLファイルを比較する準備ができましたか?両方を無料のHTML比較ツールに貼り付けて、アカウント不要で差分を並べて確認しましょう。