23 octobre 2022

Modification du document

La modification DOM est la clĂ© pour crĂ©er des pages “live”.

Ici, nous verrons comment crĂ©er de nouveaux Ă©lĂ©ments “à la volĂ©e” et modifier le contenu de la page existante.

Exemple : afficher un message

Démontrons en utilisant un exemple. Nous allons ajouter un message sur la page qui est plus joli que alert.

Voici Ă  quoi cela ressemblera :

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<div class="alert">
  <strong>Hi there!</strong> You've read an important message.
</div>

C’était un exemple HTML. CrĂ©ons maintenant la mĂȘme div avec JavaScript (en supposant que les styles sont dĂ©jĂ  dans le HTML ou un fichier CSS externe).

CrĂ©ation d’un Ă©lĂ©ment

Pour crĂ©er des nƓuds DOM, il existe deux mĂ©thodes :

document.createElement(tag)

Crée un nouveau noeud élément avec la balise donnée :

let div = document.createElement('div');
document.createTextNode(text)

CrĂ©e un nouveau nƓud texte avec le texte donnĂ© :

let textNode = document.createTextNode('Here I am');

La plupart du temps, nous devons crĂ©er des nƓuds d’élĂ©ment, tels que le div pour le message.

Création du message

La création du message div prend 3 étapes :

// 1. Create <div> element
let div = document.createElement('div');

// 2. Set its class to "alert"
div.className = "alert";

// 3. Fill it with the content
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

Nous avons créé l’élĂ©ment. Mais pour le moment, ce n’est que dans une variable nommĂ©e div, pas encore dans la page. Nous ne pouvons donc pas le voir.

MĂ©thodes d’insertion

Pour faire apparaĂźtre la div, nous devons l’insĂ©rer quelque part dans document. Par exemple, dans l’élĂ©ment <body>, rĂ©fĂ©rencĂ© par document.body.

Il existe une méthode spéciale append pour cela : document.body.append(div).

Voici le code complet :

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

  document.body.append(div);
</script>

Ici, nous avons appelĂ© append sur document.body, mais nous pouvons appeler la mĂ©thode append sur n’importe quel autre Ă©lĂ©ment, pour y mettre un autre Ă©lĂ©ment. Par exemple, nous pouvons ajouter quelque chose Ă  <div> en appelant div.append(anotherElement).

Voici plus de mĂ©thodes d’insertion, elles spĂ©cifient diffĂ©rents endroits oĂč insĂ©rer :

  • node.append(...nodes or strings) – ajouter des nƓuds ou des chaĂźnes de caractĂšres Ă  la fin de node,
  • node.prepend(...nodes or strings) – insĂ©rer des nƓuds ou des chaĂźnes de caractĂšres au dĂ©but de node,
  • node.before(...nodes or strings) –- insĂ©rer des nƓuds ou des chaĂźnes de caractĂšres avant node,
  • node.after(...nodes or strings) –- insĂ©rer des nƓuds ou des chaĂźnes de caractĂšres aprĂšs node,
  • node.replaceWith(...nodes or strings) –- remplace node avec les nƓuds ou chaĂźnes de caractĂšres donnĂ©s.

Les arguments de ces mĂ©thodes sont une liste arbitraire de nƓuds DOM Ă  insĂ©rer ou des chaĂźnes de texte (qui deviennent automatiquement des nƓuds de texte).

Voyons-les en action.

Voici un exemple d’utilisation de ces mĂ©thodes pour ajouter des Ă©lĂ©ments Ă  une liste et le texte avant/aprĂšs :

<ol id="ol">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  ol.before('before'); // insĂšre la chaĂźne de caractĂšres "before" avant <ol>
  ol.after('after'); // insĂšre la chaĂźne de caractĂšres "after" aprĂšs <ol>

  let liFirst = document.createElement('li');
  liFirst.innerHTML = 'prepend';
  ol.prepend(liFirst); // insÚre liFirst au début de <ol>

  let liLast = document.createElement('li');
  liLast.innerHTML = 'append';
  ol.append(liLast); // insĂšre liLast Ă  la fin de <ol>
</script>

Voici une image visuelle de ce que font les méthodes :

La liste finale sera donc :

before
<ol id="ol">
  <li>prepend</li>
  <li>0</li>
  <li>1</li>
  <li>2</li>
  <li>append</li>
</ol>
after

Comme indiquĂ©, ces mĂ©thodes peuvent insĂ©rer plusieurs nƓuds et morceaux de texte en un seul appel.

Par exemple, ici une chaßne de caractÚres et un élément sont insérés :

<div id="div"></div>
<script>
  div.before('<p>Hello</p>', document.createElement('hr'));
</script>

Remarque: le texte est insĂ©rĂ© “en tant que texte”, pas “en tant que HTML”, avec un Ă©chappement appropriĂ© des caractĂšres tels que <, >.

Le HTML final est donc :

&lt;p&gt;Hello&lt;/p&gt;
<hr>
<div id="div"></div>

En d’autres termes, les chaĂźnes de caractĂšres sont insĂ©rĂ©es de maniĂšre sĂ»re, comme le fait elem.textContent.

Ainsi, ces mĂ©thodes ne peuvent ĂȘtre utilisĂ©es que pour insĂ©rer des nƓuds DOM ou des morceaux de texte.

Mais que se passe-t-il si nous voulons insĂ©rer du HTML “en tant que html”, avec toutes les balises et les trucs qui fonctionnent, comme elem.innerHTML le fait ?

insertAdjacentHTML/Text/Element

Pour cela, nous pouvons utiliser une autre méthode assez polyvalente : elem.insertAdjacentHTML(where, html).

Le premier paramĂštre est un mot de code, spĂ©cifiant oĂč insĂ©rer par rapport Ă  elem. Doit ĂȘtre l’un des suivants :

  • "beforebegin" – insĂšre html immĂ©diatement avant elem,
  • "afterbegin" – insĂšre html dans elem, au dĂ©but,
  • "beforeend" – insĂšre html dans elem, Ă  la fin,
  • "afterend" – insĂšre html immĂ©diatement aprĂšs elem.

Le second paramĂštre est une chaĂźne HTML insĂ©rĂ©e “au format HTML”.

Par exemple :

<div id="div"></div>
<script>
  div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
  div.insertAdjacentHTML('afterend', '<p>Bye</p>');
</script>


Conduirait à :

<p>Hello</p>
<div id="div"></div>
<p>Bye</p>

VoilĂ  comment nous pouvons ajouter du code HTML arbitraire Ă  la page.

Voici l’image des variantes d’insertion :

Nous pouvons facilement remarquer des similitudes entre cette image et l’image prĂ©cĂ©dente. Les points d’insertion sont en fait les mĂȘmes, mais cette mĂ©thode insĂšre du HTML.

La mĂ©thode a deux sƓurs :

  • elem.insertAdjacentText(where, text) – la mĂȘme syntaxe, mais une chaĂźne de caractĂšres text est insĂ©rĂ©e en tant que texte au lieu de HTML,
  • elem.insertAdjacentElement(where, elem) – la mĂȘme syntaxe, mais insĂšre un Ă©lĂ©ment.

Elles existent principalement pour rendre la syntaxe “uniforme”. En pratique, seule insertAdjacentHTML est utilisĂ©e la plupart du temps. Parce que pour les Ă©lĂ©ments et le texte, nous avons des mĂ©thodes append/prepend/before/after – elles sont plus courtes Ă  Ă©crire et peuvent insĂ©rer des nƓuds/morceaux de texte.

Voici donc une variante alternative pour afficher un message :

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  document.body.insertAdjacentHTML("afterbegin", `<div class="alert">
    <strong>Hi there!</strong> You've read an important message.
  </div>`);
</script>

Suppression de noeuds

Pour supprimer un nƓud, il existe une mĂ©thode node.remove().

Faisons disparaĂźtre notre message aprĂšs une seconde :

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

  document.body.append(div);
  setTimeout(() => div.remove(), 1000);
</script>

Veuillez noter : si nous voulons dĂ©placer un Ă©lĂ©ment vers un autre endroit – il n’est pas nĂ©cessaire de le supprimer de l’ancien.

Toutes les mĂ©thodes d’insertion suppriment automatiquement le nƓud de l’ancien emplacement.

Par exemple, permutons les éléments :

<div id="first">First</div>
<div id="second">Second</div>
<script>
  // pas besoin d'appeler remove
  second.after(first); // prend #second et aprĂšs insĂšre #first
</script>

Clonage de Noeuds : cloneNode

