web-dev-qa-db-fra.com

Pourquoi jQuery ou une méthode DOM telle que getElementById ne trouve-t-elle pas l'élément?

Quelles sont les raisons possibles pour que document.getElementById, $("#id") ou tout autre sélecteur de méthode DOM/jQuery ne trouvant pas les éléments?

Exemples de problèmes:

  • jQuery n'a pas réussi à lier un gestionnaire d'événements
  • méthodes jQuery "getter" (.val(), .html(), .text()) renvoyant undefined
  • Une méthode DOM standard renvoyant null entraînant plusieurs erreurs:

Uncaught TypeError: impossible de définir la propriété '...' de null Uncaught TypeError: impossible de lire la propriété '...' de null

Les formes les plus courantes sont:

Uncaught TypeError: impossible de définir la propriété 'onclick' de null

Uncaught TypeError: Impossible de lire la propriété 'addEventListener' de null

Uncaught TypeError: impossible de lire la propriété 'style' de null

436
Felix Kling

L'élément que vous tentiez de trouver ne figurait pas dans le DOM lors de l'exécution de votre script.

La position de votre script dépendant de DOM peut avoir un effet profond sur son comportement. Les navigateurs analysent les documents HTML de haut en bas. Les éléments sont ajoutés au DOM et les scripts sont (généralement) exécutés à mesure qu'ils sont rencontrés. Cela signifie que l'ordre est important. En règle générale, les scripts ne peuvent pas trouver les éléments qui apparaissent plus tard dans le balisage, car ils n'ont pas encore été ajoutés au DOM.

Considérez le balisage suivant; le script n ° 1 ne parvient pas à trouver le <div> alors que le script n ° 2 réussit:

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

Alors, que devrais-tu faire? Vous avez quelques options:


Option 1: déplacez votre script

Déplacez votre script plus bas sur la page, juste avant la balise body de fermeture. Organisé de cette manière, le reste du document est analysé avant que votre script ne soit exécuté:

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

Remarque: Placer les scripts en bas est généralement considéré comme un meilleure pratique .


Option 2: ready() de jQuery

Reporter votre script jusqu'à ce que le DOM soit complètement analysé, en utilisant ready() :

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

Remarque: vous pouvez simplement relier DOMContentLoaded ou window.onload, mais chacun a ses inconvénients. jQuery's ready() fournit une solution hybride.


Option 3: Délégation d'événement

Les événements délégués ont l'avantage de pouvoir traiter les événements des éléments descendants ajoutés au document ultérieurement.

Lorsqu'un élément déclenche un événement (à condition qu'il s'agisse d'un événement bubbling et que rien ne l'arrête de se propager), chaque parent de l'ascendance de cet élément reçoit également l'événement. Cela nous permet d'attacher un gestionnaire à un élément existant et à échantillonner des événements à mesure qu'ils remontent de ses descendants ... même ceux ajoutés après l'attachement du gestionnaire. Tout ce que nous avons à faire est de vérifier l'événement pour voir s'il a été déclenché par l'élément souhaité et, le cas échéant, d'exécuter notre code.

jQuery's on() exécute cette logique pour nous. Nous fournissons simplement un nom d'événement, un sélecteur pour le descendant souhaité et un gestionnaire d'événements:

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

Remarque: En règle générale, ce modèle est réservé aux éléments qui n'existaient pas au moment du chargement ou afin d'éviter d'attacher un grand nombre de gestionnaires. Il convient également de souligner que, bien que j'ai attaché un gestionnaire à document (à des fins de démonstration), vous devez sélectionner l'ancêtre fiable le plus proche.


Option 4: l'attribut defer

Utilisez l'attribut defer de <script>.

[defer, un attribut booléen,] est défini pour indiquer à un navigateur que le script doit être exécuté après l'analyse du document.

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

Pour référence, voici le code de cette script externe :

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

Remarque: l'attribut defer semble bien comme une solution miracle mais il est important de prendre en compte les mises en garde ...
1. defer ne peut être utilisé que pour des scripts externes, c'est-à-dire ceux ayant un attribut src.
2. soyez conscient de support du navigateur , c'est-à-dire: implémentation boguée dans IE <10

441
canon

Court et simple: car les éléments que vous recherchez n'existent pas (encore) dans le document.


