Pages

Projet

Ecrire le code pour parcourir le DOM.

 

Outils à maîtriser


Les événements

Apprenez à déboguer du code sur un événement !

Espace de travail

Éditeur et débogueur : un même espace de travail.

let !== var

Nous allons au travers de la déclaration d'une boucle remarquer une différence fondamentale entre la définition d'une variable avec var et let.

var permet le Hoisting et let a une portée par bloc {}.


code

On déclare un tableau, qui contient une fonction anonyme (sans nom) renvoyant la valeur d'une variable.

var arr = [],
      i = 0;

arr.push(() => i); // équivalent à arr.push(function(){return i});

console.log( arr[0]() ) // exécution de la fonction anonyme



Cas de la boucle


Étudiez ce code 

Projet

Des boucles et encore

Test de niveau

Test CSS !

CSS test

quizz dom

Quizz DOM

Parcours



Voici quelques codes avec parcours de l'arbre du DOM

Code de base
MAP
REDUCE + es6


Testez vous !
Test1
Test2

test

<p id="para-01">
  <span>First span</span>
</p>

<script type="text/javascript">
  var p01 = document.getElementById('para-01');
  alert(p01.firstChild.nodeName)
</script>
Rappel : element.nodeName donne le nom de l'élément.

Que va afficher le code précedent ?

valeur de l'index : reduce

comparez les index dans les cas suivants

[0, 1, 2, 3, 4].reduce(function(accumulateur, valeurCourante, index, array){
console.log(`${index} : ACC= ${accumulateur}, Cour= ${valeurCourante}`);
  return accumulateur + valeurCourante;
});

avec une valeur initiale :

[0, 1, 2, 3, 4].reduce(function(accumulateur, valeurCourante, index, array){
console.log(`${index} : ACC= ${accumulateur}, Cour= ${valeurCourante}`);
  return accumulateur + valeurCourante;
},1000);

Afficher le nombre de chaque balise du DOM


Ecrire le code JS qui permet d'afficher le nombre de balises par type !

En action :

Injecter le code dans la console d'une page HTML prise au hasard.

allTag=(a=>{let b=[],c={};return _explore=(a=>{for(let c of a.children)b.push(c.nodeName),_explore(c)}),_explore(a),_getWordCnt=(a=>{return a.reduce(function(a,b){return a[b]=a[b]+1||1,a},[])}),c=_getWordCnt(b),_sort=(a=>{let b=[],c=Object.keys(a).sort((b,c)=>a[c]-a[b]);for(var d of c)b[d]=a[d];return b}),_sort(c)});let a=allTag(document.body);for(var b in a)a.hasOwnProperty(b)&&console.log(`la balise ${b} apparaît ${a[b]} fois `);

reduce en action : one more

const promos = [
  { promo: "L3miage", etudiants: ["Dupont", "Whells", "Toto"]},
  { promo: "L2miage", etudiants: ["Dupond", "Pathé"]},
  { promo: "M1miage", etudiants: ["Audu", "Baby"]},
]

const tousEtudiants = promos.reduce(function(prev, curr) {
  return [...prev, ...curr.etudiants];
},[]);

console.log(tousEtudiants.sort());

remarque, ... est bien un opérateur utilisé ici pour remplacer la concaténation.

classList : mon code

Nous allons écrire une classe JS simulant classList.

JavaScript
ptr = object.classList



C'est un vrai exercice de programmation.

Nous allons juste développer la méthode add et donnerons le reste du code à étudier.

Nous savons récupérer les classes associées à un nœud du DOM.
HTML<element class="p" ... >
JavaScript
Notons que le type retourné est "string"

typeof(object.className)


Mise en place de la fonction add

Pour manipuler (ajouter, supprimer) un string, il est fréquent de transformer le string en tableau pour utiliser les méthodes efficaces sur les tableaux.

  1. transforme string en tableau
  2. ajoute la class au tableau
  3. transforme tableau en string

Voici les fonctions de base

var t = document.getElementById("f");
var classTab,
    classString;
console.log(" type of = " , typeof(t.className));

// string->tab
classTab = t.className.split(" ");
console.log(classTab);

//tab->string
classString = classTab.join(" ");
console.log(classString);


JS Bin on jsbin.com

Nous pouvons tester la présence d'une classe avant ajout

JS Bin on jsbin.com

Amélioration du code