Comment insérer un autre message similaire ?

Nous pourrions crĂ©er une fonction et y mettre le code. Mais l’alternative serait de cloner la div existant et de modifier le texte Ă  l’intĂ©rieur (si nĂ©cessaire).

Parfois, lorsque nous avons un gros Ă©lĂ©ment, cela peut ĂȘtre plus rapide et plus simple.

  • L’appel elem.cloneNode(true) crĂ©e un clone “profond” de l’élĂ©ment – avec tous les attributs et sous-Ă©lĂ©ments. Si nous appelons elem.cloneNode(false), alors le clone est fait sans Ă©lĂ©ments enfants.

Un exemple de copie du message :

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<div class="alert" id="div">
  <strong>Hi there!</strong> You've read an important message.
</div>

<script>
  let div2 = div.cloneNode(true); // clone the message
  div2.querySelector('strong').innerHTML = 'Bye there!'; // change le clone

  div.after(div2); // affiche le clone aprĂšs le div existant
</script>

DocumentFragment

DocumentFragment est un nƓud DOM spĂ©cial qui sert de wrapper pour passer autour des listes de nƓuds.

Nous pouvons y ajouter d’autres nƓuds, mais lorsque nous l’insĂ©rons quelque part, son contenu est insĂ©rĂ© Ă  la place.

Par exemple, getListContent ci-dessous génÚre un fragment avec des éléments <li>, qui sont ensuite insérés dans <ul> :

<ul id="ul"></ul>

<script>
function getListContent() {
  let fragment = new DocumentFragment();

  for(let i=1; i<=3; i++) {
    let li = document.createElement('li');
    li.append(i);
    fragment.append(li);
  }

  return fragment;
}

ul.append(getListContent()); // (*)
</script>

Veuillez noter qu’à la derniĂšre ligne (*) nous ajoutons DocumentFragment, mais il “s’adapte”, donc la structure rĂ©sultante sera :

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

DocumentFragment est rarement utilisĂ© explicitement. Pourquoi ajouter Ă  un type spĂ©cial de nƓud, si nous pouvons renvoyer un tableau de nƓuds Ă  la place ? Exemple réécrit :

<ul id="ul"></ul>

<script>
function getListContent() {
  let result = [];

  for(let i=1; i<=3; i++) {
    let li = document.createElement('li');
    li.append(i);
    result.push(li);
  }

  return result;
}

ul.append(...getListContent()); // append + "..." operator = friends!
</script>

Nous mentionnons DocumentFragment principalement parce qu’il y a quelques concepts dessus, comme l’élĂ©ment template, que nous couvrirons beaucoup plus tard.

MĂ©thodes d’insertion/suppression Ă  l’ancienne

Ancienne école
Ces informations aident à comprendre les anciens scripts, mais ne sont pas nécessaires pour du développement moderne.

Il existe Ă©galement des mĂ©thodes de manipulation du DOM “à l’ancienne”, qui existent pour des raisons historiques.

Ces mĂ©thodes viennent d’une Ă©poque trĂšs ancienne. De nos jours, il n’y a aucune raison de les utiliser, depuis qu’il existe des mĂ©thodes modernes, telles que append, prepend, before, after, remove, replaceWith, qui sont plus flexibles.

La seule raison pour laquelle nous listons ces méthodes ici est que vous pouvez les trouver dans de nombreux anciens scripts :

parentElem.appendChild(node)

Appends node as the last child of parentElem.

The following example adds a new <li> to the end of <ol>:

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  let newLi = document.createElement('li');
  newLi.innerHTML = 'Hello, world!';

  list.appendChild(newLi);
</script>
parentElem.insertBefore(node, nextSibling)

InsĂšre node avant nextSibling dans parentElem.

Le code suivant insÚre un nouvel élément de liste avant le second <li> :

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>
<script>
  let newLi = document.createElement('li');
  newLi.innerHTML = 'Hello, world!';

  list.insertBefore(newLi, list.children[1]);
</script>

Pour insérer newLi comme premier élément, nous pouvons le faire comme ceci :

list.insertBefore(newLi, list.firstChild);
parentElem.replaceChild(node, oldChild)

Remplace oldChild avec node chez les enfants de parentElem.

parentElem.removeChild(node)

Supprime node de parentElem (en supposant que node est son enfant).

