Warum findet jQuery oder eine DOM-Methode wie getElementById das Element nicht?

483

Was sind die möglichen Gründe dafür document.getElementById, dass $("#id")eine andere DOM-Methode / ein anderer jQuery-Selektor die Elemente nicht findet?

Beispielprobleme umfassen:

  • jQuery kann einen Ereignishandler stillschweigend nicht binden
  • jQuery "Getter" -Methoden ( .val(), .html(), .text()) zurückkehrundefined
  • Eine Standard-DOM-Methode, nulldie zurückgibt und zu mehreren Fehlern führt:

Nicht erfasster TypeError: Eigenschaft '...' von null kann nicht festgelegt werden Nicht erfasster TypeError: Eigenschaft '...' von null kann nicht gelesen werden

Die häufigsten Formen sind:

Nicht erfasster TypeError: Die Eigenschaft 'onclick' von null kann nicht festgelegt werden

Nicht erfasster TypeError: Die Eigenschaft 'addEventListener' von null kann nicht gelesen werden

Nicht erfasster TypeError: Die Eigenschaft 'style' von null kann nicht gelesen werden

Felix Kling
quelle
32
Es werden viele Fragen gestellt, warum ein bestimmtes DOM-Element nicht gefunden wird, und der Grund liegt häufig darin, dass der JavaScript-Code vor dem DOM-Element steht. Dies soll eine kanonische Antwort auf diese Art von Fragen sein. Es ist ein Community-Wiki, also zögern Sie nicht, es zu verbessern .
Felix Kling

Antworten:

481

Das Element, das Sie suchen wollten, befand sich nicht im DOM, als Ihr Skript ausgeführt wurde.

Die Position Ihres DOM-abhängigen Skripts kann einen tiefgreifenden Einfluss auf sein Verhalten haben. Browser analysieren HTML-Dokumente von oben nach unten. Dem DOM werden Elemente hinzugefügt und Skripte werden (im Allgemeinen) ausgeführt, sobald sie angetroffen werden. Dies bedeutet, dass die Reihenfolge wichtig ist. In der Regel können Skripte keine Elemente finden, die später im Markup angezeigt werden, da diese Elemente noch nicht zum DOM hinzugefügt werden müssen.

Betrachten Sie das folgende Markup. Skript Nr. 1 findet das nicht, <div>während Skript Nr. 2 erfolgreich ist:

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

Also, was solltest du tun? Sie haben einige Möglichkeiten:


Option 1: Verschieben Sie Ihr Skript

Bewegen Sie Ihr Skript weiter nach unten, kurz vor dem schließenden Body-Tag. Auf diese Weise organisiert, wird der Rest des Dokuments analysiert, bevor Ihr Skript ausgeführt wird:

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

Hinweis: Das Platzieren von Skripten am unteren Rand wird im Allgemeinen als bewährte Methode angesehen .


Option 2: jQuery's ready()

