Perché jQuery o un metodo DOM come getElementById non trovano l'elemento?


483

Quali sono le possibili ragioni per document.getElementById,$("#id") o qualsiasi altro metodo DOM / selettore jQuery non trovare gli elementi?

Esempi di problemi includono:

  • jQuery non riesce a associare silenziosamente un gestore eventi
  • jQuery metodi "getter" ( .val(), .html(), .text()) ritornoundefined
  • Un metodo DOM standard che restituisce il nullrisultato in uno dei numerosi errori:

Uncaught TypeError: Impossibile impostare la proprietà '...' di null Uncaught TypeError: Impossibile leggere la proprietà '...' di null

Le forme più comuni sono:

TypeError non rilevato: impossibile impostare la proprietà 'onclick' su null

TypeError non rilevato: impossibile leggere la proprietà 'addEventListener' di null

TypeError non rilevato: impossibile leggere la proprietà 'style' di null


32
Vengono poste molte domande sul perché non viene trovato un determinato elemento DOM e il motivo è spesso perché il codice JavaScript viene inserito prima dell'elemento DOM. Questa vuole essere una risposta canonica per questo tipo di domande. È wiki della community, quindi sentiti libero di migliorarlo .
Felix Kling,

Risposte:


481

L'elemento che stavi cercando di trovare non era nel DOM durante l'esecuzione dello script.

La posizione del tuo script dipendente dal DOM può avere un profondo effetto sul suo comportamento. I browser analizzano i documenti HTML dall'alto verso il basso. Gli elementi vengono aggiunti al DOM e gli script vengono (generalmente) eseguiti man mano che vengono rilevati. Ciò significa che l'ordine conta. In genere, gli script non riescono a trovare elementi che compaiono più tardi nel markup perché quegli elementi devono ancora essere aggiunti al DOM.

Considera il seguente markup; lo script n. 1 non riesce a trovare <div>mentre lo script n. 2 riesce:

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

Quindi cosa dovresti fare? Hai alcune opzioni:


Opzione 1: sposta lo script

Sposta il tuo script più in basso nella pagina, appena prima del tag body di chiusura. Organizzato in questo modo, il resto del documento viene analizzato prima dell'esecuzione dello script:

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

Nota: Mettere gli script in fondo è generalmente considerato una best practice .


Opzione 2: jQuery ready()

