スクリプト要素をページの上部近くに配置した場合、jQuery や getElementById などの DOM メソッドが要素を見つけられないのはなぜですか? 質問する

スクリプト要素をページの上部近くに配置した場合、jQuery や getElementById などの DOM メソッドが要素を見つけられないのはなぜですか? 質問する

document.getElementById$("#id")またはその他の DOM メソッド / jQuery セレクターが要素を見つけられない理由は何でしょうか?

問題の例には次のものがあります:

  • jQuery がイベント ハンドラーのバインドに暗黙的に失敗する
  • jQueryの「getter」メソッド(、、.val())は、.html().text()undefined
  • 標準の DOM メソッドがnullいくつかのエラーのいずれかの結果を返します。

キャッチされない TypeError: null のプロパティ '...' を設定できません
キャッチされない TypeError: null のプロパティを設定できません ('...' を設定)
キャッチされない TypeError: null のプロパティ '...' を読み取れません
キャッチされない TypeError: null のプロパティを読み取れません ('...' を読み取れます)

最も一般的な形式は次のとおりです。

キャッチされない TypeError: null のプロパティ 'onclick' を設定できません
キャッチされない TypeError: null のプロパティ 'addEventListener' を読み取ることができません
キャッチされない TypeError: null のプロパティ 'style' を読み取ることができません

ベストアンサー1

探している要素はDOMスクリプトが実行されたとき。

DOM 依存スクリプトの位置は、その動作に大きな影響を与える可能性があります。ブラウザは HTML ドキュメントを上から下へ解析します。要素は DOM に追加され、スクリプトは (デフォルトでは) 検出された時点で実行されます。つまり、順序が重要になります。通常、スクリプトはマークアップの後半に現れる要素を見つけることができません。それらの要素はまだ DOM に追加されていないためです。

次のマークアップを検討してください。スクリプト #1 は検出に失敗します<div>が、スクリプト #2 は検出に成功します。

<script>
  console.log("script #1:", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
  console.log("script #2:", document.getElementById("test")); // <div id="test" ...
</script>

では、どうすればいいでしょうか? いくつかの選択肢があります:


オプション1: スクリプトを移動する

上記の例からわかるように、直感的な解決策としては、スクリプトをマークアップの下に移動し、アクセスしたい要素を通過させることが考えられます。実際、長い間、スクリプトをページの下部に配置することは、ベストプラクティスさまざまな理由により、この形式で構成すると、スクリプトを実行する前にドキュメントの残りの部分が解析されます。

<body>
  <button id="test">click me</button>
  <script>
    document.getElementById("test").addEventListener("click", function() {
      console.log("clicked:", this);
    });
  </script>
</body><!-- closing body tag -->

これは理にかなっており、従来のブラウザにとっては確実な選択肢ですが、制限があり、より柔軟で現代的なアプローチが利用可能です。


オプション2:defer属性

スクリプトは「(デフォルトでは)遭遇するとすぐに実行される」と述べましたが、最近のブラウザでは異なる動作を指定することもできます。外部スクリプトをリンクする場合は、defer属性。

[ defer、ブール属性]は、スクリプトが文書の解析後、ただし発火前に実行されることをブラウザに示すために設定されます。DOMContentLoaded

つまり、 のタグが付けられたスクリプトはdefer、 であってもどこにでも配置でき<head>、完全に実現された DOM にアクセスできるはずです。

<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>

覚えておいてください...

  1. defer外部スクリプト、つまりsrc属性を持つスクリプトにのみ使用できます。
  2. 気づくブラウザのサポートつまり、IE 10 未満ではバグのある実装

オプション3: モジュール

要件に応じて、利用できる場合がありますJavaScript モジュール標準の文字とのその他の重要な違いとしては、ここで言及する) の場合、モジュールは自動的に延期され、外部ソースに限定されません。

スクリプトtypeを に設定しますmodule。例:

<script type="module">
  document.getElementById("test").addEventListener("click", function(e) {
    console.log("clicked: ", this);
  });
</script>
<button id="test">click me</button>


オプション4: イベント処理による遅延

ドキュメントが解析された後に発生するイベントにリスナーを追加します。

DOMContentLoaded イベント

DOMContentLoadedスタイルシートや画像などが読み込まれるのを待たずに、初期解析から DOM が完全に構築された後に実行されます。

<script>
  document.addEventListener("DOMContentLoaded", function(e){
    document.getElementById("test").addEventListener("click", function(e) {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>

ウィンドウ: ロードイベント

loadイベントは、DOMContentLoadedスタイルシートや画像などの追加リソースがロードされた後に発生します。そのため、このイベントは目的よりも遅く発生します。ただし、IE8などの古いブラウザを検討している場合は、サポートはほぼ普遍的です。確かに、ポリフィルaddEventListener()

<script>
  window.addEventListener("load", function(e){
    document.getElementById("test").addEventListener("click", function(e) {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>

jQueryのready()

DOMContentLoadedそれぞれwindow:load注意点があります。jQueryのready()DOMContentLoaded可能な場合は を使用し、必要な場合は にフェイルオーバーしwindow:load、DOM がすでに完了している場合はすぐにコールバックを実行するというハイブリッド ソリューションを提供します。

準備完了ハンドラーを次のように直接 jQuery に渡すことができます。$(handler)

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script>
  $(function() {
    $("#test").click(function() {
      console.log("clicked:", this);
    });
  });
</script>
<button id="test">click me</button>


オプション5: イベント委任

イベント処理をターゲット要素の祖先に委任します。

要素がイベントを発生させると(それが泡立つイベントが発生し、その伝播を止めるものがない場合、その要素の祖先である までの各親もwindowイベントを受け取ります。これにより、既存の要素にハンドラーをアタッチして、その子孫からイベントがバブルアップするときにサンプルを採取できます...ハンドラーがアタッチされた後に追加された子孫からでも。必要なのは、イベントをチェックして、目的の要素によって発生したかどうかを確認し、発生した場合はコードを実行することだけです。

通常、このパターンは、読み込み時に存在しない要素、または多数の重複ハンドラーのアタッチを回避するために予約されています。効率性を高めるために、 にアタッチするのではなく、ターゲット要素の最も近い信頼できる祖先を選択しますdocument

ネイティブJavaScript

<div id="ancestor"><!-- nearest ancestor available to our script -->
  <script>
    document.getElementById("ancestor").addEventListener("click", function(e) {
      if (e.target.id === "descendant") {
        console.log("clicked:", e.target);
      }
    });
  </script>
  <button id="descendant">click me</button>
</div>

jQueryのon()

jQueryはこの機能を次のように利用できる。on()イベント名、目的の子孫のセレクター、およびイベント ハンドラーを指定すると、委任されたイベント処理が解決され、thisコンテキストが管理されます。

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<div id="ancestor"><!-- nearest ancestor available to our script -->
  <script>
    $("#ancestor").on("click", "#descendant", function(e) {
      console.log("clicked:", this);
    });
  </script>
  <button id="descendant">click me</button>
</div>

おすすめ記事