JavaScript | Djangoのテンプレート内でループ生成した最初の要素しか反応しなかったときの対処

eyecatch Django

mizky(^ω^) なぜかループの最初の要素だけ、JavaScriptが正常に動作する。。。

こんにちは、mizkyです。

今回はDjangoでJavaScriptを使用していた際に遭遇した現象にについて、共有します。

今回の問題

Djangoのテンプレートを使用していた際、JavaScriptを活用してページの要素を操作しようとしたところ、想定外の挙動に遭遇しました。

具体的には、forループを利用して複数のHTML要素を生成している中で、
それぞれの要素に同じIDを割り当ててしまった結果、
JavaScriptがループの最初の要素にしか動作しないという問題が発生しました。

簡単なサンプル

まず、Djangoテンプレートでのサンプルコードです。

{% for item in items %}
    <div id="item">{{ item.name }}</div>
{% endfor %}

このテンプレートでは、各アイテムに対して同じID「item」が付与されています。

そして、以下のJavaScriptを使ってこれらの要素を操作しようと試みます。

var itemDiv = document.getElementById('item');
itemDiv.style.color = 'red';

このJavaScriptコードの意図は、ループで生成されたすべての要素を赤色にすることです。

しかし、実際にはgetElementById()がページ上で最初に見つかった要素にのみアクセスするため、
期待した通りに動作しません。

結論

テンプレートについて、for文を使用せずに書き直すと

<div id="item">{{ item.name }}</div>
<div id="item">{{ item.name }}</div>
<div id="item">{{ item.name }}</div>

このようになっており、全てのdiv要素に同じIDが付与されてしまっています。

JavaScriptでgetElementById()を使用すると、このメソッドはページ上で最初に見つかった要素のみを返すため、今回のような現象が発生しました。

当たり前ですが、ID属性はそれぞれの要素に固有でなければなりません。

具体例

問題の再現

一応、Djangoのテンプレートを使用せずに、HTMLとJavaScriptだけで同現象を再現してみます。

下記に、今回使用するサンプルコードを示します。

<!DOCTYPE html>
<html>
<head>
    <title>ID Test</title>
</head>
<body>
    <div id="unique">このテキストは変更されますか?</div>
    <div id="unique">このテキストは変更されますか?</div>

    <script>
        // 同じIDを持つ要素にアクセス
        var element = document.getElementById('unique');
        
        element.textContent = '最初の要素のテキストが変更されました!';
    </script>
</body>
</html>

実際にブラウザに表示してみます。

ボタンを押下すると、、、

確かに、最初の要素だけ変わることが分かりました。

対策

IDではなく、Classで正常に実行されるか確認します。

下記に、Classを使用して書き直したサンプルコードを記載します。

<!DOCTYPE html>
<html>
<head>
    <title>ID Test</title>
</head>
<body>
    <div class="unique">このテキストは変更されますか?</div>
    <div class="unique">このテキストは変更されますか?</div>
    <button onclick="changeText()">テキストを変更</button>

    <script>
        function changeText() {
            var elements = document.querySelectorAll('.unique');
            for (var i = 0; i < elements.length; i++) {
                elements[i].textContent = 'このテキストは変更されました!';
            }
        }
    </script>
</body>
</html>

属性をIDからclassに変更しました。
また、textContentをループ処理させています。

挙動を確認してみます。

「テキストを変更」ボタンをクリックすると、

全ての文言が正常に変更されました。

まとめ

本記事では、DjangoテンプレートでHTML要素を生成する際に同じIDが複数の要素に付与される問題を掘り下げました。

問題回避のためのポイントは次の通りです

  1. IDの一意性を守る:各要素にはユニークなIDを割り当てます。
  2. クラスの活用:共通の属性を持つ要素はクラスで管理します。

コメント

タイトルとURLをコピーしました