web-dev-qa-db-ja.com

jQueryやgetElementByIdなどのDOMメソッドが要素を見つけられないのはなぜですか?

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

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

  • jQueryが黙ってイベントハンドラをバインドできない
  • undefinedを返すjQueryの "getter"メソッド(.val().html().text()
  • 標準のDOMメソッドでnullを返すと、いくつかのエラーが発生します。

    Uncaught TypeError:nullのプロパティ '...'を設定できませんUncaught TypeError:nullのプロパティ '...'を読み込めません

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

    捕捉されないTypeError:nullのプロパティ 'onclick'を設定することはできません

    捕捉されないTypeError:nullのプロパティ「addEventListener」を読み取ることができません

    捕捉されないTypeError:nullのプロパティ「style」を読み込めません

424
Felix Kling

あなたが見つけようとしていた要素は、スクリプトが実行されたときに DOM にありませんでした。

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

次のマークアップを考えてください。スクリプト#2は成功しますが、スクリプト#1は<div>を見つけることができません。

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

それで、あなたは何をすべきですか?いくつかの選択肢があります。


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

あなたのスクリプトをbodyの終了タグの直前のページのさらに下に移動します。このように編成されているため、残りの文書はスクリプトが実行される前に解析されます。

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

注:スクリプトを一番下に置くことは、一般的に ベストプラクティス と見なされます。


オプション2:jQueryのready()

ready() を使用して、DOMが完全に解析されるまでスクリプトを延期します。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(document).ready(function() {
    $("#test").click(function() {
      console.log("clicked: %o", this);
    });
  });
</script>
<button id="test">click me</button>

注意: DOMContentLoaded またはwindow.onloadにバインドすることもできますが、それぞれ注意点があります。 jQueryの ready() はハイブリッドソリューションを提供します。


オプション3:イベントの委任

デリゲートイベント には、後でドキュメントに追加された子孫要素からのイベントを処理できるという利点があります。

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

jQueryの on() がそのロジックを実行してくれます。イベント名、目的の子孫のセレクター、およびイベントハンドラーを提供するだけです。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(document).on("click", "#test", function(e) {
    console.log("clicked: %o",  this);
  });
</script>
<button id="test">click me</button>

注意:通常、このパターンはload-timeまたはに存在しなかった要素用に予約されているため、大量のハンドラを追加する必要がありません。念のためにdocumentにハンドラーをアタッチしましたが、最も信頼できる祖先を選択する必要があります。


オプション4:defer属性

<script>defer 属性を使用してください。

[defer、Boolean attribute、]は、ドキュメントが解析された後にスクリプトが実行されることになっていることをブラウザに示すために設定されます。

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

参考のために、これはその 外部スクリプト のコードです。

document.getElementById("test").addEventListener("click", function(e){
   console.log("clicked: %o", this); 
});

注:defer属性は確かにのようにのような魔法の弾丸のように見えますしかし警告に注意することは重要です。 。
1。 deferは外部スクリプト、すなわちsrc属性を持つスクリプトにのみ使用できます。
2。 ブラウザサポート に注意してください。すなわち、IE <10のバグのある実装です。

427
canon

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


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

考えられる理由

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

  1. 渡されたIDを持つ要素は、実際にはドキュメントに存在しません。 getElementByIdname__に渡すIDが(生成された)HTMLの既存の要素のIDと実際に一致し、IDが misspelled でないこと(IDは大文字と小文字を区別!)。

    ちなみに、querySelector()メソッドとquerySelectorAll()メソッドを実装する 現代のブラウザーの大半 では、CSSスタイル表記を使用して、idname__で要素を取得します。例:document.querySelector('#elementID')。これは、document.getElementById('elementID')の下のidname__によって要素を取得する方法とは対照的です。最初の#文字は必須であり、2番目の場合は要素が取得されないことになります。

  2. 要素は存在しません現時点ではgetElementByIdname__を呼び出します。

後者のケースは非常に一般的です。ブラウザはHTMLを上から下に解析および処理します。つまり、そのDOM要素がHTMLに表示される前に発生するDOM要素への呼び出しは失敗します。

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

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

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

divname__は /scriptname__の後に表示されます。スクリプトが実行された時点では、要素は存在しません yet で、getElementByIdname__はnullname__を返します。

jQuery

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

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

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

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

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


ソリューション

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

これは、JavaScript after を対応するDOM要素に置くだけで確認できます

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

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

この場合、コードをbody終了タグ(</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:スキームを追加し、パスの調整など)

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

実際、 jQueryチュートリアル は明示的に次のように述べています:

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

これを行うには、ドキュメントのreadyイベントを登録します。

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

別の方法として、簡単な構文を使用することもできます。

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

両方とも同等です。

141
Felix Kling

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

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

解決策

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

  2. Ajaxレスポンスから来る要素には、jQueryの.bind()メソッドを使います。古いバージョンのjQueryでも同じことに.live()がありました。

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

13
sumit

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

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

同様に、新しい "Shadow DOM"機能は、最近のブラウザでロールアウトされていますが、要素をドキュメントの一部にすることはできますが、document.getElementByIdおよびそのすべての兄弟メソッド(querySelectorなど)ではクエリできません。これは機能性をカプセル化し、特にそれを隠すために行われます。

しかし、繰り返しますが、あなたが探している要素は(まだ)文書内にはないということが最も可能性が高いので、Felixが示唆するようにするべきです。しかし、それが要素が(一時的または恒久的に)見つけられないかもしれない唯一の理由ではなくなっていることにも注意する必要があります。

11
Nathan Bubna

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

Iframeの中の要素を取得したい場合は、ここでどうやって を調べることができます

10
George Mulligan

スクリプトの実行順序が問題ではない場合、問題の他の考えられる原因は要素が正しく選択されていないことです。

  • getElementByIdは、渡された文字列がID逐語的であることを要求し、それ以外は何もしません。渡された文字列の前に#を付け、IDが#で始まらない場合は、何も選択されません。

    <div id="foo"></div>
    
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
    
  • 同様に、getElementsByClassNameでは、渡された文字列の前に.を付けないでください。

    <div class="bar"></div>
    
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
    
  • QuerySelector、querySelectorAll、およびjQueryでは、要素を特定のクラス名と一致させるには、クラスの直前に.を配置します。同様に、要素を特定のIDと一致させるには、IDの直前に#を置きます。

    <div class="baz"></div>
    
    // Error, selected element will be null:
    document.querySelector('baz')
    $('baz')
    // Fix:
    document.querySelector('.baz')
    $('.baz')
    

    ここでの規則は、ほとんどの場合、CSSセレクターの規則と同じです。詳細については、 こちら をご覧ください。

  • 大文字とスペルは上記のすべてに重要です。大文字の使用方法が異なる場合、またはスペルが異なる場合、要素は選択されません。

    <div class="result"></div>
    
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
    
2

問題は、JavaScript要素の実行時にdom要素「speclist」が作成されないことです。そのため、関数内にjavascriptコードを配置し、body onloadイベントでその関数を呼び出しました。

function do_this_first(){
   //appending code
}

<body onload="do_this_first()">
</body>
0
Mia Davis