11548 sujets

JavaScript, DOM et API Web HTML5

Bonjour,
Je bloque avec la fonction "document.getElementById ".

<!DOCTYPE html>
<html>
<head>
    <title>Calcul</title>
    <meta charset="utf-8">
</head>
<body>
     Valeur de ma variable: <span id="variable1">Pas de communication</span>
    <script>
       let var1 =10
       document.getElementById("variable1").innerHTML = var1;
    </script>
   </body>
</html>

Cela affiche correctement ma valeur var1 . Par contre si je fait appel a un script extérieur à mon fichier html , j'ai l'erreur: log1.js:2 Uncaught TypeError: Cannot set properties of null (setting 'innerHTML') at log1.js:2:48

mon fichier html:

<!DOCTYPE html>
<html>
<head>
    <title>Calcul</title>
    <meta charset="utf-8">
     <script type="text/javascript" src="log1.js"></script>
</head>
<body>
    Valeur de ma variable: <span id="variable1">Pas de communication</span>
</body>
</html>

mon fichier log1.js

let var1 =10
document.getElementById("variable1").innerHTML = var1;
Bonjour, c'est parce que veux modifier le DOM alors qu'il n'est pas encore construit, le code JS s'exécute avant que le HTML soit écrit.
Plusieurs solution: L'ancienne solution (qui fonctionne toujours) consiste à placer les scripts juste avant la balise </body>.
Solution plus actuel : Dans le <head> ajouter l'attribut
defer
à la balise <script>. Exemple :
<script src="log1.js" defer></script>

Note que l'attribut type n'est plus obligatoire pour JS.
Dernière solution, s'assurer que le dom est charger
https://developer.mozilla.org/fr/docs/Web/API/Document/DOMContentLoaded_event
Pour apprendre le JS => https://fr.javascript.info/
Modérateur
Bonjour,

L'attribut defer est plutôt fait pour décaler l'exécution des gros scripts à la fin du chargement de la page et non pas pour attendre que les éléments HTML de la page soit chargés.

Ça peut marcher, mais l'inconvenient avec defer est que si on a plusieurs scripts, on ne sait pas trop dans quel ordre ces scripts vont être exécutés. Personnellement, je n'aime pas du tout, d'autant plus qu'on a par ailleurs en javascript des tas d'autres occasions de voir du code exécuté avec un délai (et ce n'est pas facile à gérer).

