jQueryまたはgetElementByIdなどのDOMメソッドが要素を見つけられないのはなぜですか?
document.getElementById
、$("#id")
または他のDOMメソッド/ jQueryセレクターが要素を見つけられない理由として考えられるものは何ですか?
問題の例は次のとおりです。
- jQueryがイベントハンドラーのバインドに黙って失敗する
- jQueryの"ゲッター"メソッド(
.val()
、.html()
、.text()
)を返しますundefined
null
いくつかのエラーのいずれかが発生する標準のDOMメソッドが返されます。
Uncaught TypeError:nullのプロパティ '...'を設定できませんUncaughtTypeError:nullのプロパティ '...'を読み取れません
最も一般的な形式は次のとおりです。
Uncaught TypeError:nullのプロパティ 'onclick'を設定できません
Uncaught TypeError:nullのプロパティ 'addEventListener'を読み取れません
Uncaught TypeError:nullのプロパティ 'style'を読み取れません
回答
スクリプトの実行時に、検索しようとした要素がDOMにありませんでした。
DOMに依存するスクリプトの位置は、その動作に大きな影響を与える可能性があります。ブラウザはHTMLドキュメントを上から下に解析します。要素はDOMに追加され、スクリプトは(通常)検出されたときに実行されます。これは、順序が重要であることを意味します。通常、スクリプトは、マークアップの後半に表示される要素を見つけることができません。これらの要素はまだDOMに追加されていないためです。
次のマークアップを検討してください。スクリプト#1は<div>
、スクリプト#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.onload
ready()
オプション3:イベントの委任
委任されたイベントには、後でドキュメントに追加される子孫要素からのイベントを処理できるという利点があります。
要素がイベントを発生させると(それがバブリングイベントであり、その伝播を停止するものがない場合)、その要素の祖先の各親もイベントを受け取ります。これにより、ハンドラーを既存の要素にアタッチし、イベントが子孫からバブルアップするときにイベントをサンプリングできます...ハンドラーがアタッチされた後に追加されたものも含まれます。イベントをチェックして、目的の要素によって発生したかどうかを確認し、発生した場合はコードを実行するだけです。
jQueryon()
はそのロジックを実行してくれます。イベント名、目的の子孫のセレクター、およびイベントハンドラーを提供するだけです。
<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でのバグのある実装
短くて単純:探している要素がドキュメントに(まだ)存在しないためです。
この回答の残りのために私が使用するgetElementById
一例として、しかし同様に当てはまりgetElementsByTagName
、querySelector
選択要素こと、および任意の他のDOM方法。
考えられる理由
要素が存在しない理由は2つあります。
渡されたIDを持つ要素は、実際にはドキュメントに存在しません。渡す
getElementById
IDが(生成された)HTMLの既存の要素のIDと実際に一致していること、およびIDのスペルを間違えていないことを再確認する必要があります(IDでは大文字と小文字が区別されます!)。ちなみに、実装とメソッドを備えた現代のブラウザの大部分では、CSSスタイルの表記法を使用して、要素をその下で取得する方法とは対照的に、たとえば次のように要素を取得します。最初の文字は必須であり、2番目の文字は要素が取得されないことにつながります。
querySelector()
querySelectorAll()
id
document.querySelector('#elementID')
id
document.getElementById('elementID')
#
呼び出した時点では、要素は存在しません
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:
または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
});
どちらも同等です。
アクセスしようとしている要素が内にiframe
あり、コンテキスト外でアクセスしようとするiframe
と、失敗する原因にもなります。
iframeで要素を取得したい場合は、ここでその方法を確認できます。
IDベースのセレクターが機能しない理由
- IDが指定された要素/ DOMはまだ存在しません。
- 要素は存在しますが、DOMに登録されていません[Ajax応答から動的に追加されたHTMLノードの場合]。
- 同じIDを持つ要素が複数存在するため、競合が発生しています。
ソリューション
宣言後に要素にアクセスするか、次のようなものを使用してみてください
$(document).ready();
Ajax応答からの要素に
.bind()
は、jQueryのメソッドを使用します。古いバージョンのjQuery.live()
も同じです。ツール[ブラウザ用のwebdeveloperプラグインなど]を使用して、重複するIDを見つけて削除します。
@FelixKlingが指摘したように、最も可能性の高いシナリオは、探しているノードが(まだ)存在しないことです。
ただし、最近の開発手法では、DocumentFragmentsを使用するか、現在の要素を直接デタッチ/再アタッチするだけで、ドキュメントツリーの外部のドキュメント要素を操作できることがよくあります。このような手法は、JavaScriptテンプレートの一部として、または問題の要素が大幅に変更されている間の過度の再描画/リフロー操作を回避するために使用できます。
同様に、最新のブラウザに展開されている新しい「シャドウDOM」機能を使用すると、要素をドキュメントの一部にすることができますが、document.getElementByIdとそのすべての兄弟メソッド(querySelectorなど)でクエリを実行することはできません。これは、機能をカプセル化し、具体的に非表示にするために行われます。
繰り返しになりますが、探している要素が(まだ)ドキュメントに含まれていない可能性が高いため、Felixが示唆しているように行う必要があります。ただし、要素が(一時的または永続的に)見つからない理由は、それだけではないことにも注意する必要があります。
スクリプトの実行順序が問題ではない場合、問題のもう1つの考えられる原因は、要素が適切に選択されていないことです。
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')
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
はエラーをスローします。これらのセレクターメソッドに文字列を渡すようにしてください。文字列ではないものを、などに渡すと
querySelector
、getElementById
ほぼ確実に機能しません。選択する要素のHTML属性が引用符で囲まれている場合、それらは単純な一重引用符(一重引用符または二重引用符)である必要があります。ID、クラス、または属性で選択しようとしている場合
‘
、中引用符”
は機能するか、機能しません。
私があなたのコードを試したとき、それはうまくいきました。
イベントが機能していない唯一の理由は、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インクルードをページの下部に移動します。
これがお役に立てば幸いです。