538 votes

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

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

Voici quelques exemples de problèmes :

  • jQuery échoue silencieusement à lier un gestionnaire d'événement
  • Méthodes "getter" de jQuery ( .val() , .html() , .text() ) de retour undefined
  • Une méthode DOM standard retournant null ce qui entraîne plusieurs erreurs :

Erreur de type involontaire : Cannot set property '...' of null Uncaught TypeError : Cannot read property '...' of null

Les formes les plus courantes sont :

Erreur de Type Non Rattrapée : Impossible de définir la propriété 'onclick' de null.

Erreur de Type Non Rattrapée : Impossible de lire la propriété 'addEventListener' de null.

Erreur de Type Non Rattrapée : Impossible de lire la propriété 'style' de null

39 votes

De nombreuses questions sont posées pour savoir pourquoi un certain élément du DOM n'est pas trouvé et la raison est souvent que le code JavaScript est placé avant l'élément du DOM. Ceci est destiné à être une réponse canonique pour ce type de questions. C'est un wiki communautaire, donc n'hésitez pas à l'améliorer .

542voto

canon Points 14870

L'élément que vous essayez de trouver n'était pas dans la base de données de l'UE. DOM quand votre script s'est exécuté.

La position de votre script dépendant du 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 lorsqu'ils sont rencontrés. Cela signifie que l'ordre est important. En général, les scripts ne peuvent pas trouver les éléments qui apparaissent plus tard dans le balisage parce que ces éléments n'ont pas encore été ajoutés au DOM.

Considérez le balisage suivant ; script #1 échoue à trouver le <div> pendant que script #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 devez-vous faire ? Vous avez quelques options :


Option 1 : Déplacer votre script

Déplacez votre script plus bas dans la page, juste avant la balise de fermeture du corps. 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 -->

Note : Placer les scripts en bas de page est généralement considéré comme une <a href="http://developer.yahoo.com/performance/rules.html#js_bottom" rel="noreferrer">meilleure pratique </a>.


Option 2 : l'option de jQuery ready()

Différez votre script jusqu'à ce que le DOM ait été complètement analysé, en utilisant $(_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>

Note : Vous pourriez simplement lier à <a href="https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded" rel="noreferrer"><code>DOMContentLoaded</code></a> ou <code>window.<a href="https://developer.mozilla.org/en-US/docs/Web/API/window.onload?redirect=no" rel="noreferrer">onload</a></code> mais chacune d'entre elles a ses inconvénients. La fonction de jQuery <a href="http://api.jquery.com/ready/" rel="noreferrer"><code>ready()</code></a> offre une solution hybride.


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

Événements délégués ont l'avantage de pouvoir traiter les événements provenant d'éléments descendants qui sont ajoutés au document ultérieurement.

Lorsqu'un élément déclenche un événement (à condition qu'il s'agisse d'une bouillonnant et rien n'arrête sa propagation), chaque parent dans l'ascendance de cet élément reçoit également l'événement. Cela nous permet d'attacher un gestionnaire à un élément existant et d'échantillonner les événements à mesure qu'ils se propagent à partir 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, si c'est le cas, d'exécuter notre code.

La méthode de jQuery 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énement :

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

Note : En général, ce modèle est réservé aux éléments qui n'existaient pas au moment du chargement. <em>ou </em>pour éviter d'attacher un grand nombre de gestionnaires. Il est également intéressant de noter que, bien que j'aie attaché un gestionnaire à <code>document</code> (à titre démonstratif), vous devez sélectionner l'ancêtre fiable le plus proche.


Option 4 : le defer attribut

Utilisez le defer l'attribut de <script> .

[ defer un attribut booléen,] est défini pour indiquer au navigateur que le script est censé être exécuté après l'analyse du document, mais avant la mise à feu. DOMContentLoaded .

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

À titre de référence, voici le code de ce document script externe :

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

Note : Le <code>defer</code> attribut certainement <em>semble </em>comme une balle magique <em>mais </em>il est important d'être conscient des avertissements...<br>1. <code>defer</code> ne peut être utilisé que pour les scripts externes, c'est-à-dire ceux qui ont un nom de fichier <code>src</code> attribut.<br>2. être conscient de <a href="http://caniuse.com/script-defer" rel="noreferrer">Support du navigateur </a>c.-à-d. : mise en œuvre boguée dans IE < 10

2 votes

Nous sommes en 2020 - "Placer les scripts en bas de page" est-il toujours "considéré comme une bonne pratique" ? Je place (aujourd'hui) toutes mes ressources dans la section <head> et utiliser defer sur les scripts (je n'ai pas à supporter les mauvais vieux navigateurs incompatibles)

1 votes

Je suis un grand partisan du passage aux navigateurs modernes. Cela dit, cette pratique ne me coûte pas vraiment et elle fonctionne partout. D'autre part, les deux defer et async bénéficient d'un soutien assez large, allant même jusqu'à des versions de navigateurs beaucoup plus anciennes. Ils présentent l'avantage supplémentaire de signaler clairement et explicitement le comportement souhaité. N'oubliez pas que ces attributs ne fonctionnent que pour les scripts spécifiant un attribut src c'est-à-dire des scripts externes. Pesez les avantages. Peut-être utiliser les deux ? À vous de voir.

1 votes

A mon avis, cette réponse devrait également inclure une référence à developer.mozilla.org/fr/US/docs/Web/API/Window/ dans l'approche n°2. Il s'agit essentiellement de la solution JS classique à ce problème, ce que certains Googlers aimeraient probablement savoir.