L’exemple suivant supprime d’abord <li> de <ol> :

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  let li = list.firstElementChild;
  list.removeChild(li);
</script>

Toutes ces mĂ©thodes renvoient le nƓud insĂ©rĂ©/supprimĂ©. En d’autres termes, parentElem.appendChild(node) renvoie node. Mais gĂ©nĂ©ralement, la valeur retournĂ©e n’est pas utilisĂ©e, nous exĂ©cutons simplement la mĂ©thode.

Un mot sur “document.write”

Il existe une autre méthode trÚs ancienne pour ajouter quelque chose à une page Web : document.write.

La syntaxe :

<p>Somewhere in the page...</p>
<script>
  document.write('<b>Hello from JS</b>');
</script>
<p>The end</p>

L’appel Ă  document.write(html) Ă©crit le html dans la page “ici et maintenant”. La chaĂźne de caractĂšres html peut ĂȘtre gĂ©nĂ©rĂ©e dynamiquement, donc c’est un peu flexible. Nous pouvons utiliser JavaScript pour crĂ©er une page Web Ă  part entiĂšre et l’écrire.

La mĂ©thode vient de l’époque oĂč il n’y avait pas de DOM, pas de standards 
 Des temps vraiment anciens. Il vit toujours, car il existe des scripts qui l’utilisent.

Dans les scripts modernes, nous le voyons rarement, en raison de la limitation importante suivante :

L’appel à document.write ne fonctionne que pendant le chargement de la page.

Si nous l’appelons ensuite, le contenu du document existant est effacĂ©.

Par exemple :

<p>After one second the contents of this page will be replaced...</p>
<script>
  // document.write aprĂšs 1 seconde
  // cela aprÚs que la page soit chargée, donc il efface le contenu existant
  setTimeout(() => document.write('<b>...By this.</b>'), 1000);
</script>

C’est donc un peu inutilisable au stade “post chargement”, contrairement aux autres mĂ©thodes DOM que nous avons couvertes ci-dessus.

VoilĂ  l’inconvĂ©nient.

Il y a aussi un avantage. Techniquement, lorsque document.write est appelĂ© pendant que le navigateur lit (“analyse”) le HTML entrant, et qu’il Ă©crit quelque chose, le navigateur le consomme comme s’il Ă©tait initialement lĂ , dans le texte HTML.

Cela fonctionne donc trĂšs rapidement, car il n’y a aucune modification du DOM impliquĂ©e. Il Ă©crit directement dans le texte de la page, tandis que le DOM n’est pas encore construit.

Donc, si nous devons ajouter beaucoup de texte en HTML de maniĂšre dynamique, et que nous sommes en phase de chargement de page, et que la vitesse compte, cela peut aider. Mais dans la pratique, ces exigences se rencontrent rarement. Et gĂ©nĂ©ralement, nous pouvons voir cette mĂ©thode dans les scripts simplement parce qu’ils sont anciens.

Résumé

  • MĂ©thodes pour crĂ©er de nouveaux nƓuds :

    • document.createElement(tag) – crĂ©e un Ă©lĂ©ment avec la balise donnĂ©e,
    • document.createTextNode(value) – crĂ©e un nƓud texte (rarement utilisĂ©),
    • elem.cloneNode(deep) – clone l’élĂ©ment, si deep==true tous les descendants viennent avec.
  • Insertion et suppression :

    • node.append(...nodes or strings) – insĂšre dans node, Ă  la fin,
    • node.prepend(...nodes or strings) – insĂšre dans node, au dĂ©but,
    • node.before(...nodes or strings) –- insĂšre juste avant node,
    • node.after(...nodes or strings) –- insĂšre juste aprĂšs node,
    • node.replaceWith(...nodes or strings) –- remplace node.
    • node.remove() –- supprime le node.

    Les chaĂźnes de caractĂšres texte sont insĂ©rĂ©es “sous forme de texte”.

  • Il existe Ă©galement des mĂ©thodes “old school” :

    • parent.appendChild(node)
    • parent.insertBefore(node, nextSibling)
    • parent.removeChild(node)
    • parent.replaceChild(newElem, node)

    Toutes ces méthodes retournent le node.

  • Étant donnĂ© un peu de HTML dans html, elem.insertAdjacentHTML(where, html) l’insĂšre en fonction de la valeur de where :

    • "beforebegin" – insĂšre html juste avant elem,
    • "afterbegin" – insĂšre html dans elem, au dĂ©but,
    • "beforeend" – insĂšre html dans elem, Ă  la fin,
    • "afterend" – insĂšre html juste aprĂšs elem.

    Il existe également des méthodes similaires, elem.insertAdjacentText et elem.insertAdjacentElement, qui insÚrent des chaßnes de caractÚres texte et des éléments, mais ils sont rarement utilisés.

  • Pour ajouter du HTML Ă  la page avant la fin du chargement :

    • document.write(html)

    Une fois la page chargée, un tel appel efface le document. Surtout vu dans les anciens scripts.