Cela marche, parfait, mais c'est maintenant que la partie intéressante commence !
  1. Créer des fonctions pour réutiliser notre code de base
  2. Introduire des éléments de test
Création de fonctions de base

    var classesSeparator = " ";

    function splitClasses(classesString) {
        return classesString.split(classesSeparator);
    }

    function joinClasses(classes) {
        return classes.join(classesSeparator);
    }

    function indexInClasses(className, classes) {
        if (!className || !classes || !classes.length)
            return false;
        return classes.indexOf(className);
    }
JS Bin on jsbin.com

Amélioration du code

Il est foncdemental d'imaginer que ce code va rentrer en conflit avec une autre bibliothèque JS. Imaginer que notre fonction add soit utilisée ainsi add(1,2) ?

Les améliorations consistent


  1. Création d'un espace de noms
  2. Mise en évidence des fonctions internes
  3. Retour d'un objet pointant sur les fonctions

Création d'un espace de nom

Classes = (function () {
    //code
})();

Mise en évidence des fonctions et variables internes

Nous marquons ces items avec un _

exemple :
 var _classesSeparator = " ";


Retour de l'objet

La partie la plus subtile.

Classes = (function () {
...
    return {
        "add": add
    };
})();

Le  principe reste simple. 

Une fonction anonyme s'auto exécute et renvoie un objet que l'on affecte à Classes.
Classes est un objet qui a un méthode add qui pointe en réalité sur la méthode add que nous avons définie et qui fait appelle à des fonctions internes qui ne sont plus atteignable : TOP !


JS Bin on jsbin.com

Pour finir, on peut evisager d'écrire l'ensemble des fonctions utiles.
  JS Bin on jsbin.com

DOM en action

JS Bin on jsbin.com
JS Bin on jsbin.com

with closure !
JS Bin on jsbin.com

DOM : API

Property / Method Description
element.appendChild() Adds a new child node, to an element, as the last child node
element.attributes Returns a NamedNodeMap of an element's attributes
element.childElementCount Returns the number of child elements an element has
element.childNodes Returns a collection of an element's child nodes (including text and comment nodes)
element.children Returns a collection of an element's child element (excluding text and comment nodes)
element.classList Returns the class name(s) of an element
element.className Sets or returns the value of the class attribute of an element
element.cloneNode() Clones an element
element.contains() Returns true if a node is a descendant of a node, otherwise false
element.contentEditable Sets or returns whether the content of an element is editable or not
element.firstChild Returns the first child node of an element
element.firstElementChild Returns the first child element of an element
element.getAttribute() Returns the specified attribute value of an element node
element.getAttributeNode() Returns the specified attribute node
element.getElementsByClassName() Returns a collection of all child elements with the specified class name
element.getElementsByTagName() Returns a collection of all child elements with the specified tag name
element.hasChildNodes() Returns true if an element has any child nodes, otherwise false
element.id Sets or returns the value of the id attribute of an element
element.innerHTML Sets or returns the content of an element
element.insertBefore() Inserts a new child node before a specified, existing, child node
element.lastChild Returns the last child node of an element
element.lastElementChild Returns the last child element of an element
element.nextSibling Returns the next node at the same node tree level
element.nextElementSibling Returns the next element at the same node tree level
element.nodeName Returns the name of a node
element.nodeType Returns the node type of a node
element.nodeValue Sets or returns the value of a node
element.parentNode Returns the parent node of an element
element.parentElement Returns the parent element node of an element
element.previousSibling Returns the previous node at the same node tree level
element.previousElementSibling Returns the previous element at the same node tree level
element.querySelector() Returns the first child element that matches a specified CSS selector(s) of an element
element.querySelectorAll() Returns all child elements that matches a specified CSS selector(s) of an element
element.removeAttribute() Removes a specified attribute from an element
element.removeChild() Removes a child node from an element
element.replaceChild() Replaces a child node in an element
element.setAttribute() Sets or changes the specified attribute, to the specified value
element.setAttributeNode() Sets or changes the specified attribute node
element.style Sets or returns the value of the style attribute of an element
element.tagName Returns the tag name of an element
element.textContent Sets or returns the textual content of a node and its descendants
nodelist.item() Returns the node at the specified index in a NodeList
nodelist.length Returns the number of nodes in a NodeList

without loups, without loups !

http://jrsinclair.com/articles/2017/javascript-without-loops/?utm_source=javascriptweekly&utm_medium=email

en action

JS Bin on jsbin.com