Rinvia lo script fino a quando il DOM non è stato completamente analizzato, utilizzando :$(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>

Nota: si potrebbe semplicemente associare DOMContentLoadedo ma ognuno ha i suoi avvertimenti. jQuery offre una soluzione ibrida.window.onloadready()


Opzione 3: delega degli eventi

Gli eventi delegati hanno il vantaggio di poter elaborare eventi da elementi discendenti che vengono aggiunti al documento in un secondo momento.

Quando un elemento genera un evento (a condizione che sia un gorgoglio evento e che nulla interrompa la sua propagazione), anche ogni genitore nella discendenza di quell'elemento riceve l'evento. Questo ci consente di collegare un gestore a un elemento esistente e di campionare eventi mentre saltano fuori dai suoi discendenti ... anche quelli aggiunti dopo che il gestore è stato collegato. Tutto quello che dobbiamo fare è controllare l'evento per vedere se è stato generato dall'elemento desiderato e, in tal caso, eseguire il nostro codice.

jQuery on()esegue questa logica per noi. Forniamo semplicemente un nome di evento, un selettore per il discendente desiderato e un gestore di eventi:

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

Nota: in genere, questo modello è riservato agli elementi che non esistevano in fase di caricamento o per evitare di collegare una grande quantità di gestori. Vale anche la pena sottolineare che mentre ho collegato un gestore a document(a scopo dimostrativo), è necessario selezionare l'antenato affidabile più vicino.


Opzione 4: l' deferattributo

Usa l' deferattributo di <script>.

[ defer, un attributo booleano,] è impostato per indicare a un browser che lo script deve essere eseguito dopo che il documento è stato analizzato, ma prima dell'attivazione DOMContentLoaded.

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

Per riferimento, ecco il codice di quello script esterno :

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

Nota: l' deferattributo sembra certamente un proiettile magico, ma è importante essere consapevoli delle avvertenze ...
1. deferpuò essere utilizzato solo per script esterni, ovvero: quelli che hanno un srcattributo.
2. essere a conoscenza del supporto del browser , ovvero: implementazione buggy in IE <10


È ora il 2020: "Inserire gli script in fondo" è ancora "considerato una buona pratica"? Io (oggi) metto tutte le mie risorse in <head>e uso defersu script (non devo supportare browser incompatibili di vecchia generazione)
Stephen P

Sono un grande sostenitore del passaggio ai browser moderni. Detto questo, questa pratica non mi costa davvero nulla e funziona solo ovunque. D'altra parte, entrambi defere asynchanno un supporto abbastanza ampio, anche risalendo ad alcune versioni del browser molto più vecchie. Hanno il vantaggio aggiuntivo di segnalare chiaramente ed esplicitamente il comportamento previsto. Ricorda solo che questi attributi funzionano solo per gli script che specificano un srcattributo, ovvero: script esterni. Pesare i benefici. Forse usi entrambi? La tua chiamata.
canon

146

Breve e semplice: perché gli elementi che stai cercando non esistono (ancora) nel documento.


Per il resto di questa risposta userò getElementByIdcome esempio, ma lo stesso vale per getElementsByTagName, querySelectore qualsiasi altro metodo DOM che gli elementi Seleziona.

Possibili ragioni

Esistono due motivi per cui un elemento potrebbe non esistere:

  1. Un elemento con l'ID passato in realtà non esiste nel documento. Dovresti ricontrollare che l'ID che passi per getElementByIdcorrispondere realmente a un ID di un elemento esistente nell'HTML (generato) e che non hai sbagliato a scrivere l'ID (gli ID fanno distinzione tra maiuscole e minuscole !).

    Per inciso, nella maggior parte dei browser contemporanei , che implementano querySelector()e querySelectorAll()metodi, la notazione in stile CSS viene utilizzata per recuperare un elemento dal suo id, ad esempio: document.querySelector('#elementID')al contrario del metodo con cui un elemento viene recuperato dal suo idsotto document.getElementById('elementID'); nel primo il #personaggio è essenziale, nel secondo porterebbe a non recuperare l'elemento.

  2. L'elemento non esiste al momento della chiamata getElementById.

Quest'ultimo caso è abbastanza comune. I browser analizzano ed elaborano l'HTML dall'alto verso il basso. Ciò significa che qualsiasi chiamata a un elemento DOM che si verifica prima che l'elemento DOM venga visualizzato nell'HTML avrà esito negativo.

Considera il seguente esempio:

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

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

Il divcompare dopo il script. Nel momento in cui lo script viene eseguito, l'elemento non esiste ancora e getElementByIdtornerà null.

jQuery

Lo stesso vale per tutti i selettori con jQuery. jQuery non troverà elementi se hai sbagliato l' ortografia del selettore o stai provando a selezionarli prima che esistano effettivamente .

Un'ulteriore svolta è quando jQuery non viene trovato perché hai caricato lo script senza protocollo e sei in esecuzione dal file system:

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

questa sintassi viene utilizzata per consentire allo script di caricare tramite HTTPS su una pagina con protocollo https: // e di caricare la versione HTTP su una pagina con protocollo http: //

Ha lo sfortunato effetto collaterale di tentare e non riuscire a caricare file://somecdn.somewhere.com...


soluzioni

Prima di effettuare una chiamata a getElementById (o qualsiasi metodo DOM per quella materia), assicurarsi che gli elementi a cui si desidera accedere esistano, ovvero che il DOM sia caricato.

Questo può essere garantito semplicemente inserendo il tuo JavaScript dopo l'elemento DOM corrispondente

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

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

nel qual caso puoi anche inserire il codice appena prima del tag body di chiusura ( </body>) (tutti gli elementi DOM saranno disponibili al momento dell'esecuzione dello script).

Altre soluzioni includono l'ascolto degli eventi load [MDN] o DOMContentLoaded [MDN] . In questi casi, non importa dove nel documento inserisci il codice JavaScript, devi solo ricordare di inserire tutto il codice di elaborazione DOM nei gestori di eventi.

Esempio:

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

// or

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

Per ulteriori informazioni sulla gestione degli eventi e sulle differenze del browser, consultare gli articoli su quirksmode.org .

jQuery

Assicurati innanzitutto che jQuery sia caricato correttamente. Utilizza gli strumenti di sviluppo del browser per scoprire se il file jQuery è stato trovato e correggi l'URL se non lo è (ad esempio aggiungi lo schema http:o https:all'inizio, regola il percorso, ecc.)

L'ascolto di load/ DOMContentLoaded events è esattamente quello che jQuery sta facendo con .ready() [docs] . Tutto il codice jQuery che influisce sull'elemento DOM dovrebbe essere all'interno del gestore eventi.

In effetti, il tutorial di jQuery afferma esplicitamente:

Poiché quasi tutto ciò che facciamo quando si utilizza jQuery legge o manipola il modello a oggetti del documento (DOM), dobbiamo assicurarci di iniziare ad aggiungere eventi ecc. Non appena il DOM è pronto.

Per fare ciò, registriamo un evento pronto per il documento.

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

In alternativa puoi anche usare la sintassi abbreviata:

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

Entrambi sono equivalenti.


1
Varrebbe la pena modificare l'ultimo bit readydell'evento per riflettere la sintassi preferita "più recente", oppure è meglio lasciarlo così com'è in modo che funzioni su versioni precedenti?
Tieson T. il

1
Per jQuery, vale la pena anche di aggiungere in subordine $(window).load()(e possibilmente alternative sintattiche al $(document).ready(), $(function(){})Sembra correlato, ma? Sente un po 'tangente al punto che stai facendo.
David dice reinstate Monica

@ David: buon punto con .load. Per quanto riguarda le alternative di sintassi, potrebbero essere ricercate nella documentazione, ma poiché le risposte dovrebbero essere autosufficienti, potrebbe valere la pena aggiungerle. Poi di nuovo, sono abbastanza sicuro che questo specifico bit su jQuery abbia già ricevuto una risposta ed è probabilmente coperto da un'altra risposta e il collegamento ad esso potrebbe essere sufficiente.
Felix Kling,

1
@ David: devo rivedere: apparentemente .loadè deprecato: api.jquery.com/load-event . Non so se sia solo per immagini o windowpure.
Felix Kling,

1
@ David: non preoccuparti :) Anche se non sei sicuro, è bene che tu l'abbia menzionato. Probabilmente aggiungerò solo alcuni semplici frammenti di codice per mostrare l'utilizzo. Grazie per il tuo contributo!
Felix Kling,

15

Ragioni per cui i selettori basati su ID non funzionano

  1. L'elemento / DOM con ID specificato non esiste ancora.
  2. L'elemento esiste, ma non è registrato in DOM [nel caso di nodi HTML aggiunti dinamicamente dalle risposte Ajax].
  3. È presente più di un elemento con lo stesso ID che causa un conflitto.

soluzioni

  1. Prova ad accedere all'elemento dopo la sua dichiarazione o in alternativa usa cose come $(document).ready();

  2. Per gli elementi provenienti dalle risposte Ajax, utilizzare il .bind()metodo di jQuery. Le versioni precedenti di jQuery avevano .live()lo stesso.

  3. Utilizzare gli strumenti [ad esempio il plugin webdeveloper per browser] per trovare ID duplicati e rimuoverli.


13

Come sottolineato da @FelixKling, lo scenario più probabile è che i nodi che stai cercando non esistano (ancora).

Tuttavia, le moderne pratiche di sviluppo possono spesso manipolare gli elementi del documento all'esterno dell'albero del documento con DocumentFragments o semplicemente staccare / ricollegare direttamente gli elementi correnti. Tali tecniche possono essere utilizzate come parte del modello JavaScript o per evitare operazioni di ridipintura / rifusione eccessive mentre gli elementi in questione vengono fortemente modificati.

Allo stesso modo, la nuova funzionalità "Shadow DOM" implementata nei browser moderni consente agli elementi di far parte del documento, ma non è possibile eseguire query tramite document.getElementById e tutti i suoi metodi di pari livello (querySelector, ecc.). Questo viene fatto per incapsulare la funzionalità e specificatamente nasconderla.

Ancora una volta, tuttavia, è molto probabile che l'elemento che stai cercando semplicemente non sia (ancora) nel documento, e dovresti fare come suggerisce Felix. Tuttavia, dovresti anche essere consapevole che questa non è sempre più l'unica ragione per cui un elemento potrebbe non essere individuabile (temporaneamente o permanentemente).


13

Se l'elemento a cui si sta tentando di accedere si trova all'interno di un iframee si tenta di accedervi al di fuori del contesto iframe, anche questo avrà esito negativo.

Se vuoi ottenere un elemento in un iframe puoi scoprire come qui .


7

Se l'ordine di esecuzione dello script non è il problema, un'altra possibile causa del problema è che l'elemento non viene selezionato correttamente:

  • getElementByIdrichiede che la stringa passata sia l'ID testualmente e nient'altro. Se si antepone la stringa passata con a #e l'ID non inizia con a #, non verrà selezionato nulla:

    <div id="foo"></div>
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
  • Allo stesso modo, per getElementsByClassName, non aggiungere il prefisso alla stringa passata con un .:

    <div class="bar"></div>
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
  • Con querySelector, querySelectorAll e jQuery, per abbinare un elemento con un nome di classe particolare, inserire un .direttamente prima della classe. Allo stesso modo, per abbinare un elemento a un determinato ID, inserire un #direttamente prima dell'ID:

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

    Le regole qui sono, nella maggior parte dei casi, identiche a quelle per i selettori CSS e possono essere viste in dettaglio qui .

  • Per abbinare un elemento che ha due o più attributi (come due nomi di classe, o un nome di classe e un data-attributo), metti i selettori di ciascun attributo uno accanto all'altro nella stringa del selettore, senza uno spazio che li separa (perché uno spazio indica il selettore discendente ). Ad esempio, per selezionare:

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

    usa la stringa di query .foo.bar. Selezionare

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

    usa la stringa di query .foo[data-bar="someData"] . Per selezionare quanto <span>segue:

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

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

  • La capitalizzazione e l'ortografia sono importanti per tutto quanto sopra. Se la capitalizzazione è diversa o l'ortografia è diversa, l'elemento non verrà selezionato:

    <div class="result"></div>
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
  • È inoltre necessario assicurarsi che i metodi abbiano le maiuscole e l'ortografia corrette. Usa uno di:

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

    Qualsiasi altra ortografia o maiuscola non funzionerà. Per esempio,document.getElementByClassName genererà un errore.

  • Assicurati di passare una stringa a questi metodi di selezione. Se si passa qualcosa che non è una stringa querySelector, getElementByIdecc, ma quasi certamente non funzionerà.


0

Quando ho provato il tuo codice, ha funzionato.

L'unica ragione per cui il tuo evento non funziona, potrebbe essere che il tuo DOM non era pronto e il tuo pulsante con ID "event-btn" non era ancora pronto. E il tuo javascript è stato eseguito e ha cercato di associare l'evento a quell'elemento.

Prima di utilizzare l'elemento DOM per l'associazione, quell'elemento dovrebbe essere pronto. Ci sono molte opzioni per farlo.

Opzione 1: è possibile spostare il codice di associazione dell'evento all'interno dell'evento pronto per il documento. Piace:

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

Opzione2: è possibile utilizzare un evento di timeout, in modo che l'associazione venga ritardata di alcuni secondi. piace:

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

Opzione 3: sposta il tuo javascript incluso nella parte inferiore della pagina.

Spero che questo ti aiuta.


@ Willdomybest18, controlla questa risposta
Jatinder Kumar il

-1

il problema è che l'elemento dom 'speclist' non viene creato nel momento in cui il codice javascript viene eseguito. Quindi ho inserito il codice JavaScript all'interno di una funzione e l'ho chiamata sull'evento di caricamento del corpo.

function do_this_first(){
   //appending code
}

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

-1

La mia soluzione era di non usare l'id di un elemento di ancoraggio: <a id='star_wars'>Place to jump to</a>. Apparentemente il blazor e altre strutture termali hanno problemi a saltare alle ancore sulla stessa pagina. Per aggirare quello che ho dovuto usare document.getElementById('star_wars'). Tuttavia, questo non ha funzionato fino a quando non ho inserito l'id in un elemento di paragrafo:<p id='star_wars'>Some paragraph<p> .

Esempio usando 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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.