Exercices

importance: 5

Nous avons un élément DOM vide elem et une chaßne de caractÚres text.

Lesquelles de ces 3 commandes feront exactement la mĂȘme chose ?

  1. elem.append(document.createTextNode(text))
  2. elem.innerHTML = text
  3. elem.textContent = text

Réponse : 1 et 3.

Les deux commandes ont pour rĂ©sultat d’ajouter le texte “en tant que texte” dans elem.

Voici un exemple :

<div id="elem1"></div>
<div id="elem2"></div>
<div id="elem3"></div>
<script>
  let text = '<b>text</b>';

  elem1.append(document.createTextNode(text));
  elem2.innerHTML = text;
  elem3.textContent = text;
</script>
importance: 5

CrĂ©ez une fonction clear(elem) qui supprime tout de l’élĂ©ment.

<ol id="elem">
  <li>Hello</li>
  <li>World</li>
</ol>

<script>
  function clear(elem) { /* votre code */ }

  clear(elem); // efface la liste
</script>

Voyons d’abord comment ne pas le faire :

function clear(elem) {
  for (let i=0; i < elem.childNodes.length; i++) {
      elem.childNodes[i].remove();
  }
}

Cela ne fonctionnera pas, car l’appel Ă  remove() dĂ©cale la collection elem.childNodes, donc les Ă©lĂ©ments commencent Ă  partir de l’index 0 Ă  chaque fois. Mais i augmente et certains Ă©lĂ©ments seront ignorĂ©s.

La boucle for..of fait de mĂȘme.

La bonne variante pourrait ĂȘtre :

function clear(elem) {
  while (elem.firstChild) {
    elem.firstChild.remove();
  }
}

Et il existe Ă©galement un moyen plus simple de faire de mĂȘme :

function clear(elem) {
  elem.innerHTML = '';
}
importance: 1

Dans l’exemple ci-dessous, l’appel table.remove() supprime le tableau du document.

mais si vous l’exĂ©cutez, vous pouvez voir que le texte "aaa" est toujours visible.

Pourquoi cela se produit-il ?

<table id="table">
  aaa
  <tr>
    <td>Test</td>
  </tr>
</table>

<script>
  alert(table); // la table, comme il se doit

  table.remove();
  // pourquoi y a-t-il encore "aaa" dans le document ?
</script>

Le code HTML de cette tùche est incorrect. Voilà la raison de la chose étrange.

Le navigateur doit le rĂ©parer automatiquement. Mais il peut ne pas y avoir de texte Ă  l’intĂ©rieur de la <table> : selon la spĂ©cification, seules les balises spĂ©cifiques Ă  la table sont autorisĂ©es. Le navigateur ajoute donc "aaa" avant la <table>.

Maintenant, il est évident que lorsque nous retirons la table, la chaßne de caractÚres reste.

La question peut ĂȘtre facilement rĂ©pondue en explorant le DOM Ă  l’aide des outils du navigateur. Vous verrez "aaa" avant la <table>.

Le standard HTML spécifie en détail comment traiter un mauvais HTML, et un tel comportement du navigateur est correct.

importance: 4

Écrivez une interface pour crĂ©er une liste Ă  partir des entrĂ©es utilisateur.

Pour chaque élément de la liste :

  1. Interrogez un utilisateur sur son contenu en utilisant prompt.
  2. Créez le <li> avec et ajoutez-le à <ul>.
  3. Continuez jusqu’à ce que l’utilisateur annule l’entrĂ©e (en appuyant sur la touche Esc ou une entrĂ©e vide).

Tous les Ă©lĂ©ments doivent ĂȘtre créés dynamiquement.

