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:
.val()
, .html()
, .text()
) renvoyant undefined
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
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:
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 .
ready()
de jQueryReporter 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.
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.
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
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:
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é.
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.
Raisons pour lesquelles les sélecteurs basés sur id ne fonctionnent pas
Solutions
Essayez d’accéder à l’élément après sa déclaration ou bien utilisez des éléments tels que $(document).ready();
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.
Utilisez des outils [par exemple, plug-in webdeveloper pour les navigateurs] pour rechercher les ID en double et les supprimer.
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).
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 .
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.
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>