555

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

問題の例は次のとおりです。

  • jQueryがイベントハンドラーのバインドに黙って失敗する
  • jQueryの「getter」メソッド(、、.val().html().text()返されるundefined
  • nullいくつかのエラーのいずれかが発生する標準のDOMメソッドが返されます。

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

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

Uncaught TypeError:nullのプロパティ'onclick'を設定できませんUncaughtTypeError:null
のプロパティ'addEventListener'を読み取れませんUncaughtTypeError:null
のプロパティ'style'を読み取れません

4

6 に答える 6

558

スクリプトの実行時に、検索しようとした要素がDOMにありませんでした。

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

次のマークアップを検討してください。<div>スクリプト#1は、スクリプト#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. defersrc外部スクリプト、つまり属性を持つスクリプトにのみ使用できます。
  2. ブラウザのサポートに注意してください。つまり、IE<10でのバグのある実装

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

要件によっては、JavaScriptモジュールを利用できる場合があります。標準スクリプト(ここに記載)とのその他の重要な違いの中でも、モジュールは自動的に延期され、外部ソースに限定されません。

スクリプトを次のように設定しますtypemodule例:

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


オプション4:イベント処理を延期する

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

DOMContentLoadedイベント

DOMContentLoadedDOMが最初の解析から完全に構築された後、スタイルシートや画像などが読み込まれるのを待たずに起動します。

<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それぞれに注意点があります。jQueryは、可能な場合はを使用し、必要な場合はフェイルオーバーし、DOMがすでに完了している場合はすぐにコールバックを起動ready()する、ハイブリッドソリューションを提供します。DOMContentLoadedwindow:load

次のように、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>

于 2012-01-03T18:06:51.617 に答える
163

短くて単純:探している要素がドキュメントに(まだ)存在しないためです。


この回答の残りの部分では例を使用しますが、、、、および要素を選択するその他のDOMメソッドにも同じgetElementByIdことが当てはまります。getElementsByTagNamequerySelector

考えられる理由

要素が存在しない理由は3つあります。

  1. 渡されたIDを持つ要素は、実際にはドキュメントに存在しません。渡すgetElementByIdIDが(生成された)HTMLの既存の要素のIDと実際に一致していること、およびIDのスペルを間違えていないことを再確認する必要があります(IDでは大文字と小文字が区別されます!)。

    を使用している場合は、要素のIDのみgetElementByIdを指定していることを確認してください(例:) 。CSSセレクター(など)を受け入れるメソッドを使用している場合は、IDを探していることを示すためにIDの前に必ずインクルードしてください(例:)。withを使用してはなら、 withおよび同様のものを使用する必要があります。また、IDにCSS識別子で無効な文字が含まれている場合(たとえば、文字を含む属性は不適切ですが有効です)、())を使用する場合はエスケープする必要がありますが、 ()を使用する場合はエスケープする必要があります。 。document.getElemntById("the-id")querySelector#document.querySelector("#the-id")#getElementByIdquerySelector.id.querySelectordocument.querySelector("#the\\.id")getElementByIddocument.getElementById("the.id")

  2. 呼び出した時点では、要素は存在しませんgetElementById

  3. iframe要素は(独自のドキュメントである)にあるため、ページに表示されていても、クエリしているドキュメントには含まれていません。の要素は、iframesそれらを含むドキュメントを検索するときに検索されません。

問題が理由3(または類似のもの)である場合は、おそらく要素を取得し、そのプロパティを使用してそのドキュメントにアクセスすることにより、親ドキュメントではなく、iframe内のドキュメントを調べる必要があります(同一生成元のみ)。この回答の残りの部分では、最初の2つの理由について説明します。iframeiframecontentDocument

2番目の理由-まだそこにはありません -は非常に一般的です。ブラウザはHTMLを上から下に解析して処理します。つまり、そのDOM要素がHTMLに表示される前に発生するDOM要素の呼び出しは、失敗します。

次の例を考えてみましょう。

<script>
    var element = document.getElementById('my_element');
</script>

<div id="my_element"></div>

div表示されます。スクリプトが実行された時点では、要素はまだ存在しておらず、を返します。scriptgetElementByIdnull

jQuery

同じことがjQueryのすべてのセレクターに当てはまります。セレクターのスペルを間違えた場合、または実際に存在する前に要素を選択しようとした場合、jQueryは要素を検出しません。

追加のひねりは、プロトコルなしでスクリプトをロードし、ファイルシステムから実行しているためにjQueryが見つからない場合です。

<script src="//somecdn.somewhere.com/jquery.min.js"></script>

