1. ホーム
  2. javascript

[解決済み] jQueryやgetElementByIdのようなDOMメソッドが、要素を見つけられないのはなぜですか?

2022-03-17 10:38:25

質問

の理由として考えられることは何ですか? document.getElementById , $("#id") または他のDOMメソッド/ jQueryセレクタが要素を見つけることができませんか?

問題例としては、以下のようなものがあります。

  • jQueryがイベントハンドラのバインドに無言で失敗する。
  • jQuery の "getter" メソッド ( .val() , .html() , .text() ) を返します。 undefined
  • を返す標準的な DOM メソッドです。 null が発生し、いくつかのエラーが発生します。
<ブロッククオート

Uncaught TypeError: null のプロパティ '...' を設定できません。
Uncaught TypeError: nullのプロパティを設定できない('...'を設定)。
Uncaught TypeError: null のプロパティ '...' を読み取れない
Uncaught TypeError: nullのプロパティを読み取れない('...'を読み取る)。

最も一般的な形は

Uncaught TypeError: プロパティ 'onclick' に null を設定できない。
Uncaught TypeError: nullのプロパティ'addEventListener'を読み取れませんでした。
Uncaught TypeError: nullのプロパティ'style'を読み取れませんでした。

解決方法は?

探そうとしていた要素は DOM スクリプトを実行したときに

DOMに依存するスクリプトの位置は、その動作に大きな影響を与えることがあります。ブラウザはHTMLドキュメントを上から下へと解析します。要素は DOM に追加され、スクリプトは(一般に)遭遇したときに実行されます。 つまり、順番が重要なのです。 一般に、マークアップの後半に登場する要素は、まだDOMに追加されていないため、スクリプトは見つけることができません。

次のようなマークアップを考えてみましょう。 <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()

DOMContentLoadedwindow:load にはそれぞれ注意点があります。 ready() を使用したハイブリッドソリューションを提供します。 DOMContentLoaded にフェイルオーバーします。 window:load DOM が既に完成している場合は、即座にコールバックを実行します。

ready ハンドラを直接 jQuery に渡すには、以下のようにします。 $(handler) , 例:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></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://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></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>