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


483

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

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

  • イベントハンドラーのバインドに失敗するjQuery
  • jQueryの"ゲッター"メソッド(.val().html().text())を返しますundefined
  • nullいくつかのエラーのいずれかを返す標準のDOMメソッド:

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

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

Uncaught TypeError:nullのプロパティ 'onclick'を設定できません

Uncaught TypeError:nullのプロパティ 'addEventListener'を読み取れません

Uncaught TypeError:nullのプロパティ 'style'を読み取れません


32
特定のDOM要素が見つからない理由について多くの質問が行われます。その理由は、多くの場合、JavaScriptコードがDOM要素の前に配置されているためです。これは、これらのタイプの質問に対する標準的な回答になることを目的としています。コミュニティウィキですので、お気軽にどうぞ
Felix Kling

回答:


481

スクリプトを実行したときに、探していた要素がDOMにありませんでした。

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

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

<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()

以下を使用して、DOMが完全に解析されるまでスクリプトを延期します。$(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: %o", this);
    });
  });
</script>
<button id="test">click me</button>

注:単純に結合することができるDOMContentLoadedか が、それぞれがその警告を持っています。jQuery はハイブリッドソリューションを提供します。window.onloadready()


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

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

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

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>

注:通常、このパターンは、ロード時に存在しなかった要素、または大量のハンドラーのアタッチを回避するために予約されています。また、ハンドラーをdocument(説明のために)アタッチしている間、最も近い信頼できる祖先を選択する必要があることも指摘する価値があります。


オプション4:defer属性

defer属性を使用します<script>

[ defer、ブール属性、]は、ドキュメントが解析された後、実行される前にスクリプトが実行されることをブラウザに示すために設定されますDOMContentLoaded

<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でのバグのある実装


それは2020年です—「スクリプトを下部に配置する」ことはまだ「ベストプラクティスと見なされていますか」?私は(今日)すべてのリソースをに入れてスクリプト<head>で使用deferします(古くて互換性のないブラウザーをサポートする必要はありません)
Stephen P

私は最新のブラウザーに移行することを強く支持しています。とは言っても、この方法は実際には何の費用もかからず、どこでも機能します。一方、両方deferasyncバックいくつかの非常に古いバージョンのブラウザに到達-evenかなり幅広くサポートしています。これらには、意図された動作を明確かつ明示的に通知するという追加の利点があります。これらの属性は、src属性を指定するスクリプト(つまり、外部スクリプト)でのみ機能することに注意してください。メリットを比較検討します。多分両方を使用しますか?あなたの電話。
カノン

146

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


この回答の残りのために私が使用するgetElementById一例として、しかし同様に当てはまりgetElementsByTagNamequerySelector選択要素こと、および任意の他のDOM方法。

考えられる理由

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

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

    ちなみに、実装とメソッドを実装している現代のブラウザ大部分では、CSSスタイルの表記法を使用して要素を要素ごとに取得しています。たとえば、要素が要素によって取得されるのではなく、最初の文字は必須で、2番目の文字は要素が取得されない原因になります。querySelector()querySelectorAll()iddocument.querySelector('#elementID')iddocument.getElementById('elementID')#

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

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

次の例を考えてみます。

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

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

はのdiv表示さますscript。スクリプトが実行された時点では、要素はまだ存在せgetElementByIdを返しnullます。

jQuery

jQueryを使用するすべてのセレクターにも同じことが当てはまります。セレクタのスペル間違えたり、実際に存在する前にそれらを選択しようとしたりすると、jQueryは要素を見つけられません。

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

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

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

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


ソリューション

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

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

<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:or https:スキームを追加する、パスを調整するなど)。

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

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

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

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

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

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

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

どちらも同等です。


1
readyイベントの最後のビットを修正して「新しい」優先構文を反映する価値はありますか、それともそのままにして古いバージョンで機能するようにした方がよいでしょうか?
Tieson T.