この構文は、プロトコルhttps://のページにHTTPS経由でスクリプトをロードし、プロトコルhttp://のページにHTTPバージョンをロードできるようにするために使用されます。

ロードを試みたり失敗したりするという不幸な副作用がありますfile://somecdn.somewhere.com...


ソリューション

を呼び出す前にgetElementById(またはそのことについては任意のDOMメソッド)、アクセスする要素が存在することを確認してください。つまり、DOMがロードされていることを確認してください。

これは、JavaScriptを対応するDOM要素の後に置くだけで確実になります

<div id="my_element"></div>

<script>
    var element = document.getElementById('my_element');
</script>

この場合、本文の終了タグ()の直前にコードを配置することもできます</body>(すべてのDOM要素はスクリプトの実行時に使用可能になります)。

他の解決策には、load [MDN]またはDOMContentLoaded [MDN]イベントのリッスンが含まれます。このような場合、JavaScriptコードをドキュメントのどこに配置するかは問題ではなく、すべてのDOM処理コードをイベントハンドラーに配置することを忘れないでください。

例:

window.onload = function() {
    // process DOM elements here
};

// or

// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
    // process DOM elements here
});

イベント処理とブラウザの違いの詳細については、quirksmode.orgの記事を参照してください。

jQuery

まず、jQueryが正しく読み込まれていることを確認します。ブラウザの開発者ツールを使用して、jQueryファイルが見つかったかどうかを確認し、見つからなかった場合はURLを修正します(たとえば、最初にhttp:またはhttps:スキームを追加したり、パスを調整したりします)。

load/イベントをリッスンすることは、DOMContentLoaded jQueryが.ready() [docs]で行っていることとまったく同じです。DOM要素に影響を与えるすべてのjQueryコードは、そのイベントハンドラー内にある必要があります。

実際、jQueryチュートリアルには次のように明示的に記載されています。

jQueryを使用するときに行うほとんどすべてのことは、ドキュメントオブジェクトモデル(DOM)を読み取ったり操作したりするため、DOMの準備ができたらすぐにイベントなどの追加を開始する必要があります。

これを行うために、ドキュメントの準備完了イベントを登録します。

$(document).ready(function() {
   // do stuff when DOM is ready
});

または、省略構文を使用することもできます。

$(function() {
    // do stuff when DOM is ready
});

どちらも同等です。

于 2012-12-25T08:26:26.553 に答える
16

IDベースのセレクターが機能しない理由

  1. IDが指定された要素/DOMはまだ存在していません。
  2. 要素は存在しますが、DOMに登録されていません[Ajax応答から動的に追加されたHTMLノードの場合]。
  3. 同じIDを持つ要素が複数存在するため、競合が発生しています。

ソリューション

  1. 宣言後に要素にアクセスするか、次のようなものを使用してみてください$(document).ready();

  2. Ajax応答からの要素には.bind()、jQueryのメソッドを使用します。古いバージョンのjQuery.live()も同じです。

  3. ツール[たとえば、ブラウザ用のwebdeveloperプラグイン]を使用して、重複するIDを見つけ、それらを削除します。

于 2014-01-07T12:12:25.600 に答える
16

アクセスしようとしている要素が内にありiframe、コンテキスト外でアクセスしようとすると、iframeこれも失敗します。

iframe内の要素を取得したい場合は、ここでその方法を確認できます。

于 2016-01-05T23:34:24.880 に答える
14

@FelixKlingが指摘したように、最も可能性の高いシナリオは、探しているノードが(まだ)存在しないことです。

ただし、最近の開発手法では、DocumentFragmentsを使用するか、現在の要素を直接デタッチ/再アタッチするだけで、ドキュメントツリーの外部でドキュメント要素を操作できることがよくあります。このような手法は、JavaScriptテンプレートの一部として、または問題の要素が大幅に変更されている間の過度の再描画/リフロー操作を回避するために使用できます。

同様に、最新のブラウザに展開されている新しい「Shadow DOM」機能を使用すると、要素をドキュメントの一部にすることができますが、document.getElementByIdとそのすべての兄弟メソッド(querySelectorなど)でクエリを実行することはできません。これは、機能をカプセル化し、具体的に非表示にするために行われます。

繰り返しになりますが、探している要素が(まだ)ドキュメントに含まれていない可能性が高いため、Felixが提案しているように実行する必要があります。ただし、要素が(一時的または永続的に)見つからない理由は、これだけではないことにも注意する必要があります。

于 2013-12-06T17:29:33.410 に答える
12
于 2019-05-10T09:29:26.920 に答える