La dernière solution proposée par casper, celle avec le DOMContentLoaded_event (https://developer.mozilla.org/fr/docs/Web/API/Document/DOMContentLoaded_event), ou bien une solution en surveillant d'autres évènements comme le load_event (https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event) sont à privilégier, où qu'on mette le script dans le html.

Personnellement, je préfère agir dans le cas général après le load event quand je manipule le DOM (les éléments HTML de la page), parce que si on agit après le DOMContentLoaded_event (qui arrive avant le load event), selon ce que fait le script js, ça peut ne pas marcher comme attendu vu que toutes les ressources ne sont pas chargées à ce moment-là, et c'est parfois une grosse prise de tête de savoir pourquoi.

Mais bon, y a beaucoup de fans du DOMContentLoaded_event aussi ! Tant qu'ils savent ce qu'ils font, pourquoi pas ! Smiley cligne

Amicalement,
Modifié par parsimonhi (03 May 2025 - 11:48)
Merci casper2.D'accord, je pensais que c'était le code html qui "appelait" le code javascript .

Cela explique certainement un autre problème , j'utilise un script commun pour plusieurs pages html n'affichant pas les mêmes variables . Sur la console j'ai des erreurs sur les variables qui ne sont pas affichées sur la page html mais "rafraichies" dans le script .
Pour être le plus souple possible, Il faudrait que mon script détecte si ma variable est utilisée dans ma page html ?
Modifié par herve_37 (03 May 2025 - 11:49)
Modérateur
Bonjour,
herve_37 a écrit :
Merci casper2.D'accord, je pensais que c'était le code html qui "appelait" le code javascript.
C'est le cas en quelque sorte. Mais le script est exécuté immédiatement sauf mention contraire. Donc s'il cherche à modifier quelque chose qui est situé après dans le code HTML, ça ne va pas marcher "comme ça".

Amicalement,
Modérateur
Bonjour,
herve_37 a écrit :
Pour être le plus souple possible, Il faudrait que mon script détecte si ma variable est utilisée dans ma page html ?
On ne sait pas ce que fait ton script, mais a priori, on ne s'amuse pas à "détecter" via un script si une "variable" est utilisée.

Par contre, on vérifie assez souvent si un élément html est présent ou pas.

Amicalement,
Il faut donc que mon script ne s'exécute qu'après l'exécution de ma page web et tester si la variable est utilisée :

window.onload = (event) => {
    console.log("La page est complètement chargée");
  
    let var1 =10
    let var2 =20

    if (document.getElementById("variable1"))
        {
        document.getElementById("variable1").innerHTML = var1;
        }
    if (document.getElementById("variable2"))
        {
        document.getElementById("variable2").innerHTML = var2;
        }
};
Modérateur
Bonjour,

Ce que tu appelles "variable" est en fait l'id d'un élément html, donc pas vraiment une variable.

Ceci étant, oui, tu peux tester l'existence des éléments html comme le fait ton code ci-dessus.

Tu peux légèrement raccourcir ton code aussi. Par exemple, tu peux remplacer :
if (document.getElementById("variable1"))
{
        document.getElementById("variable1").innerHTML = var1;
 }
par
let e1 =  document.getElementById("variable1")
if (e1) e1.innerHTML = var1
Tu évites ainsi de demander au navigateur d'exécuter deux fois le document.getElementById("variable1")

Amicalement,
Modérateur
Bonjour,

Pour ajouter aux autres infos.
Il y a aussi une différence importante entre ta version avec le script en ligne et celle avec le script externe. Cette différence est l'endroit où le script est appelé.

     Valeur de ma variable: <span id="variable1">Pas de communication</span>
    <script>
       let var1 =10
       document.getElementById("variable1").innerHTML = var1;
    </script>

dans ce cas, le document se charge et le span existe (chargé/lu) avant ton script. Pas de soucis, le script trouve ton id et modifie son contenu.
Si tu fait :

    <script>
       let var1 =10
       document.getElementById("variable1").innerHTML = var1;
    </script>
     Valeur de ma variable: <span id="variable1">Pas de communication</span>

tu as le même soucis qu'en faisant :
<!DOCTYPE html>
<html>
<head>
    <title>Calcul</title>
    <meta charset="utf-8">
     <script src="log1.js"></script>
</head>
<body>
    Valeur de ma variable: <span id="variable1">Pas de communication</span>
</body>
</html>

le script est chargé ou lancé avant que ton span soit lu et chargé.

En ajoutant un évènement onload sur le document tu peut attendre le chargement(lecture) du document avant de lancé ta fonction.

document.addEventListener("load", 
 function() {
    let var1 =10
    document.getElementById("variable1").innerHTML = var1;
  }
);


tu peut aussi chargé ton script en fin de page lorsque ton span est déjà chargé, juste avant la fermeture de body par exemple
<!DOCTYPE html>
<html>
<head>
    <title>Calcul</title>
    <meta charset="utf-8">
</head>
<body>
    Valeur de ma variable: <span id="variable1">Pas de communication</span>

     <script src="log1.js"></script>
</body>
</html>


Cdt
Administrateur
Bonjour,

concernant l'attribut defer, un article de dew sur le sujet : Les attributs async et defer pour <script>.

Pour remplacer le test if (élément existe) élément.propriété= faitQqch, il y a cette écriture un peu avancée avec un point d'interrogation (que l'on apprend viiite en se lançant dans TypeScript Smiley ravi ) :

document.getElementById("monId")?.addClass("is-active")
/*                           --^                                    */


- Chaînage optionnel (optional chaining) (MDN FR)
- Chaînage optionnel '?.' (javascript.info FR)
Modifié par Felipe (04 May 2025 - 11:29)
Pour l’écriture avec le chainage optionnel on ne peut pas affecter une valeur:
document.getElementById("valeurLatitude").innerHTML ?= this.responseText;

Dans le cas présent je ne vois pas comment l'utiliser
Salut,

il faut mettre le ? avant d’essayer d'accéder à .innerHTML, pour vérifier que le getElementById fonctionne bien. Cela donne plutôt ça :


document.getElementById("valeurLatitude")?.innerHTML = this.responseText;
Il est interdit d'assigner une valeur avec l'opérateur null coalescing, comme le dit déjà Herve_37. Je viens de le découvrir à l'instant en vous lisant, mais en y réfléchissant un peu, c'est tout à fait normal que ce soit interdit. On peut éventuellement tricher en faisant un peu autrement, mais c'est tordu et concrètement inutile.

Je pourrais exposer mes suppositions du pourquoi c'est interdit si ça vous intéresse de parler machine virtuelle et théorie des langages, mais ça risque d'être un peu long et un peu compliqué, et je ne suis pas totalement certain d'avoir raison.

Donc la seule et unique façon de faire est:

let e = document.getElementById('element');
if (e) e.innerHTML = '...';


EDIT: Allez, pour le fun, la tentative de triche tordue et inutile. J'ai même pas testé:


HTMLElement.prototype.setInnerHTML = function (html) {
  this.innerHTML = html;
}

document.getElementById('element')?.setInnerHTML('...');


Eh oui ! ON ne peut pas assigner, mais par contre on peut tout à fait appeler une méthode...
Modifié par QuentinC (07 May 2025 - 18:29)
Modérateur
parsimonhi a écrit :
Bonjour,

Ce ? est un nid à bug !

Amicalement,


Personnellement, je ne vois pas ça comme ça. Si tu as un exemple de contexte problématique, je suis preneur. Je dirai au contraire que c'est une bonne chose. Ça évite le fameux :

if(document.getElementById('unId') !== undefined){
    // ...
}


bien à toi Smiley smile
Modérateur
Bonjour,

niuxe a écrit :
Personnellement, je ne vois pas ça comme ça. Si tu as un exemple de contexte problématique, je suis preneur.

Si tu relis les réponses faites un peu plus haut, Mathieuu (qui n'est pas un débutant) s'est fait prendre, et Quentin (qui n'est pas plus un débutant) semble avoir découvert qu'on ne pouvait pas faire d'assignement si ce ? se trouve dans la partie gauche de l'instruction. Et moi-même, j'ai failli me faire prendre et j'ai dû tester. Et dans 6 mois, j'aurai tout oublié (parce que les occasions d'utiliser cette instruction à bon escient me semblent plutôt rares si le code est bien conçu), et je serai à nouveau prêt à me faire prendre.

Ça fait beaucoup ! Smiley cligne

Amicalement,
Bonsoir,

Moi aussi j'aimerais bien avoir un exemple de ce que tu considères problématique avec ?.

Le fait de ne pas pouvoir assigner avec ?. n'est pas un bug ou un piège, c'est une limite qui a été intentionnellement mise pour éviter encore plus de confusion justement. J'ai effectivement découvert ça hier en vous lisant, mais je n'ai absolument pas l'impression de m'être fait avoir, comme tu le dis. J'utilise régulièrement ?. depuis déjà un bon moment.

En fait,

a?.b?.c?.d


N'est en réalité rien d'autre qu'un sucre syntaxique équivalent à :

a && a.b && a.b.c && a.b.c.d


A cela près que c'est évidemment optimisé pour n'évaluer qu'une seule fois les opérandes au plus, contre potentiellement 4 fois a, 3 fois a.b, 2 fois a.b.c avec la version désucrée. Ca a été rajouté car c'est quelque chose qu'on fait assez souvent, et la version avec && est inutilement répétitive et source d'erreur.

ON comprend tout de suite mieux pourquoi ce code n'est pas valable et ne fait aucun sens.

a && a.b && a.b.c && a.b.c.d = X



Ca ne fait pas de sens car en principe, l'assignement est l'opérateur avec la plus basse priorité. Donc c'est équivalent à :

( (a) && (a.b) && ((a.b).c) && (((a.b).c).d) )  = X


ET il est plus ou moins implicitement défini dans le langage que l'expression à gauche ne peut pas avoir une priorité inférieure à . et []. Logique, car alors ça représente une expression calculée, et plus une référence assignable. Les développeurs C++ parleront de L-Expression (référence assignable) et de R-Expression (expression calculée non assignable).

Par contre on a le droit d'écrire:

a && a.b && a.b.c && (a.b.c.d = X)


ET là ça va marcher, l'assignement n'a simplement pas lieu si a ou a.b ou a.b.c sont null/undefined (grâce à la règle du court-circuit de && et ||).

Ce qui est intéressant, c'est qu'on ne peut pas retranscrire cette dernière expression avec ?. car si on écrit:

(a?.b?.c).d = X


alors on aura une erreur si a ou a.b ou a.b.c sont null/undefined (TypeError: cannot set property of undefined).

Alors oui on pourrait tout à fait changer les règles et autoriser
a?.b?.c?.d = X
. Mais ça fait beaucoup trop vite oublier l'aspect court-circuitant, et puis quand on veut assigner une valeur à une propriété, en principe on sait déjà et on est sûr que l'objet existe. On pourrait aussi imaginer créer implicitement les objets intermédiaires s'ils n'existent pas, mais est-ce que le raccourci en vaut la chandelle ? Pas sûr.
Modérateur
Bonjour,
QuentinC a écrit :
Moi aussi j'aimerais bien avoir un exemple de ce que tu considères problématique avec ?.[/code]

Je ne dis pas qu'il ne faut pas l'utiliser. Je dis juste qu'on peut se faire prendre assez facilement.

Après, c'est comme tout, si on l'utilise régulièrement, ça peut très certainement le faire.

Amicalement,