👻

「無駄なDivを作るな」とはどういうことなのか

2023/08/29に公開

同僚からこんなことを言われたことがある。

「無駄なdivを作るな って前職で結構言われていたんですよね」
「そのdivなくてもレイアウトできるじゃん って」

それがどんな文脈だったか忘れたが、確かに言われそうなことではある。その言葉を受けて、改めてdivについて調べてみた。

div とは

改めてdivとは何かを調べてみると、以下のような説明があった。

<div> は HTML の要素で、フローコンテンツの汎用コンテナーです。 CSS を用いて何らかのスタイル付けがされる(例えば、スタイルが直接適用されたり、親要素にフレックスボックスなどの何らかのレイアウトモデルが適用されるなど)までは、コンテンツやレイアウトには影響を与えません。

引用: https://developer.mozilla.org/ja/docs/Web/HTML/Element/div

すなわち、divはコンテンツをグループ化するための要素であるので、グループ化が必要なところで利用する分には問題ないように思える。

divによるグループ化とは

ここで、グループ化とはどういうタイミングで行うのかを考えてみる。

  • 一連の要素に共通のスタイルを適用するため
  • レイアウトの制御のため
  • インタラクティブな機能を追加するため
  • コンテンツの構造や意味を明確にするため

一連の要素に共通のスタイルを適用するため

divタグで囲んで一気にスタイルを適用するパターン。

<div class="link-list">
    <a href="#">記事1</a>
    <a href="#">記事2</a>
    <a href="#">記事3</a>
</div>
.link-list a {
    color: blue;
}

レイアウトの制御のため

特にCSSフレームワークやモダンなレイアウト方法(FlexboxやGrid Layoutなど)を利用する場合、要素のグルーピングや順序制御のために<div>は頻繁に使用される。

<div class="flex-container">
  <div>アイテム1</div>
  <div>アイテム2</div>
  <div>
    <a>アイテム3<a>
    <a>アイテム4</a>
  </div>
</div>

インタラクティブな機能を追加するため

特定の要素グループにインタラクティブな機能を付与する際、<div>はその機能を適用する範囲を定義する役割を果たす。

<div class="toggle-button" onclick="showMenu()">
    メニュー
    <div class="menu-content hidden">
        <a href="#">トップページ</a>
        <a href="#">プロフィール</a>
        <a href="#">設定</a>
    </div>
</div>
function showMenu() {
    var menu = document.querySelector('.menu-content');
    menu.classList.toggle('hidden');
}

コンテンツの構造や意味を明確にするため

セマンティックなマークアップはWebアクセシビリティの向上やSEOのために重要だが、すべての場面でセマンティックな要素が適切ではない場合がある。そのようなとき、<div>はコンテンツの構造や意味をグルーピングして明確にする手段として使用される。

<div class="profile-section">
    <h2>プロフィール</h2>
    <p>名前: 山田太郎</p>
    <p>趣味: 旅行、写真</p>
</div>

また、開発者の目線で見ても、コンテンツの構造や意味が明確になっていれば、コードの可読性が向上する。

では、無駄なdivとは

「無駄なdiv」とはなんなのだろうか。

本当に必要でないのに使用されている、グループ化の役割を果たしていないdiv のことなのだろう。
div を使っていいのか使わないべきなのかで迷う場合は、上記のケースのうちどれに当てはまるのかを考えてみると良いだろう。

ちなみに、span について

divを使わずにグループ化を行う方法として、<span>がある。<span><div>と同様にグループ化を行うための要素であるが、<div>と異なり、ブロック要素ではなくインライン要素である。

よく利用する例として、<span>はテキストの一部にスタイルを適用するために利用される。

<p>今日は<span class="highlight">晴れ</span>です。</p>
.highlight {
    color: red;
}

おまけ: コストについて

無駄なdivをめちゃくちゃネストした場合、どのくらいのパースコストがかかるのかが気になったので、適当ではあるが調査してみた。

調査したのは、100, 1000, 10000 の3パターン。

こんな感じでHTMLを作成。

python -c "print('<div>' * 10000 + 'Hello World' + '</div>' * 10000)" > nested_divs.html

これをpythonのhttp.serverで配信し、ChromeのDevToolsのPerformanceタブでパース・レンダリングコストを計測した。

python -m http.server 8080

結果

結果はこんな感じ。(大体のオーダー感が知れれば良いのですべて一回だけ計測。)

ネスト数 Parse HTML Layout Recalculate Style
100 0.1370ms 0.3820ms 0.2510ms
1000 1.5850ms 4.6960ms 3.2800ms
10000 108.2ms 41.4ms 34.7ms

1000と10000ネストを比べてみると、Parse HTMLが1000ネストの場合の100倍、LayoutとRecalculate Styleが10倍のオーダーになっている。
これは、ネスト数が増えると、HTMLパースを段階的に行い、それに伴ってRecaclulate StyleとLayoutが発生するためだと思われる。

10000ネスト時のParseHTMLが段階的に行われている様子。

1000 ネストでは起きていない様子。

なので、同僚が無駄なdivを10000個ほど作っていたとしたら、「パフォーマンスの面で無駄なdivはやめてくれ」とアドバイスしたほうがよさそうだ。

GitHubで編集を提案

Discussion