Loading

ROUTE

ルートゼロの
アクティビティ

th:replaceとth:insertでDOMはどう変わる?Fragment共通化の使い分け

5

ThymeleafのFragmentで迷いやすいth:insertとth:replaceは、ホスト要素を残すかどうかで区別できます。th:insertは指定したFragmentをホストタグの中へ挿入します。th:replaceはホストタグ自体をFragmentで置き換えます。「共通ヘッダーを入れたらdivが一段増えた」という場合は、まずこの出力差を確認すると原因を切り分けやすくなります。

insertはホストを残し、replaceは置き換える

属性 ホスト要素 Fragment 向いているケース
th:insert 残る ホスト要素の子として入る 既存のラッパーを維持したい
th:replace 消える Fragment自身がその位置へ入る Fragment側のタグ構造をそのまま使いたい

どちらが常に正解というものではありません。最終的に残したいHTML構造から逆算して選びます。


同じFragmentでも生成後のDOMが変わる

まず共通Fragmentをfooter.htmlへ定義します。

<footer th:fragment="copy">
  <p>Copyright Route Zero</p>
</footer>

呼び出し側でinsertとreplaceをそれぞれ使います。

<div th:insert="~{footer :: copy}"></div>

<div th:replace="~{footer :: copy}"></div>

テンプレート処理後の構造は次のように異なります。insertでは呼び出し側のdivが残り、その中へfooterが入ります。replaceでは呼び出し側のdivが消え、footerがその位置へ入ります。

<!-- th:insert の結果 -->
<div>
  <footer>
    <p>Copyright Route Zero</p>
  </footer>
</div>

<!-- th:replace の結果 -->
<footer>
  <p>Copyright Route Zero</p>
</footer>

タグが二重になったらinsertのホストを確認する

Fragment側がheaderやfooterなど意味のあるタグを持ち、呼び出し側にも同じ役割のラッパーを置いていると、insertでは階層が一つ増えます。それ自体は仕様どおりですが、CSSセレクタやFlexbox/Gridの対象が変わる原因になることがあります。

  • 呼び出し側のホストタグを残す必要があるか確認する
  • Fragment自身がheader・footer・navなどの完成した構造を持つならreplaceを検討する
  • ホスト側にclassやレイアウト責務があるならinsertを検討する
  • 生成後HTMLをブラウザの開発者ツールで確認し、想定した階層になっているかを見る

Fragmentを指定する基本構文

<header th:replace="~{fragments/header :: siteHeader}"></header>

この例ではfragments/headerテンプレート内のsiteHeader Fragmentを参照します。Thymeleaf 3.1の公式チュートリアルでは、~{template :: fragment}の形でFragment式を記述できます。

用語解説:Fragment
ヘッダー、フッター、ナビゲーションなど、複数テンプレートから再利用するHTML断片です。

insertとreplaceの判断手順

  • 最終的に残したいHTMLタグ構造を先に決める
  • 呼び出し側のホスト要素を残したいならinsert
  • Fragment自身のルートタグをその位置へ置きたいならreplace
  • CSSやJavaScriptが特定のDOM階層へ依存している場合は生成後HTMLで確認する

属性名だけを暗記するより、「ホスト要素が残るか消えるか」を基準にすると、共通化後のDOMを予測しやすくなります。

関連するThymeleafの基本を確認する

Fragment以前の基本構文から確認したい場合はThymeleaf入門、${…}・@{…}・*{…}の違いは式の解説、テンプレートが読み込めない場合はParse Errorの記事が参考になります。

(Thymeleafの基本構文から確認するについては『Thymeleaf入門│Spring Bootで実践する基本サンプル』をご参照ください)

(Thymeleaf式の違いを確認するについては『Thymeleafで「$」「@」「*」はどう違う?使い分けガイド』をご参照ください)

(テンプレートの構文エラーを切り分けるについては『Thymeleaf Parse Errorの原因と対処法』をご参照ください)

よくある疑問

共通ヘッダーにはreplaceを使えばよい?

Fragment自身がheaderタグを持ち、そのタグをそのまま配置したいならreplaceが分かりやすいです。呼び出し側のラッパーを残す必要があるならinsertを選びます。

th:insertを使うと必ずdivが増える?

ホストがdivならそのdivは残ります。Fragmentがその内側へ挿入されるためです。ホストタグが何かによって生成後の構造は変わります。

th:replaceはFragmentの中身だけを入れる?

いいえ。th:replaceはホストタグを指定Fragmentそのもので置き換えます。Fragmentのルート要素も結果に含まれます。

まとめ

th:insertはホスト要素を残してFragmentを子として挿入し、th:replaceはホスト要素ごとFragmentへ置き換えます。タグが一段増えた、CSSが想定とずれたというときは、テンプレートだけでなく生成後DOMを確認してください。残したいHTML構造を先に決めれば、insertとreplaceを選びやすくなります。

まず既存のFragmentを一つ選び、ブラウザの開発者ツールでinsert/replace後のDOM階層を確認してみてください。

カジュアル面談はこちら

RANKINGranking-icon

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。