1
jQueryのために、それはまた別の方法で追加する価値がある$(window).load()(そして多分に構文上の選択肢$(document).ready()$(function(){})?それは、関連すると思われるが、それは感じているあなたがいるづくりのポイントにわずか接線方向。
デビッドREINSTATEモニカ言う

@David:の良い点.load。代替構文については、ドキュメントで検索できますが、回答は自己完結型であるため、実際に追加する価値があります。次に、jQueryに関するこの特定のビットは既に回答されており、おそらく別の回答でカバーされているので、それにリンクするだけで十分だと確信しています。
Felix Kling、

1
@David:私は改訂する必要があります:どうやら.load非推奨です:api.jquery.com/load-event。画像専用かどうかはわかりませんwindow
Felix Kling

1
@David:心配ありません:)わからない場合でも、あなたがそれを言ったのは良いことです。使用法を示すために、おそらくいくつかの単純なコードスニペットのみを追加します。ご協力ありがとうございます。
Felix Kling

15

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

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

ソリューション

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

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

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


13

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

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

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

ただし、繰り返しになりますが、探している要素がドキュメントに(まだ)含まれていない可能性が高いので、Felixの提案に従ってください。ただし、要素が(一時的または永続的に)検索できない唯一の理由はそれだけではないことにも注意してください。


13

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

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


7

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

  • 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セレクターのルールと同じであり、ここで詳しく確認できます

  • 2つ以上の属性(2つのクラス名、またはクラス名とdata-属性など)を持つ要素を一致させるには、各文字列のセレクターをセレクター文字列の隣に配置し、スペースで区切らないようにします(スペースは子孫セレクタ)。たとえば、次のように選択します。

    <div class="foo bar"></div>

    クエリ文字列を使用します.foo.bar。選ぶ

    <div class="foo" data-bar="someData"></div>

    クエリ文字列を使用します.foo[data-bar="someData"]<span>以下を選択するには:

    <div class="parent">
      <span data-username="bob"></span>
    </div>

    使用しますdiv.parent > span[data-username="bob"]

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

    <div class="result"></div>
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
  • また、メソッドに適切な大文字とスペルがあることを確認する必要もあります。次のいずれかを使用します。

    $(selector)
    document.querySelector
    document.querySelectorAll
    document.getElementsByClassName
    document.getElementsByTagName
    document.getElementById

    他のスペルや大文字は機能しません。たとえばdocument.getElementByClassName、エラーがスローされます。

  • これらのセレクターメソッドに文字列を渡すようにしてください。あなたがに文字列ではない何かを渡した場合querySelectorgetElementByIdなどが、それはほぼ確実に動作しません。


0

私があなたのコードを試したところ、うまくいきました。

イベントが機能していない唯一の理由は、DOMが準備されておらず、IDが「event-btn」のボタンがまだ準備されていない可能性があります。そしてあなたのjavascriptが実行されて、イベントをその要素にバインドしようとしました。

バインドにDOM要素を使用する前に、その要素を準備する必要があります。それには多くのオプションがあります。

オプション1:イベント準備コード内でイベントバインディングコードを移動できます。お気に入り:

document.addEventListener('DOMContentLoaded', (event) => {
  //your code to bind the event
});

オプション2:タイムアウトイベントを使用して、バインディングが数秒間遅延するようにすることができます。お気に入り:

setTimeout(function(){
  //your code to bind the event
}, 500);

オプション3:JavaScriptインクルードをページの下部に移動します。

これがお役に立てば幸いです。


@ Willdomybest18、この回答を確認してください
Jatinder Kumar

-1

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

function do_this_first(){
   //appending code
}

<body onload="do_this_first()">
</body>

-1

私の解決策は、アンカー要素のIDを使用しないことでした:<a id='star_wars'>Place to jump to</a>。どうやらブレイザーと他のスパフレームワークは同じページのアンカーにジャンプする問題を持っています。それを回避するには、私は使用しなければなりませんでしたdocument.getElementById('star_wars')。ただし、代わりに段落要素にIDを配置するまで、これは機能しませんでした。<p id='star_wars'>Some paragraph<p>

ブートストラップを使用した例:

<button class="btn btn-link" onclick="document.getElementById('star_wars').scrollIntoView({behavior:'smooth'})">Star Wars</button>

... lots of other text

<p id="star_wars">Star Wars is an American epic...</p>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.