159voto

Felix Kling Points 247451

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


Pour le reste de cette réponse, j'utiliserai les termes suivants getElementById à titre d'exemple, mais il en va de même pour getElementsByTagName , querySelector et toute autre méthode DOM qui sélectionne des éléments.

Raisons possibles

Il y a deux raisons pour lesquelles un élément peut ne pas exister :

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

    D'ailleurs, dans le la majorité des navigateurs contemporains qui mettent en œuvre querySelector() et querySelectorAll() la notation de style CSS est utilisée pour retrouver un élément par son id par exemple : document.querySelector('#elementID') par opposition à la méthode par laquelle un élément est récupéré par son nom. id sous document.getElementById('elementID') ; dans la première, le # est essentiel, dans le second, il conduirait à ce que l'élément ne soit pas récupéré.

  2. L'élément n'existe pas au moment présent vous appelez getElementById .

Ce dernier cas est assez fréquent. Les navigateurs analysent et traitent le 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 HTML, échouera.

Prenons l'exemple suivant :

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

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

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

jQuery

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

Un autre cas de figure est celui où jQuery n'est pas trouvé parce que vous avez chargé le script sans protocole et que vous vous exécutez à partir du système de fichiers :

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

cette syntaxe est utilisée pour permettre 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 l'effet secondaire malheureux d'essayer et d'échouer à charger file://somecdn.somewhere.com...


Solutions

Avant de passer un appel à getElementById (ou toute autre méthode DOM d'ailleurs), assurez-vous que les éléments auxquels vous voulez accéder existent, c'est-à-dire que le DOM est chargé.

Cela 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 placer le code juste avant la balise de fermeture du corps ( </body> ) (tous les éléments du DOM seront disponibles au moment de l'exécution du script).

D'autres solutions consistent à écouter le load [MDN] ou DOMContentLoaded [MDN] événements. Dans ces cas, l'endroit du document où vous placez le code JavaScript n'a pas d'importance, vous devez simplement vous rappeler de placer tout le code de traitement du 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 le articles à quirksmode.org pour plus d'informations sur le traitement des événements et les différences entre les navigateurs.

jQuery

Assurez-vous d'abord que jQuery est chargé correctement. Utilisez les outils de développement du navigateur pour savoir si le fichier jQuery a été trouvé et corriger l'URL si ce n'est pas le cas (par exemple, ajoutez l'élément http: ou https: schéma au début, ajuster le chemin, etc.)

À l'écoute de la load / DOMContentLoaded est exactement ce que jQuery fait avec .ready() [docs] . Tout votre code jQuery qui affecte l'élément DOM doit se trouver dans ce gestionnaire d'événement.

En fait, le Tutoriel jQuery déclare explicitement :

Comme presque tout ce que nous faisons en utilisant jQuery lit ou manipule le modèle d'objet du 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
});

Vous pouvez également utiliser la syntaxe abrégée :

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

Les deux sont équivalents.

1 votes

Cela vaudrait-il la peine d'amender la dernière partie concernant les ready pour refléter la "nouvelle" syntaxe préférée, ou est-il préférable de le laisser tel quel pour qu'il fonctionne dans les anciennes versions ?

1 votes

Pour jQuery, cela vaut-il la peine d'ajouter également l'alternative suivante $(window).load() (et éventuellement des alternatives syntaxiques à l'option $(document).ready() , $(function(){}) ? Cela semble lié, mais c'est se sent légèrement tangentiel au point que vous faites.

0 votes

@David : Bon point avec .load . En ce qui concerne les alternatives syntaxiques, elles peuvent être recherchées dans la documentation, mais comme les réponses doivent être autonomes, cela pourrait valoir la peine de les ajouter. Par ailleurs, je suis presque sûr que cette partie spécifique sur jQuery a déjà été traitée et est probablement couverte dans une autre réponse et un lien vers celle-ci pourrait suffire.

16voto

George Mulligan Points 5935

Si l'élément auquel vous essayez d'accéder se trouve à l'intérieur d'un fichier de type iframe et que vous essayez d'y accéder en dehors du contexte de l'application iframe cela entraînera également sa défaillance.

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

16voto

sumit Points 520

Raisons pour lesquelles les sélecteurs basés sur l'identification 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 le DOM [dans le cas de nœuds HTML ajoutés dynamiquement à partir de réponses Ajax].
  3. Plus d'un élément avec le même identifiant est présent, ce qui cause un conflit.

Solutions

  1. Essayez d'accéder à l'élément après sa déclaration ou utilisez des trucs comme $(document).ready();

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

  3. Utilisez des outils [par exemple, le plugin webdeveloper pour les navigateurs] pour trouver les identifiants en double et les supprimer.

14voto

Nathan Bubna Points 3779

Comme l'a souligné @FelixKling, 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 permettent souvent de manipuler les éléments du document en dehors de l'arbre du document, soit avec des DocumentFragments, soit en détachant/réattachant simplement les éléments actuels directement. Ces techniques peuvent être utilisées dans le cadre de la création de modèles JavaScript ou pour éviter des opérations de rafraîchissement/refoulement excessives lorsque les éléments en question sont fortement modifiés.

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

Mais là encore, il est fort probable que l'élément que vous recherchez ne se trouve tout simplement pas (encore) dans le document, et vous devriez faire ce que Felix suggère. Cependant, vous devez également savoir que ce n'est de plus en plus souvent la seule raison pour laquelle un élément peut être introuvable (de façon temporaire ou permanente).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X