Verschieben Sie Ihr Skript, bis das DOM vollständig analysiert wurde. Verwenden Sie dazu :$(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>

Hinweis: Sie können einfach an DOMContentLoadedoder binden, aber jeder hat seine Vorbehalte. jQuery's liefert eine Hybridlösung.window.onloadready()


Option 3: Ereignisdelegation

Delegierte Ereignisse haben den Vorteil, dass sie Ereignisse von untergeordneten Elementen verarbeiten können, die zu einem späteren Zeitpunkt zum Dokument hinzugefügt werden.

Wenn ein Element ein Ereignis auslöst (vorausgesetzt, es ist ein sprudelndes Ereignis und nichts stoppt seine Ausbreitung), erhält auch jedes übergeordnete Element in der Abstammung dieses Elements das Ereignis. Auf diese Weise können wir einen Handler an ein vorhandenes Element anhängen und Ereignisse abtasten, wenn diese aus seinen Nachkommen sprudeln ... auch solche, die nach dem Anhängen des Handlers hinzugefügt wurden. Wir müssen lediglich das Ereignis überprüfen, um festzustellen, ob es vom gewünschten Element ausgelöst wurde, und in diesem Fall unseren Code ausführen.

jQuery's on()führt diese Logik für uns aus. Wir geben einfach einen Ereignisnamen, eine Auswahl für den gewünschten Nachkommen und einen Ereignishandler an:

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

Hinweis: In der Regel ist dieses Muster für Elemente reserviert, die zum Zeitpunkt des Ladens nicht vorhanden waren, oder um das Anhängen einer großen Anzahl von Handlern zu vermeiden. Es ist auch erwähnenswert, dass Sie, obwohl ich document(zu Demonstrationszwecken) einen Handler angehängt habe , den nächstgelegenen zuverlässigen Vorfahren auswählen sollten.


Option 4: Das deferAttribut

Verwenden Sie das deferAttribut von <script>.

[ defer, ein boolesches Attribut,] gibt einem Browser an, dass das Skript ausgeführt werden soll, nachdem das Dokument analysiert wurde, jedoch bevor es ausgelöst wird DOMContentLoaded.

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

Als Referenz ist hier der Code aus diesem externen Skript :

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

Hinweis: Das deferAttribut scheint sicherlich ein Wundermittel zu sein, aber es ist wichtig, sich der Einschränkungen bewusst zu sein ...
1. deferKann nur für externe Skripte verwendet werden, dh für solche mit einem srcAttribut.
2. Beachten Sie die Browserunterstützung , dh die fehlerhafte Implementierung in IE <10

Kanon
quelle
Es ist jetzt 2020 - wird "Platzieren von Skripten am unteren Rand" immer noch als "Best Practice" angesehen? Ich (heutzutage) setze alle meine Ressourcen in das <head>und verwende es deferfür Skripte (ich muss keine schlechten alten inkompatiblen Browser unterstützen)
Stephen P
Ich bin ein großer Befürworter der Umstellung auf moderne Browser. Das heißt, diese Praxis kostet mich nichts und funktioniert einfach überall. Auf der anderen Seite haben beide deferund asynceine ziemlich breite Unterstützung - auch wenn sie auf einige viel ältere Browserversionen zurückgreifen. Sie haben den zusätzlichen Vorteil, das beabsichtigte Verhalten klar und explizit zu signalisieren. Denken Sie daran, dass diese Attribute nur für Skripte funktionieren, die ein srcAttribut angeben , dh für externe Skripte. Wägen Sie die Vorteile ab. Vielleicht beides verwenden? Ihr Anruf.
Kanon
146

Kurz und einfach: Weil die Elemente, nach denen Sie suchen, (noch) nicht im Dokument vorhanden sind.


Für den Rest dieser Antwort wird ich getElementByIdals Beispiel, aber das gleiche gilt für getElementsByTagName, querySelectorund andere Verfahren , dass DOM wählt Elemente.

Mögliche Gründe

Es gibt zwei Gründe, warum ein Element möglicherweise nicht vorhanden ist:

  1. Ein Element mit der übergebenen ID ist im Dokument wirklich nicht vorhanden. Sie sollten überprüfen, ob die ID, an die Sie übergeben, getElementByIdwirklich mit der ID eines vorhandenen Elements im (generierten) HTML übereinstimmt und ob Sie die ID nicht falsch geschrieben haben (bei IDs wird zwischen Groß- und Kleinschreibung unterschieden !).

    Im übrigen wird in der Mehrzahl der modernen Browser , die Umsetzung querySelector()und querySelectorAll()Methoden, CSS-Notation verwendet wird , ein Element von seinen abzurufen id, zum Beispiel: document.querySelector('#elementID')wie das Verfahren im Gegensatz von dem ein Element durch seine abgerufen wird idunter document.getElementById('elementID'); im ersten #Fall ist das Zeichen wesentlich, im zweiten Fall würde es dazu führen, dass das Element nicht abgerufen wird.

  2. Das Element ist zum Zeitpunkt Ihres Aufrufs nicht vorhanden getElementById.

Der letztere Fall ist ziemlich häufig. Browser analysieren und verarbeiten den HTML-Code von oben nach unten. Das bedeutet, dass jeder Aufruf eines DOM-Elements, der vor dem Erscheinen dieses DOM-Elements im HTML erfolgt, fehlschlägt.

Betrachten Sie das folgende Beispiel:

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

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

Das diverscheint nach dem script. Im Moment ist das Skript ausgeführt wird, wird das Element nicht existiert noch und getElementByIdwird zurückkehren null.

jQuery

Gleiches gilt für alle Selektoren mit jQuery. jQuery findet keine Elemente, wenn Sie Ihren Selektor falsch geschrieben haben oder versuchen, sie auszuwählen, bevor sie tatsächlich vorhanden sind .

Eine zusätzliche Wendung ist, wenn jQuery nicht gefunden wird, weil Sie das Skript ohne Protokoll geladen haben und vom Dateisystem ausgeführt werden:

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

Diese Syntax wird verwendet, um das Laden des Skripts über HTTPS auf eine Seite mit dem Protokoll https: // und das Laden der HTTP-Version auf eine Seite mit dem Protokoll http: // zu ermöglichen

Es hat den unglücklichen Nebeneffekt, dass versucht wird und nicht geladen wird file://somecdn.somewhere.com...


Lösungen

Stellen Sie vor dem Aufrufen getElementById(oder einer anderen DOM-Methode) sicher, dass die Elemente vorhanden sind, auf die Sie zugreifen möchten, dh das DOM wird geladen.

Dies kann sichergestellt werden, indem Sie einfach Ihr JavaScript nach dem entsprechenden DOM-Element setzen

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

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

In diesem Fall können Sie den Code auch direkt vor dem schließenden Body-Tag ( </body>) einfügen (alle DOM-Elemente sind zum Zeitpunkt der Ausführung des Skripts verfügbar).

Andere Lösungen umfassen das Abhören der Ereignisse load [MDN] oder DOMContentLoaded [MDN] . In diesen Fällen spielt es keine Rolle, wo in dem Dokument Sie den JavaScript-Code platzieren. Sie müssen lediglich daran denken, den gesamten DOM-Verarbeitungscode in die Ereignishandler einzufügen.

Beispiel:

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

// or

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

Weitere Informationen zur Ereignisbehandlung und zu Browserunterschieden finden Sie in den Artikeln auf quirksmode.org .

jQuery

Stellen Sie zunächst sicher, dass jQuery ordnungsgemäß geladen ist. Verwenden Sie die Entwicklertools des Browsers, um herauszufinden, ob die jQuery-Datei gefunden wurde, und korrigieren Sie die URL, wenn dies nicht der Fall ist (z. B. fügen Sie das Schema http:oder https:am Anfang hinzu, passen Sie den Pfad an usw.).

Das Abhören der load/ DOMContentLoaded -Ereignisse ist genau das, was jQuery mit .ready() [docs] macht . Der gesamte jQuery-Code, der sich auf das DOM-Element auswirkt, sollte sich in diesem Ereignishandler befinden.

Tatsächlich heißt es im jQuery-Tutorial explizit:

Da fast alles, was wir bei der Verwendung von jQuery tun, das Dokumentobjektmodell (DOM) liest oder bearbeitet, müssen wir sicherstellen, dass wir Ereignisse usw. hinzufügen, sobald das DOM bereit ist.

Dazu registrieren wir ein fertiges Ereignis für das Dokument.

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

Alternativ können Sie auch die Kurzsyntax verwenden:

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

Beide sind gleichwertig.

Felix Kling
quelle
1
Wäre es sinnvoll, das letzte Bit des readyEreignisses zu ändern , um die "neuere" bevorzugte Syntax widerzuspiegeln, oder ist es besser, es unverändert zu lassen, damit es in älteren Versionen funktioniert?
Tieson T.
1
Für jQuery, lohnt es sich auch in der alternativen Zugabe $(window).load()(und möglicherweise syntaktischen Alternativen zu dem $(document).ready(), $(function(){})es im Zusammenhang scheint, aber es? Fühlt sich leicht tangential zu dem Punkt , Sie machen.
David sagt wieder einzusetzen Monica
@ David: Guter Punkt mit .load. Syntaxalternativen können in der Dokumentation nachgeschlagen werden. Da die Antworten jedoch in sich geschlossen sein sollten, lohnt es sich möglicherweise, sie hinzuzufügen. Andererseits bin ich mir ziemlich sicher, dass dieses spezielle Bit über jQuery bereits beantwortet wurde und wahrscheinlich in anderen Antworten behandelt wird und eine Verknüpfung damit möglicherweise ausreicht.
Felix Kling
1
@ David: Ich muss überarbeiten: Anscheinend .loadist veraltet: api.jquery.com/load-event . Ich weiß nicht, ob es nur für Bilder ist oder windowauch.
Felix Kling
1
@ David: Keine Sorge :) Auch wenn Sie nicht sicher sind, ist es gut, dass Sie es erwähnt haben. Ich werde wahrscheinlich nur einige einfache Codefragmente hinzufügen, um die Verwendung zu zeigen. Danke für deinen Beitrag!
Felix Kling
15

Gründe, warum ID-basierte Selektoren nicht funktionieren

  1. Das Element / DOM mit der angegebenen ID ist noch nicht vorhanden.
  2. Das Element ist vorhanden, wird jedoch nicht im DOM registriert [bei HTML-Knoten, die dynamisch an Ajax-Antworten angehängt werden].
  3. Es ist mehr als ein Element mit derselben ID vorhanden, das einen Konflikt verursacht.

Lösungen

  1. Versuchen Sie, nach der Deklaration auf das Element zuzugreifen, oder verwenden Sie alternativ Dinge wie $(document).ready();

  2. Verwenden Sie für Elemente, die aus Ajax-Antworten stammen, die .bind()Methode von jQuery. Ältere Versionen von jQuery hatten .live()das gleiche.

  3. Verwenden Sie Tools [z. B. das Webentwickler-Plugin für Browser], um doppelte IDs zu finden und diese zu entfernen.

sumit
quelle
13

Wie @FelixKling hervorhob, besteht das wahrscheinlichste Szenario darin, dass die gesuchten Knoten (noch) nicht vorhanden sind.

Moderne Entwicklungspraktiken können jedoch häufig Dokumentelemente außerhalb des Dokumentbaums entweder mit DocumentFragments oder einfach durch direktes Trennen / erneutes Anhängen aktueller Elemente bearbeiten. Solche Techniken können als Teil der JavaScript-Vorlage oder zur Vermeidung übermäßiger Repaint- / Reflow-Vorgänge verwendet werden, während die betreffenden Elemente stark geändert werden.

In ähnlicher Weise ermöglicht die neue "Shadow DOM" -Funktionalität, die in modernen Browsern eingeführt wird, dass Elemente Teil des Dokuments sind, jedoch nicht von document.getElementById und allen seinen Geschwistermethoden (querySelector usw.) abfragbar sind. Dies geschieht, um die Funktionalität zu kapseln und gezielt auszublenden.

Wiederum ist es jedoch höchstwahrscheinlich, dass das Element, nach dem Sie suchen, (noch) nicht im Dokument enthalten ist, und Sie sollten tun, was Felix vorschlägt. Sie sollten sich jedoch auch darüber im Klaren sein, dass dies zunehmend nicht der einzige Grund ist, warum ein Element (vorübergehend oder dauerhaft) möglicherweise nicht auffindbar ist.

Nathan Bubna
quelle
13

Wenn sich das Element, auf das Sie zugreifen möchten, innerhalb eines Elements befindet iframeund Sie versuchen, außerhalb des Kontexts des Elements darauf zuzugreifen, schlägt iframedies ebenfalls fehl.

Wenn Sie ein Element in einem Iframe zu erhalten , wie Sie herausfinden können , hier .

George Mulligan
quelle
7

Wenn die Skriptausführungsreihenfolge nicht das Problem ist, besteht eine weitere mögliche Ursache für das Problem darin, dass das Element nicht richtig ausgewählt wird:

  • getElementByIderfordert, dass die übergebene Zeichenfolge wörtlich die ID ist , und sonst nichts. Wenn Sie der übergebenen Zeichenfolge ein Präfix voranstellen #und die ID nicht mit a beginnt #, wird nichts ausgewählt:

    <div id="foo"></div>
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
  • In ähnlicher Weise dürfen Sie getElementsByClassNameder übergebenen Zeichenfolge nicht Folgendes voranstellen .:

    <div class="bar"></div>
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
  • Um mit querySelector, querySelectorAll und jQuery ein Element mit einem bestimmten Klassennamen abzugleichen, setzen Sie ein .direkt vor die Klasse. Um ein Element mit einer bestimmten ID abzugleichen, setzen Sie ein #direkt vor die ID:

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

    Die Regeln hier sind in den meisten Fällen identisch mit denen für CSS-Selektoren und können hier im Detail eingesehen werden .

  • Um ein Element mit zwei oder mehr Attributen (z. B. zwei Klassennamen oder einen Klassennamen und ein data-Attribut) abzugleichen, setzen Sie die Selektoren für jedes Attribut nebeneinander in die Auswahlzeichenfolge, ohne dass ein Leerzeichen sie trennt (da ein Leerzeichen angibt) der Nachkommenselektor ). Zum Beispiel, um Folgendes auszuwählen:

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

    Verwenden Sie die Abfragezeichenfolge .foo.bar. Auswählen

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

    Verwenden Sie die Abfragezeichenfolge .foo[data-bar="someData"]. So wählen Sie <span>Folgendes aus:

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

    verwenden div.parent > span[data-username="bob"].

  • Groß- und Kleinschreibung und Rechtschreibung sind für alle oben genannten Punkte von Bedeutung. Wenn die Groß- oder Kleinschreibung oder die Schreibweise unterschiedlich ist, wird das Element nicht ausgewählt:

    <div class="result"></div>
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
  • Sie müssen auch sicherstellen, dass die Methoden die richtige Groß- und Kleinschreibung und Rechtschreibung haben. Verwenden Sie eine der folgenden Optionen:

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

    Jede andere Schreibweise oder Großschreibung funktioniert nicht. Zum Beispiel document.getElementByClassNamewird ein Fehler ausgelöst.

  • Stellen Sie sicher, dass Sie diesen Auswahlmethoden eine Zeichenfolge übergeben. Wenn Sie etwas übergeben , die kein String ist querySelector, getElementByIdusw., wird es mit ziemlicher Sicherheit nicht.