Pour le reste de cette réponse, je vais utiliser getElementById comme exemple, mais la même chose s'applique à getElementsByTagName , querySelector et à toute autre méthode DOM qui sélectionne des éléments .

Raisons possibles

Un élément peut ne pas exister pour deux raisons:

  1. Un élément avec l'ID passé n'existe pas vraiment dans le document. Vous devez vérifier que l'ID que vous transmettez à getElementById correspond vraiment à l'ID d'un élément existant dans le code HTML (généré) et que vous n'avez pas mal orthographié l'ID (les ID sont sensible à la casse !).

    Incidemment, dans les la majorité des navigateurs contemporains , qui implémentent les méthodes querySelector() et querySelectorAll(), la notation de style CSS est utilisée pour extraire un élément avec sa id, par exemple: document.querySelector('#elementID'), contrairement à la méthode par laquelle un élément est récupéré par son id sous document.getElementById('elementID'); dans le premier, le caractère # est essentiel, dans le second, l'élément ne sera pas récupéré.

  2. L'élément n'existe pas à l'heure actuelle , vous appelez getElementById.

Ce dernier cas est assez fréquent. Les navigateurs analysent et traitent le code HTML de haut en bas. Cela signifie que tout appel à un élément DOM qui se produit avant que cet élément DOM n'apparaisse dans le code HTML échoue.

Prenons l'exemple suivant:

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

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

div apparaît après le script. Au moment où le script est exécuté, l'élément n'existe pas encore et getElementById renverra null.

jQuery

La même chose s'applique à tous les sélecteurs avec jQuery. jQuery ne trouvera pas d'éléments si vous avez mal orthographié votre sélecteur ou si vous essayez de les sélectionner avant qu'ils n'existent réellement .

Une modification supplémentaire survient lorsque jQuery est introuvable, car vous avez chargé le script sans protocole et que vous exécutez à partir du système de fichiers:

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

cette syntaxe permet au script de se charger via HTTPS sur une page avec le protocole https: // et de charger la version HTTP sur une page avec le protocole http: //.

Il a malheureusement pour effet secondaire de tenter de ne pas charger file://somecdn.somewhere.com...


Solutions

Avant d’appeler getElementById (ou toute autre méthode DOM), assurez-vous que les éléments auxquels vous souhaitez accéder existent, c’est-à-dire que le DOM est chargé.

Ceci peut être assuré en mettant simplement votre JavaScript après l'élément DOM correspondant.

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

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