const words = ["Beau", "Balle", "Ange",
"Ananas", "Reduce","Action"];


const alphabetical = words.reduce((a, x) => {
if(!a[x[0]]) a[x[0]] = [];
a[x[0]].push(x);
return a; }, {});

Object.keys(alphabetical).forEach(function (key) {
 console.log(` ${key} => ${alphabetical[key]}`);
});

console.log(alphabetical);

type primitif

... un opérateur intérésant

function fx(x,y,z) {
   console.log( x, y, z );
}

fx( ...[1,2,3] );
function fx(...args) {
   console.log( args );
}

fx( 1, 2, 3, 4, 5); // 

Ici ... propage
Ici … rassemble


Les deux ensemble : 
function foo(...args) {

args.shift();
args.push("BOB");

console.log( ...args );
}

foo("denis", "DUPONT");

  • "DUPONT"
  • "BOB"

Autre cas
function fx(x,...args) {
   console.log( x, args );
}


fx( 1, 2, 3, 4, 5);
  • 1
  • [2, 3, 4, 5]

idée : forEach

Amélioration du code

La structuration de valeurs dans un tableau, nécessite une boucle pour réaliser un parcours des valeurs :

Affichage

let array = [1, 2, 3];
for (let i = 0; i < array.length; i++) {
  let current = array[i];
  console.log(current); // affichage
}


Imaginons l'affichage des valeurs comme une action.

for (let i = 0; i < array.length; i++) {
  let current = array[i];
  actionAffichage(current); // affichage
}

Affichage positif

Pour afficher les valeurs positives, il est facile de créer une condition ; la fonction précédente devient.

for (let i = 0; i < array.length; i++) {
  let current = array[i];
  if (current>0) actionAffichage(current);
}

ou

for (let i = 0; i < array.length; i++) {
  let current = array[i];
  actionAffichagePositif(current);
}


Affichage Négatif

Pour afficher les valeurs négatives, il est facile de créer une condition ; la fonction précédente devient.

for (let i = 0; i < array.length; i++) {
  let current = array[i];
  actionAffichageNégatif(current);
}


Bilans

Pour chaque type d'affichage, il est nécessaire de redéfinir la boucle.

Améliorations

Nous allons encapsuler le code de la boucle dans une fonction forEach.

Il nous reste à réfléchir sur le passage des actions (positif, négatif ...) comme paramètres.

Ainsi,  "afficher quelque chose" peut être vu comme une fonction.
Les fonctions étant des variables et comme les variables peuvent être passées en paramètres, on peut passer "afficher quelque chose" à une fonction.

Ainsi, la fonction "afficher quelque chose" sera passée en paramètre à la fonction forEach.

Résumé des codes


Affichage

function affVal(v){
   console.log(`val tab : ${v}`)
}


Affichage Positif

function AffPositif(v){
   if (v>0) console.log(` val positif : ${v} `);
}


Boucle sur le tableau

function forEach(array, fx) {
  for (var i = 0; i < array.length; i++)//for of
    fx(array[i]);
}


Appel


forEach(T, AffPositif);

Appel anonyme

forEach(T, function (v){
  console.log(`-> tab : ${v}`);
});


En Action

Fichier code

JS Bin on jsbin.com

Pour aller plus loin !

forEach(filtre(T, EstPositif),affVal)

Définir la fonction "filtre" qui prend en paramètre également une fonction permettant de ne concerner que certaines valeurs du tableau.

Ainsi la fonction affVal ne fait plus qu'afficher, elle ne filtre pas les valeurs. 


Extension


Pour voir le comportement en fonctions de média ! 
 emmet


En action : 


callback !

Rappel :

let testCallback = function(val,callback) {

   callback(2*val, " est égale à 2 fois ", val);
};


let monCallback = function(...arg) {

   console.log( ` Le saviez vous : ${arg.join("")} ! `);
};


testCallback( 2, monCallback ); 


En action

Allons plus loin : la gestion d'erreur.


let racine = function (input, callback){
 let result = 0,

       square= function (num, callback){
(num>0) ? callback(null, Math.sqrt(num)) : callback(" Le traitement des racines de nombres négatifs n'est pas uniforme ");
},

      handleResult = function(error,result){
if (error) {
             callback(error); return;}
callback(null,result);
}

   square(input,handleResult);

 };



racine( 9, function (error,result){
if (error) {console.error(error);return; };
console.log(result);
});



En action