Bestimmte Leistung
quelle
0

Als ich Ihren Code ausprobiert habe, hat es funktioniert.

Der einzige Grund, warum Ihr Ereignis nicht funktioniert, kann sein, dass Ihr DOM nicht bereit war und Ihre Schaltfläche mit der ID "event-btn" noch nicht bereit war. Und Ihr Javascript wurde ausgeführt und versucht, das Ereignis mit diesem Element zu verbinden.

Bevor Sie das DOM-Element zum Binden verwenden, sollte dieses Element bereit sein. Dafür gibt es viele Möglichkeiten.

Option 1: Sie können Ihren Ereignisbindungscode innerhalb des dokumentbereiten Ereignisses verschieben. Mögen:

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

Option 2: Sie können das Timeout-Ereignis verwenden, sodass die Bindung um einige Sekunden verzögert wird. mögen:

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

Option 3: Verschieben Sie Ihr Javascript-Include an den unteren Rand Ihrer Seite.

Ich hoffe das hilft dir.

Jatinder Kumar
quelle
@ Willdomybest18, bitte überprüfen Sie diese Antwort
Jatinder Kumar
-1

Das Problem ist, dass das dom-Element 'speclist' zum Zeitpunkt der Ausführung des Javascript-Codes nicht erstellt wird. Also habe ich den Javascript-Code in eine Funktion eingefügt und diese Funktion beim Body-Onload-Ereignis aufgerufen.

function do_this_first(){
   //appending code
}

<body onload="do_this_first()">
</body>
Mia Davis
quelle
-1

Meine Lösung bestand darin, die ID eines Ankerelements nicht zu verwenden : <a id='star_wars'>Place to jump to</a>. Anscheinend haben Blazor und andere Spa-Frameworks Probleme, auf derselben Seite zu Ankern zu springen. Um das zu umgehen, musste ich verwenden document.getElementById('star_wars'). Dies funktionierte jedoch nicht, bis ich stattdessen die ID in ein Absatzelement einfügte : <p id='star_wars'>Some paragraph<p>.

Beispiel mit Bootstrap:

<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>
goku_da_master
quelle