Aide-mémoire JS

Un mémo des manipulations courantes en JavaScript

a marqué ce sujet comme résolu.

Bonjour tout le monde !

Comme c'est déjà le cas pour pas mal de langages, je me suis dit qu'il était temps de mettre en place une petite FAQ des choses courantes en JavaScript.

Pour l'instant il n'y a pas des masses de contenu, mais à terme (en fonction des questions posées sur le forum par exemple) il y en aura un peu plus, promis !

Généralités sur le langage

Manipuler le DOM

Manipuler les tableaux

Manipuler les chaînes

Manipuler les nombres

Aller plus loin avec les fonctions


Si vous avez des idées de pense-bêtes du genre ou de questions récurrentes à ajouter à la liste, n'hésitez pas à me le signaler par MP ! ;)

Ajouter un élément dans un tableau

À la fin du tableau (méthode recommandée)

1
2
3
var tableau = ["foo", "bar"];
tableau.push("toto");
console.log(tableau); // ["foo", "bar", "toto"]

À un endroit précis (écrase l'éventuel contenu existant à cet emplacement)

1
2
3
var tableau = ["foo", "bar"];
tableau[2] = "toto";
console.log(tableau); // ["foo", "bar", "toto"]

Supprimer un élément d'un tableau

S'il s'agit du dernier élément

1
2
3
var tableau = ["foo", "bar", "toto"];
tableau.pop(); // "toto"
console.log(tableau); // ["foo", "bar"]

Plus d'infos sur la méthode pop

Sinon…

1
2
3
var tableau = ["foo", "bar", "toto"];
tableau.splice(1, 1); // ["bar"]
console.log(tableau); // ["foo", "toto"]

Plus d'infos sur la méthode splice

Transformer en slug pour une URL lisible et simple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
String.prototype.toSlug = function () {
    var value = "" + this; // On évite d'écraser la chaîne d'origine en créant une copie de travail

    // `re` correspond à la regex de recherche, `ch` étant le caractère correspondant (celui à utiliser en remplacement)
    var accents = [
        { re: /[\xC0-\xC6]/g, ch: 'A' },
        { re: /[\xE0-\xE6]/g, ch: 'a' },
        { re: /[\xC8-\xCB]/g, ch: 'E' },
        { re: /[\xE8-\xEB]/g, ch: 'e' },
        { re: /[\xCC-\xCF]/g, ch: 'I' },
        { re: /[\xEC-\xEF]/g, ch: 'i' },
        { re: /[\xD2-\xD6]/g, ch: 'O' },
        { re: /[\xF2-\xF6]/g, ch: 'o' },
        { re: /[\xD9-\xDC]/g, ch: 'U' },
        { re: /[\xF9-\xFC]/g, ch: 'u' },
        { re: /[\xC7-\xE7]/g, ch: 'c' },
        { re: /[\xD1]/g, ch: 'N' },
        { re: /[\xF1]/g, ch: 'n' }
    ];

    // On convertit les caractères accentués en leurs équivalent alphanumériques (via le tableau défini juste au-dessus)
    for (var i=0, nb=accents.length; i<nb; i++) {
        value = value.replace(accents[i].re, accents[i].ch);
    }

    // On passe en minuscules, on remplace les espaces par des tirets, on enlève les caractères non-alphanumériques puis on enlève les tirets multiples
    return value.toLowerCase()
                .replace(/\s+/g, '-')
                .replace(/[^a-z0-9-]/g, '')
                .replace(/\-{2,}/g,'-');
};

Ajouter/supprimer une class

1
var elem = document.getElementById('mon-element');

Ajouter une class

1
elem.classList.add('toto');

Ajouter plusieurs class

1
elem.classList.add('foo', 'bar');

Retirer une class

1
elem.classList.remove('toto');

Basculer une class

1
elem.classList.toggle('toto');

Cette méthode ajoutera votre class si elle n'existe pas, sinon elle la retirera.

Ou, si votre élément n'a aucune class1 et/ou que vous voulez utiliser quelque chose de plus compatible :

1
2
elem.className = "foo bar"; // On définit quelques class de base
elem.className += "toto"; // On en ajoute une, quoi qu'il arrive

  1. Jeu de mot non-intentionnel. Si celui-ci vous a fait rire (ou sourire) pensez à consulter. 

Créer et injecter un élément dans le DOM

Avec du HTML dedans

1
2
3
var elem = document.createElement('div'); // On crée une div vide
elem.innerHTML = '<p>Toto</p>'; // On définit le HTML (qui sera évalué par le client) de notre nœud
elemParent.appendChild(elem); // On ajoute notre div à l'élément parent (que l'on aura sélectionné ou créé avant)

Avec du texte brut

1
2
3
var elem = document.createElement('div'); // On crée une div vide
elem.textContent = 'Toto'; // On définit le contenu textuel de notre nœud
elemParent.appendChild(elem); // On ajoute notre div à l'élément parent (que l'on aura sélectionné ou créé avant)
Ou pour rester compatible (avec du texte brut)
1
2
3
4
var elem = document.createElement('div'); // On crée une div vide
var txt = document.createTextNode('Toto'); // On crée un nœud texte avec son contenu
elem.appendChild(txt); // On ajoute le noeud texte dans la div
elemParent.appendChild(elem); // On ajoute notre div à l'élément parent (que l'on aura sélectionné ou créé avant)

Une syntaxe différente pour une boucle for

En modifiant la façon d'écrire notre boucle for il est possible d'éviter certains calculs répétitifs et de déclarer des variables locales.

1
2
3
for (var lignes = document.querySelectorAll('table tr'), i = 0; i < lignes.length; i++) {
    /* Le contenu de la boucle ici */
}

Cela permet donc de remplacer la syntaxe habituelle suivante :

1
2
3
4
5
var lignes = document.querySelectorAll('table tr');

for (var i = 0; i < lignes.length; i++) {
    /* Le contenu de la boucle ici */
}

On évite ainsi de déclarer lignes à l'extérieur de la boucle, ce qui n'est pas forcément utile si l'on a besoin de celle-ci qu'à cet endroit.

Cette syntaxe permet aussi de déclarer des variables avant l'exécution de la boucle, ce qui évite de faire des calculs et/ou traitements à chaque tour.

Explication

Pour faire simple, on utilise simplement la syntaxe suivante pour définir plusieurs variables en JS :

1
2
3
var a = "foo",
    b = "bar",
    c = "toto";

On définit ainsi plusieurs variables en une seule instruction. Cela permet de conserver un code concis, évite de se répéter et permet ici de définir une variable dont le scope correspond à la boucle for.

On pourrait également modifier notre code comme ceci (mais on se complique la vie pour pas grand chose) :

1
2
3
for (var lignes = document.querySelectorAll('table tr'), nb = lignes.length, i = 0; i < nb; i++) {
    /* Le contenu de la boucle ici */
}

Exemple modifié suite aux remarques pertinentes de La source.

Positionner ou déplacer un élément du DOM

Supposons le HTML suivant :

1
2
3
4
5
6
<ul id="ma-liste">
    <li>N°1</li>
    <li>N°2</li>
    <li>N°3</li>
    <li>N°4</li>
</ul>

On crée un nouvel élément que l'on positionnera :

1
2
var nouveau = document.createElement('li');
nouveau.textContent = 'Je suis le nouveau';

Pour déplacer un élément existant (récupéré via document.getElementById par exemple), il suffit de le positionner comme vous le feriez pour un nouvel élément : il sera retiré de son emplacement actuel pour être inséré au nouveau

Placer en dernier

1
2
3
var liste = document.getElementById('ma-liste');

liste.appendChild(nouveau);

Placer en premier

1
2
3
var liste = document.getElementById('ma-liste');

liste.insertBefore(nouveau, liste.firstChild);

Placer avant un élément

1
2
3
var reference = document.querySelector('#ma-liste>li:nth-child(2)'); //  On récupère le N°2

reference.parentNode.insertBefore(nouveau, reference);

Placer après un élément

1
2
3
4
5
6
7
8
var reference = document.querySelector('#ma-liste>li:nth-child(3)'); //  On récupère le N°3 pour changer

if (reference.nextSibling) { // Si le nœud de référence n'est pas le dernier
    reference.parentNode.insertBefore(nouveau, reference.nextSibling);
}
else { // Sinon (le nœud de référence est le dernier)…
    reference.parentNode.appendChild(nouveau);
}

Créer et utiliser une closure

Créer une closure

1
2
3
4
5
6
7
// Une closure est une fonction…
function styliser (propriete, valeur) {
    // … qui retourne une autre fonction
    return function (elem) {
        elem.style[propriete] = valeur;
    }
}

Utiliser cette closure

1
2
3
4
5
6
7
// On peut ainsi générer plusieurs fonctions similaires facilement

var fondBleu = styliser('background-color', 'blue');

var texteRouge = styliser('color', 'red');
var texteVert = styliser('color', 'green');
var transparent = styliser('opacity', 0);

On obtient donc rapidement 4 nouvelles fonctions, en utilisant une seule fonction de base.

1
2
3
4
fondBleu(document.body);
texteRouge(document.querySelector('#message-erreur'));
texteVert(document.querySelector('#message-succes'));
transparent(document.querySelector('#je-suis-invisible'));

Simple, non ? :)