Si un utilisateur tape des balises HTML, elles doivent ĂȘtre traitĂ©es comme un texte.

DĂ©mo dans une nouvelle fenĂȘtre

Veuillez noter l’utilisation de textContent pour attribuer le contenu <li>.

Ouvrez la solution dans une sandbox.

importance: 5

Écrivez une fonction createTree qui crĂ©e une liste imbriquĂ©e ul/li Ă  partir de l’objet imbriquĂ©.

Par exemple :

let data = {
  "Fish": {
    "trout": {},
    "salmon": {}
  },

  "Tree": {
    "Huge": {
      "sequoia": {},
      "oak": {}
    },
    "Flowering": {
      "apple tree": {},
      "magnolia": {}
    }
  }
};

La syntaxe :

let container = document.getElementById('container');
createTree(container, data); // crée l'arbre dans le conteneur

Le résultat (arbre) devrait ressembler à ceci :

Choisissez l’une des deux façons de rĂ©soudre cette tĂąche :

  1. CrĂ©ez le code HTML de l’arborescence, puis attribuez-le Ă  container.innerHTML.
  2. CrĂ©ez des nƓuds d’arbre et ajoutez-les avec les mĂ©thodes DOM.

Ce serait génial si vous pouviez faire les deux.

P.S. L’arbre ne doit pas avoir d’élĂ©ments “supplĂ©mentaires” comme des <ul></ul> vides pour les feuilles (de l’arbre).

Open a sandbox for the task.

La façon la plus simple de parcourir l’objet est d’utiliser la rĂ©cursivitĂ©.

  1. La solution avec innerHTML.
  2. La solution avec DOM.
importance: 5

Il y a un arbre organisé comme un ul/li imbriqué.

Écrivez le code qui ajoute à chaque <li> le nombre de ses descendants. Sautez les feuilles (nƓuds sans enfants).

Le resultat :

Open a sandbox for the task.

Pour ajouter du texte à chaque <li>, nous pouvons modifier le nƓud texte data.

Ouvrez la solution dans une sandbox.

importance: 4

Écrivez une fonction createCalendar(elem, year, month).

L’appel doit crĂ©er un calendrier pour l’annĂ©e/le mois donnĂ© et le mettre dans elem.

Le calendrier doit ĂȘtre un tableau, oĂč une semaine est un <tr> et un jour est un <td>. Le dessus du tableau doit ĂȘtre un <th> avec les noms des jours de la semaine : le premier jour doit ĂȘtre le lundi, et ainsi de suite jusqu’au dimanche.

Par exemple, createCalendar(cal, 2012, 9) devrait gĂ©nĂ©rer dans l’élĂ©ment cal le calendrier suivant :

P.S. Pour cette tĂąche, il suffit de gĂ©nĂ©rer le calendrier, il ne doit pas encore ĂȘtre cliquable.

Open a sandbox for the task.

Nous allons crĂ©er le tableau sous forme de chaĂźne de caractĂšres : "<table>...</table>", puis l’affecter Ă  innerHTML.

L’algorithme :

  1. CrĂ©er l’en-tĂȘte du tableau avec les noms <th> et les jours de la semaine.
  2. CrĂ©ez l’objet de date d = new Date(year, month-1). C’est le premier jour de month (en tenant compte du fait que les mois en JavaScript commencent Ă  0, pas Ă  1).
  3. Les premiĂšres cellules jusqu’au premier jour du mois d.getDay() peuvent ĂȘtre vides. Remplissons-les avec <td></td>.
  4. Augmentez le jour en d: d.setDate(d.getDate()+1). Si d.getMonth() n’est pas encore le mois suivant, alors ajoutez la nouvelle cellule <td> au calendrier. Si c’est un dimanche, ajoutez une nouvelle ligne “</tr><tr>”.
  5. Si le mois est terminĂ©, mais que la ligne du tableau n’est pas encore pleine, ajoutez-y un <td> vide pour le rendre carrĂ©.

Ouvrez la solution dans une sandbox.

importance: 4

Créez une horloge colorée comme ici :

Utilisez HTML/CSS pour le style, JavaScript ne met à jour que le temps dans les éléments.

Open a sandbox for the task.

Tout d’abord, crĂ©ons notre HTML/CSS.