dans ce cas, vous pouvez également mettre le code juste avant la balise body de fermeture (</body>) (tous les éléments DOM seront disponibles au moment de l'exécution du script).

D'autres solutions incluent l'écoute de la load[MDN] ou DOMContentLoaded[MDN] événements. Dans ces cas, peu importe où vous placez le code JavaScript dans le document, vous devez simplement vous rappeler de mettre tout le code de traitement DOM dans les gestionnaires d'événements.

Exemple:

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

// or

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

Veuillez consulter les articles sur quirksmode.org pour plus d'informations sur la gestion des événements et les différences entre les navigateurs.

jQuery

Tout d’abord, assurez-vous que jQuery est chargé correctement. tilisez les outils de développement du navigateur pour savoir si le fichier jQuery a été trouvé et corrigez l'URL s'il ne l'était pas (par exemple, ajoutez le schéma http: ou https: au début, ajuster le chemin, etc.)

L'écoute des événements load/DOMContentLoaded est exactement ce que fait jQuery avec .ready()[docs] . Tout votre code jQuery qui affecte l'élément DOM doit être à l'intérieur de ce gestionnaire d'événements.

En fait, le tutoriel jQuery indique explicitement:

Comme presque tout ce que nous faisons lorsque jQuery lit ou manipule le modèle d'objet document (DOM), nous devons nous assurer que nous commençons à ajouter des événements, etc. dès que le DOM est prêt.

Pour ce faire, nous enregistrons un événement ready pour le document.

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

Sinon, vous pouvez également utiliser la syntaxe abrégée:

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

Les deux sont équivalents.

141
Felix Kling

Raisons pour lesquelles les sélecteurs basés sur id ne fonctionnent pas

  1. L'élément/DOM avec l'id spécifié n'existe pas encore.
  2. L'élément existe, mais il n'est pas enregistré dans DOM [dans le cas de nœuds HTML ajoutés dynamiquement à partir de réponses Ajax].
  3. Plusieurs éléments portant le même identifiant sont présents, ce qui provoque un conflit.

Solutions

  1. Essayez d’accéder à l’élément après sa déclaration ou bien utilisez des éléments tels que $(document).ready();

  2. Pour les éléments provenant de réponses Ajax, utilisez la méthode .bind() de jQuery. Les anciennes versions de jQuery avaient .live() pour les mêmes.

  3. Utilisez des outils [par exemple, plug-in webdeveloper pour les navigateurs] pour rechercher les ID en double et les supprimer.

13
sumit

Comme @FelixKling l'a souligné, le scénario le plus probable est que les nœuds que vous recherchez n'existent pas (encore).

Cependant, les pratiques de développement modernes peuvent souvent manipuler des éléments de document en dehors de l'arborescence de documents, soit avec DocumentFragments, soit simplement en détachant/rattachant directement les éléments actuels. De telles techniques peuvent être utilisées dans le cadre de modèles JavaScript ou pour éviter des opérations excessives de repeinte/redistribution alors que les éléments en question sont fortement altérés.

De même, la nouvelle fonctionnalité "Shadow DOM" déployée sur les navigateurs modernes permet aux éléments de faire partie du document, mais ne peut pas être interrogés par document.getElementById et toutes ses méthodes associées (querySelector, etc.). Ceci est fait pour encapsuler des fonctionnalités et les masquer spécifiquement.

Encore une fois, cependant, il est fort probable que l'élément que vous recherchez ne figure tout simplement pas (encore) dans le document, et vous devez procéder comme le suggère Felix. Cependant, vous devez également savoir que ce n'est de plus en plus la seule raison pour laquelle un élément peut être introuvable (temporairement ou définitivement).

11
Nathan Bubna

Si l'élément auquel vous essayez d'accéder se trouve à l'intérieur d'une iframe et que vous essayez d'y accéder en dehors du contexte de la iframe, cela entraînera également son échec.

Si vous voulez obtenir un élément dans un iframe, vous pouvez savoir comment ici .

10
George Mulligan

Si l'ordre d'exécution du script n'est pas le problème, une autre cause possible du problème est que l'élément n'est pas sélectionné correctement:

  • getElementById requiert que la chaîne transmise soit l'ID mot à mot , et rien d'autre. Si vous préfixez la chaîne passée avec un # et que l'ID ne commence pas par un #, rien ne sera sélectionné:

    <div id="foo"></div>
    
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
    
  • De même, pour getElementsByClassName, ne préfixez pas la chaîne passée avec un .:

    <div class="bar"></div>
    
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
    
  • Avec querySelector, querySelectorAll et jQuery, pour faire correspondre un élément avec un nom de classe particulier, placez un . directement devant la classe. De même, pour faire correspondre un élément avec un identifiant particulier, placez un # directement avant l'identifiant:

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

    Les règles ici sont, dans la plupart des cas, identiques à celles des sélecteurs CSS, et peuvent être vues en détail ici .

  • Pour faire correspondre un élément ayant deux attributs ou plus (comme deux noms de classe, ou un nom de classe et un attribut data-), placez les sélecteurs de chaque attribut les uns à côté des autres dans la chaîne de sélecteur, sans un espace les séparant (car un espace indique le sélecteur descendant . Par exemple, pour sélectionner:

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

    utilisez la chaîne de requête .foo.bar. Pour sélectionner

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

    utilisez la chaîne de requête .foo[data-bar="someData"].

  • La capitalisation et l'orthographe ont une importance pour tout ce qui précède. Si la capitalisation est différente ou si l'orthographe est différente, l'élément ne sera pas sélectionné:

    <div class="result"></div>
    
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
    
  • Vous devez également vous assurer que les méthodes ont la capitalisation et l'orthographe appropriées. Utilisez l'un des:

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

    Toute autre orthographe ou majuscule ne fonctionnera pas. Par exemple, document.getElementByClassName générera une erreur.

  • Assurez-vous de passer une chaîne à ces méthodes de sélecteur. Si vous passez quelque chose qui n'est pas une chaîne à querySelector, getElementById, etc., cela ne fonctionnera presque certainement pas.

4

le problème est que l'élément dom 'speclist' n'est pas créé au moment de l'exécution du code javascript. Donc, j'ai mis le code javascript à l'intérieur d'une fonction et appelé cette fonction sur l'événement body onload.

function do_this_first(){
   //appending code
}

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