Pourquoi document.body est-il nul dans mon javascript?

132

Voici mon bref document HTML.

Pourquoi la console Chrome note-t-elle cette erreur:

"Uncaught TypeError: Impossible d'appeler la méthode 'appendChild' de null"?

<html>
<head>
    <title>Javascript Tests</title>

    <script type="text/javascript">

        var mySpan = document.createElement("span");
        mySpan.innerHTML = "This is my span!";

        mySpan.style.color = "red";
        document.body.appendChild(mySpan);

        alert("Why does the span change after this alert? Not before?");

    </script>
</head>
<body>

</body>
</html>
John Hoffman
la source
J'utilise Google Chrome.
John Hoffman

Réponses:

180

Le corps n'a pas encore été défini à ce stade. En général, vous souhaitez créer tous les éléments avant d'exécuter du javascript qui utilise ces éléments. Dans ce cas, vous avez du javascript dans la headsection qui utilise body. Pas cool.

Vous voulez envelopper ce code dans un window.onloadgestionnaire ou le placer après la <body>balise (comme mentionné par e-bacho 2.0 ).

<head>
    <title>Javascript Tests</title>

    <script type="text/javascript">
      window.onload = function() {
        var mySpan = document.createElement("span");
        mySpan.innerHTML = "This is my span!";

        mySpan.style.color = "red";
        document.body.appendChild(mySpan);

        alert("Why does the span change after this alert? Not before?");
      }

    </script>
</head>

Voir la démo .

Sergio Tulentsev
la source
1
Merci, qu'est-ce que tu veux dire? Je n'ai pas d'étiquette corporelle?
John Hoffman
5
La page est lue de haut en bas et javascript est exécuté en cours de route. Vous avez du javascript dans la headsection en cours d'exécution. Mais une bodypartie du HTML n'a peut-être même pas encore été téléchargée. Ou il est téléchargé, mais il n'est toujours pas évalué à ce stade. Donc ça n'existe pas dans le DOM.
Sergio Tulentsev
3
Que faire si vous ne souhaitez pas remplacer une onload existante définie dans un autre fichier?
Tom Brito
1
@TomBrito: "ou placez-le après le <body>tag"
Sergio Tulentsev
1
Un peu tard, mais pourrait également utiliser addEventListener pour attacher l'écouteur à la fenêtre, sans remplacer ceux existants.
edvilme
36

Votre script est exécuté avant même que l' bodyélément ne soit chargé.

Il existe plusieurs façons de contourner ce problème.

  • Enveloppez votre code dans un rappel de chargement DOM:

    Enveloppez votre logique dans un écouteur d'événements pour DOMContentLoaded.

    Ce faisant, le rappel sera exécuté lorsque l' bodyélément sera chargé.

    document.addEventListener('DOMContentLoaded', function () {
        // ...
        // Place code here.
        // ...
    });

    En fonction de vos besoins, vous pouvez également attacher un loadécouteur d'événements à l' windowobjet:

    window.addEventListener('load', function () {
        // ...
        // Place code here.
        // ...
    });

    Pour connaître la différence entre les événements DOMContentLoadedet load, consultez cette question .

  • Déplacez la position de votre <script>élément et chargez JavaScript en dernier:

    En ce moment, votre <script>élément est en cours de chargement dans l' <head>élément de votre document. Cela signifie qu'il sera exécuté avant le bodychargement de. Les développeurs Google recommandent de déplacer les <script>balises à la fin de votre page afin que tout le contenu HTML soit rendu avant le traitement du JavaScript.

    <!DOCTYPE html>
    <html>
    <head></head>
    <body>
      <p>Some paragraph</p>
      <!-- End of HTML content in the body tag -->
    
      <script>
        <!-- Place your script tags here. -->
      </script>
    </body>
    </html>
Josh Crozier
la source
2
Je pense que c'est plus complet et actuel que la réponse acceptée.
theUtherSide
8

Ajoutez votre code à l'événement onload. La réponse acceptée le montre correctement, mais cette réponse ainsi que toutes les autres au moment de la rédaction suggèrent également de placer la balise script après la balise body de fermeture,.

Ce n'est pas du HTML valide. Cependant, cela fera fonctionner votre code, car les navigateurs sont trop gentils;)

Voir cette réponse pour plus d'informations. Est-il incorrect de placer la balise <script> après la balise </body>?

Pour cette raison, les autres réponses ont été rejetées.

Martin Hansen
la source
4

Ou ajoutez cette partie

<script type="text/javascript">

    var mySpan = document.createElement("span");
    mySpan.innerHTML = "This is my span!";

    mySpan.style.color = "red";
    document.body.appendChild(mySpan);

    alert("Why does the span change after this alert? Not before?");

</script>

après le HTML, comme:

    <html>
    <head>...</head>
    <body>...</body>
   <script type="text/javascript">
        var mySpan = document.createElement("span");
        mySpan.innerHTML = "This is my span!";

        mySpan.style.color = "red";
        document.body.appendChild(mySpan);

        alert("Why does the span change after this alert? Not before?");

    </script>

    </html>
Boris Bachovski
la source
1

Le navigateur analyse votre html de haut en bas, votre script s'exécute avant le chargement du corps. Pour corriger, mettez le script après le corps.

  <html>
  <head>
       <title> Javascript Tests </title> 
  </head>
 <body>
 </body>
  <script type="text/javascript">

    var mySpan = document.createElement("span");
    mySpan.innerHTML = "This is my span!";

    mySpan.style.color = "red";
    document.body.appendChild(mySpan);

    alert("Why does the span change after this alert? Not before?");

</script>
</html>
Emmanuel N
la source
0

document.body n'est pas encore disponible lorsque votre code s'exécute.

Ce que vous pouvez faire à la place:

var docBody=document.getElementsByTagName("body")[0];
docBody.appendChild(mySpan);
Christophe
la source
@Jake pourriez-vous être plus précis? Un exemple de navigateur / version où cela ne fonctionne pas?
Christophe
chrome @ 39 et firefox @ 33
Jake
@Jake ok, donc ma réponse était correcte au moment de la rédaction ;-) Merci de m'avoir alerté, je vais faire quelques tests et je posterai une mise à jour.
Christophe