Chaque composante du temps aurait fiĂšre allure dans son propre <span> :

<div id="clock">
  <span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span>
</div>

Nous aurons également besoin de CSS pour les colorer.

La fonction update rafraĂźchira l’horloge, qui sera appelĂ©e par setInterval toutes les secondes :

function update() {
  let clock = document.getElementById('clock');
  let date = new Date(); // (*)
  let hours = date.getHours();
  if (hours < 10) hours = '0' + hours;
  clock.children[0].innerHTML = hours;

  let minutes = date.getMinutes();
  if (minutes < 10) minutes = '0' + minutes;
  clock.children[1].innerHTML = minutes;

  let seconds = date.getSeconds();
  if (seconds < 10) seconds = '0' + seconds;
  clock.children[2].innerHTML = seconds;
}

Dans la ligne (*) nous vérifions à chaque fois la date actuelle. Les appels à setInterval ne sont pas fiables : ils peuvent survenir avec des retards.

Les fonctions de gestion d’horloge :

let timerId;

function clockStart() { // exécute l'horloge
  if (!timerId) { // défini un nouvel intervalle uniquement si l'horloge ne fonctionne pas
    timerId = setInterval(update, 1000);
  }
  update(); // (*)
}

function clockStop() {
  clearInterval(timerId);
  timerId = null; // (**)
}

Veuillez noter que l’appel Ă  update() est non seulement planifiĂ© dans clockStart(), mais s’exĂ©cute immĂ©diatement dans la ligne (*). Sinon, le visiteur devra attendre la premiĂšre exĂ©cution de setInterval. Et l’horloge serait vide jusque-lĂ .

Il est Ă©galement important de dĂ©finir un nouvel intervalle dans clockStart() uniquement lorsque l’horloge ne fonctionne pas. Sinon, cliquer plusieurs fois sur le bouton de dĂ©marrage dĂ©finirait plusieurs intervalles simultanĂ©s. Pire encore, nous ne garderions que le timerID du dernier intervalle, perdant ainsi les rĂ©fĂ©rences Ă  tous les autres. Alors nous ne pourrions plus jamais arrĂȘter le chronomĂštre! Notez que nous devons effacer le timerID lorsque l’horloge est arrĂȘtĂ©e dans la ligne (**), afin qu’elle puisse ĂȘtre redĂ©marrĂ©e en exĂ©cutant clockStart().

Ouvrez la solution dans une sandbox.

importance: 5

Écrivez le code pour insĂ©rer <li>2</li><li>3</li> entre deux <li> ici :

<ul id="ul">
  <li id="one">1</li>
  <li id="two">4</li>
</ul>

Lorsque nous devons insérer un morceau de HTML quelque part, insertAdjacentHTML est le meilleur choix.

La solution :

one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>');
importance: 5

Il y a un tableau :

<table>
<thead>
  <tr>
    <th>Name</th><th>Surname</th><th>Age</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>John</td><td>Smith</td><td>10</td>
  </tr>
  <tr>
    <td>Pete</td><td>Brown</td><td>15</td>
  </tr>
  <tr>
    <td>Ann</td><td>Lee</td><td>5</td>
  </tr>
  <tr>
    <td>...</td><td>...</td><td>...</td>
  </tr>
</tbody>
</table>

Il peut y avoir plus de lignes.

Écrivez le code pour le trier par la colonne "name".

Open a sandbox for the task.

La solution est courte, mais peut sembler un peu délicate, alors ici je la présente avec de nombreux commentaires :

let sortedRows = Array.from(table.tBodies[0].rows) // 1
  .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));

table.tBodies[0].append(...sortedRows); // (3)

L’algorithme pas à pas :

  1. Obtenez tous les <tr> de <tbody>.
  2. Triez-les ensuite en les comparant au contenu du premier <td> (le champ du nom).
  3. InsĂ©rez maintenant les nƓuds dans le bon ordre par .append(...sortedRows).

Nous n’avons pas Ă  supprimer les Ă©lĂ©ments de ligne, il suffit de les “rĂ©insĂ©rer”, ils quittent automatiquement l’ancien emplacement.

P.S. Dans notre cas, il y a un <tbody> explicite dans le tableau, mais mĂȘme si le tableau HTML n’a pas de <tbody>, la structure DOM l’a toujours.

Ouvrez la solution dans une sandbox.

Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)