// Une closure est une fonction… // … qui retourne une autre fonction

Non, l'exemple que tu donnes n'est pas une closure. En revanche la fonction retourne est une closure. Un exemple minimal de closure serait plutôt ceci : (attention c'est très moche)

1
2
3
4
var x = 1;
function add1(y) {
  return y + x;
}

En d'autres termes, comme je le disais sur un autre topic de ce forum il y a quelques minutes :

Une closure est une fonction qui référence/utilise des variables qui n'appartiennent pas au scope de la fonction en question.

+0 -0

Je donnerais deux exemples. 1/ Celui que j'ai donné ci-dessus pour illustrer la définition. 2/ Un exemple où l'utilisation d'une closure est réellement utile. Là je te laisse choisir, parce que je vois plein de cas très différents où une closure est utile ou indispensable.

+0 -0

Mettre le contenu contenu dans la balise body dans une "div wrapper"

Code HTML de départ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<div id="pierre">
    <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
        proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
</div>

<div id="paul">
    <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
        proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
</div>

<div id="jacques">
    <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
        proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
</div>

Code HTML souhaité

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<section id="wrapper" class="container">

    <div id="pierre">
        <!-- Son contenu -->
    </div>

    <div id="paul">
        <!-- Son contenu -->
    </div>

    <div id="jacques">
        <!-- Son contenu -->
    </div>

</section>

Pour afficher le résultat, il faut ouvrir l'outil de développeur présent dans votre navigateur favori.

Le script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Les élements présents dans body (noeuds)
var childs = document.body.childNodes;

// Le type d'élément à créer
var new_wrapper = document.createElement('section');

// Le nom de l'id créé
new_wrapper.id = "wrapper";

// Le nom de la classe créée
new_wrapper.className = 'container';

// Lecture des noeuds
for (var i = 0; i < childs.length; i++) {
    // Insertion des données dans le wrapper
    new_wrapper.appendChild(childs.item(i));
    // Insertion du wrapper avant le 1er item
    childs.item(0).parentNode.insertBefore(new_wrapper, childs.item(i));
}

Exclusion d'une ou plusieurs div

On peut ainsi exclure les 2 dernières div ("paul" et "jacques") en mettant en place une condition dans la boucle pour ne pas les prendre dans le wrapper.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for (var i = 0; i < childs.length; i++) {
    if (childs.item(i).localName == 'div') {
        // Exclusion des 2 dernières div par leur id
        if (childs.item(i).id !== 'paul' && childs.item(i).id !== 'jacques') {
            // Insertion des données dans le wrapper
            new_wrapper.appendChild(childs.item(i));
            // Insertion du wrapper avant le 1er item
            childs.item(0).parentNode.insertBefore(new_wrapper, childs.item(i));
        }
    }
}
+0 -0

Non, l'exemple que tu donnes n'est pas une closure.

En fait n'importe quelle fonction est une closure : c'est à dire la combinaison d'une fonction et d'un objet qui contient l'environnement de la fonction à sa déclaration. Cet objet contient toutes les variables libres de l'environnement qui vont être utiles pour le fonctionnement de la fonction. Dans le code ci-dessous l'environnement contient la variable a car est utile et ne contient pas la variable b car celle-ci ne servira pas à la fonction.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var a = 1;
var closureFunc = function () {
    // l'environnement determine la valeur qui doit être utilisée par a
    console.log(a);  // le seul a auquel on peut accéder dans notre environnement est celui 
        // qui vaut 1 !
};

var anotherFunc = function () {
    var a = 2;
    return closureFunc();
};

anotherFunc(); // 1
// quand la fonction closureFunc est invoquée, elle utilise son propre 
// environnement pour connaitre la valeur de a, et non l'environnement
// du contexte d'invocation. => closure

N'importe quelle fonction est une closure mais en pratique ce n'est utile que pour "capturer" l'environnement quand il change. D'où la confusion courrante entre une fonction "normale" et une fonction "closure". Mais il n'existe pas d'un coté les closures et de l'autre les fonctions non closure.

Toutes les fonctions sont des closures, même si on ne l'utilise pas toujours.


PS 1 : Je ne suis pas sur à 100% que l'environnement ne contienne bien que les variables utiles à l'utilisation de la fonction mais il me semble bien que c'est comme ça que ça marche. Pourquoi garder la trace en mémoire des variables qui ne seront pas utiles sinon ?

PS 2 : Du coup si ça fonctionne bien ainsi une fonction sans aucune variable et qui ferait simplement un console.log n'a pas de variables libres dont il est utile de garder la trace… Est ce qu'elle posséde alors un objet d'environnement ? Si non cela voudrait dire techniquement que toutes les fonctions ne sont pas des closures. A mon avis le langage doit quand même créer un objet vide et le garder vide, car c'est ce qui semble le plus pratique. Si vous avez des réponses à ces questions je suis preneur !

+0 -0

Non, l'exemple que tu donnes n'est pas une closure.

En fait n'importe quelle fonction est une closure : c'est à dire la combinaison d'une fonction et d'un objet qui contient l'environnement de la fonction à sa déclaration.

Non. Et comme tu construis ton post sur cette définition fausse, je ne vais pas répondre au reste.

Par exemple :

1
const f = () => 1;

n'est pas une closure. f ne dépend pas de ce que contient le scope parent. f ne capture aucune valeur du scope parent. Peut importe le scope dans lequel f sera appelé, ce qu'il retournera ne dépendra pas du scope dans lequel il a été appelé ou défini.

+0 -0

Tu devinera que je parlais en ES5 et d'un truc destiné aux débutants, j'aurai peut être dû le préciser, en effet. Je ne crois pas qu'une personne qui utilise les fonctions fléchés ai besoin d'aide pour comprendre ce qu'est une closure si ? :p

+0 -1
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte