Javascript

Comment Visual Event permet-il d’inspecter les événements JavaScript dans Chrome ?

Quand une interface web réagit sans que l’on comprenne vraiment pourquoi, le problème vient souvent d’un événement JavaScript accroché quelque part dans la page. Un clic lance une fonction inattendue, un champ déclenche plusieurs traitements, un menu se referme tout seul, et dans Chrome on finit vite par ouvrir les DevTools, l’onglet Elements, la console, puis une série de fichiers minifiés pas toujours très parlants. C’est précisément là que Visual Event devient intéressant. Derrière son nom un peu ancien, l’outil reste très pratique pour voir rapidement quels éléments portent des écouteurs JavaScript et quel code leur est associé.

Le brouillon parle d’un plugin Chrome, mais dans la pratique Visual Event fonctionne comme un bookmarklet à lancer sur la page à inspecter. L’idée est simple : au lieu de chercher à l’aveugle où sont branchés les event listeners, l’outil ajoute une couche visuelle sur la page et signale les zones qui réagissent à des événements. Pour du débogage front-end concret, c’est souvent plus parlant qu’une longue exploration manuelle du DOM.

Si vous aimez les outils qui rendent le comportement du navigateur plus visible, vous pouvez aussi jeter un œil à cet autre billet du site. Et si votre sujet du moment tourne autour de l’inspection dans Chrome, ce second article connexe s’intègre naturellement à la même boîte à outils.

À quoi sert Visual Event dans un vrai cas de debug ?

Visual Event sert à répondre à une question très simple : quels éléments de cette page écoutent des événements JavaScript, et lesquels ? Sur une page riche en interactions, la réponse n’est pas toujours évidente. Un bouton peut réagir à un clic, une ligne de tableau à un survol, un champ à un change ou à un keyup, et plusieurs bibliothèques peuvent empiler leurs propres mécanismes au-dessus du DOM natif.

L’intérêt de Visual Event, c’est qu’il ne se contente pas de dire qu’il y a du JavaScript sur la page. Il affiche les éléments concernés, le type d’événement attaché, et même le code de la fonction associée. D’après la page officielle et le dépôt GitHub du projet, l’outil peut aussi indiquer le fichier source et la ligne où la fonction a été définie dans certains navigateurs basés sur WebKit. Dit autrement, on passe d’une intuition floue à une carte visuelle des interactions.

En phase de recette ou de maintenance, c’est utile quand on reprend un projet que l’on n’a pas développé soi-même. C’est aussi très pratique sur des interfaces anciennes où les événements ont été ajoutés au fil du temps par jQuery, MooTools ou d’autres couches historiques. On clique sur le bookmarklet et, en quelques secondes, on repère les zones de la page qui portent réellement du comportement.

Comment Visual Event fonctionne-t-il dans Chrome ?

// dans la console Chrome
getEventListeners(document.querySelector("button"))

Visual Event a été conçu comme un bookmarklet, donc son installation est légère. On ouvre la page officielle de l’outil, on glisse le lien “Visual Event” dans la barre de favoris, puis on l’active sur la page à examiner. C’est tout. Il n’y a pas de gros module à installer ni de panneau supplémentaire à configurer dans le navigateur.

Une fois lancé, l’outil analyse la page et tente de retrouver les événements attachés aux nœuds du DOM. C’est un point important : JavaScript ne fournit pas de méthode standard simple pour lister tous les écouteurs d’un élément depuis la page elle-même. Visual Event contourne donc cette limite en s’appuyant sur la façon dont certaines bibliothèques stockent leurs événements. C’est pour cela que le projet a documenté des parsers pour plusieurs environnements, notamment les événements DOM 0, jQuery 1.2+, YUI 2, MooTools 1.2+, Prototype 1.6+, Glow et, dans la version 2, ExtJS 4.0.x.

Autrement dit, Visual Event n’est pas un remplaçant universel des DevTools modernes. C’est un outil ciblé, pensé pour rendre visibles des liaisons d’événements que l’on ne voit pas immédiatement dans l’interface. Sur des projets classiques ou un peu anciens, ce focus fait justement sa force.

Pourquoi cet outil reste utile malgré les DevTools de Chrome ?

javascript:(function(){/* lancer le bookmarklet Visual Event */})();

Chrome DevTools sait déjà afficher beaucoup d’informations, y compris des écouteurs d’événements sur un élément précis. Le problème, c’est que cette inspection reste souvent locale : il faut sélectionner un nœud, ouvrir le bon panneau, puis recommencer ailleurs. Quand on ne sait pas encore où regarder, cette méthode peut prendre du temps.

Visual Event inverse la logique. Au lieu d’inspecter un nœud pour découvrir ses événements, il part des événements pour vous montrer où se trouvent les nœuds intéressants dans la page. C’est exactement ce qu’il faut quand un comportement parasite apparaît sur une page complexe. Par exemple, si un clic sur une carte produit deux actions concurrentes, ou si un formulaire exécute plusieurs traitements au moment de la saisie, l’affichage visuel permet de repérer très vite les éléments concernés.

Il y a aussi une dimension pédagogique. Sur une page tierce, Visual Event montre comment les interactions ont été distribuées dans le DOM. Pour un développeur ou un intégrateur, c’est un bon moyen de comprendre rapidement l’architecture comportementale d’une interface sans ouvrir immédiatement toute la base de code.

Dans quels cas Visual Event fait gagner du temps ?

Le premier cas, c’est la reprise de projet. Vous arrivez sur une application où “quelque chose” se passe au clic, mais personne ne sait plus très bien si l’événement vient du HTML, de jQuery ou d’un composant ajouté plus tard. Visual Event donne un premier niveau de réponse sans fouille longue.

Le deuxième cas, c’est le débogage d’effets de bord. Une modale se ferme quand elle ne devrait pas, un bouton a plusieurs gestionnaires, une zone interactive répond alors qu’elle semble passive à l’écran. En visualisant les événements présents sur la page, on élimine vite quelques hypothèses.

Le troisième cas, c’est l’audit d’interface. Quand on veut comprendre comment une page a été pensée, voir où sont branchés les événements apporte souvent plus d’informations qu’une simple lecture du balisage. Cela ne dispense pas d’ouvrir les DevTools ensuite, mais cela aide à savoir par où commencer.

Au quotidien, Visual Event n’a pas besoin de remplacer vos outils habituels. Il est surtout utile comme raccourci d’observation : un moyen rapide de rendre visible ce qui, d’ordinaire, reste enfoui dans les scripts et les handlers attachés au DOM. Pour un développeur qui travaille dans Chrome et qui doit comprendre vite pourquoi une page réagit, c’est un petit outil de debug qui mérite encore sa place dans les favoris.

Comment créer une page avec Ionic 3 sans se perdre entre les versions ?

Pourquoi la création d’une page n’est pas la même entre Ionic 3 et Ionic 4 ?

Quand on cherche comment créer une page avec Ionic, on tombe vite sur des exemples qui mélangent plusieurs générations du framework. Et c’est souvent là que la confusion commence. Avec Ionic 4 et les versions suivantes, la logique tourne largement autour du routage Angular. Avec Ionic 3, on est encore dans un fonctionnement très lié aux pages, à la navigation par NavController et, selon le projet, au lazy loading. Dit autrement : le nom de la commande peut se ressembler, mais le résultat attendu n’est pas exactement le même.

Si votre projet tourne encore sous Ionic 3, le plus simple est de rester sur une approche pratique. Le but n’est pas de relire toute la doc Angular de l’époque, mais de créer une page proprement, savoir quels fichiers sont générés et comprendre à quel moment il faut encore faire un peu de configuration à la main. Si vous avez besoin d’un rappel plus large sur la structure du framework, vous pouvez aussi jeter un œil à cet article sur Ionic, qui aide à remettre les bases dans le bon ordre.

Quelle commande utiliser pour générer une page Ionic 3 ?

ionic g page Contact
ionic generate page Dashboard

Dans un projet Ionic 3, la commande la plus directe reste :

ionic generate page ma-page

Vous pouvez aussi utiliser la version courte :

ionic g page ma-page

Cette commande crée généralement un dossier dédié dans src/pages/ma-page/ avec plusieurs fichiers :

  • ma-page.html pour le template,
  • ma-page.scss pour le style,
  • ma-page.ts pour la logique de la page,
  • ma-page.module.ts pour le module associé.

C’est un détail important, parce que c’est justement l’un des points qui déroute quand on passe d’un tutoriel Ionic 4 à un projet Ionic 3. Sous Ionic 3, la génération de page s’inscrit dans une logique où chaque page peut embarquer son propre module. Ce n’est pas juste un écran ajouté au hasard dans l’application.

Concrètement, si vous lancez la commande suivante :

ionic g page contact

vous obtenez une page ContactPage prête à être ouverte depuis la navigation. C’est la méthode la plus propre pour éviter les oublis de fichiers ou les erreurs de nommage. Franchement, faire ça à la main n’apporte pas grand-chose, sauf si vous aimez corriger des imports pendant vingt minutes.

Que contient une page générée dans Ionic 3 ?

Une fois la commande exécutée, le fichier TypeScript ressemble en général à une classe simple avec un sélecteur, un template et parfois un constructeur injectant NavController ou NavParams. Le cœur du travail se fait ensuite dans trois zones.

D’abord le HTML. C’est là que vous posez votre structure avec les composants Ionic comme ion-header, ion-navbar, ion-content ou encore les listes et formulaires. Ensuite le SCSS, pratique pour styler uniquement cette page sans tout mélanger dans les styles globaux. Enfin le fichier .ts, où vous gérez les données, les méthodes et les actions utilisateur.

Un exemple très simple de contenu peut ressembler à ça :

<ion-header>
  <ion-navbar>
    <ion-title>Contact</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <p>Ma nouvelle page Ionic 3 fonctionne.</p>
</ion-content>

Ce n’est pas spectaculaire, mais c’est suffisant pour valider que la génération est correcte et que la page s’affiche bien. À ce stade, le bon réflexe consiste à tester tout de suite plutôt que d’enchaîner plusieurs modifications sans vérifier la base.

Faut-il déclarer la page manuellement dans le projet ?

this.navCtrl.push('ContactPage')
// ou selon le projet
this.navCtrl.push(ContactPage)

La vraie réponse, c’est : ça dépend de l’organisation de votre application Ionic 3. Si vous utilisez le lazy loading tel qu’il était courant sur Ionic 3, la page générée avec son fichier ma-page.module.ts peut vivre de manière autonome, et vous l’ouvrirez souvent avec :

this.navCtrl.push('MaPage');

Dans certains projets plus anciens, ou dans des projets qui n’ont pas été structurés autour du lazy loading, il faut encore déclarer la page dans app.module.ts et parfois dans entryComponents. C’est typiquement le genre de détail qui fait perdre du temps, parce qu’on suit un tutoriel correct… mais pour une autre version d’Ionic ou une autre architecture de projet.

Le plus sûr est donc de regarder comment les pages déjà présentes dans votre application sont branchées. Si elles ont chacune leur module et une navigation par nom, gardez la même logique. Si tout est centralisé dans app.module.ts, ne partez pas dans une structure hybride. Mélanger les deux approches dans un vieux projet, c’est souvent le meilleur moyen d’obtenir une page qui compile mal ou qui ne s’ouvre jamais au bon moment.

Comment ouvrir la nouvelle page dans Ionic 3 ?

Créer la page ne suffit pas, il faut aussi l’appeler. Dans Ionic 3, on passe souvent par NavController. Depuis une page existante, on peut par exemple écrire :

this.navCtrl.push(ContactPage);

ou, dans un contexte lazy loaded :

this.navCtrl.push('ContactPage');

Là encore, il faut rester cohérent avec le reste du projet. Si vos autres écrans utilisent une classe importée, copiez ce modèle. Si l’application navigue par chaîne de caractères, faites pareil. Ce n’est pas la partie la plus moderne d’Ionic, mais sur Ionic 3, c’est encore la mécanique normale.

Pour aller un peu plus loin sur les bonnes pratiques autour des pages et de l’organisation d’un projet, vous pouvez aussi lire ce contenu complémentaire. Il complète bien l’approche purement CLI et évite de traiter la génération de page comme une commande isolée sortie de son contexte.

Les erreurs les plus fréquentes quand on crée une page Ionic 3

La première erreur consiste à suivre un tutoriel Ionic 4 ou 5 en pensant que cela fonctionnera pareil. En général, non. Vous vous retrouvez avec des histoires de routing, de dossiers différents ou d’imports qui ne correspondent pas à votre projet.

La deuxième erreur, plus discrète, c’est de générer la page correctement puis de l’intégrer selon une autre logique que celle déjà utilisée dans l’application. Une app Ionic 3 ancienne a souvent son historique, ses choix techniques et parfois ses petites bizarreries. Il vaut mieux respecter cet existant que vouloir moderniser un seul écran au passage.

Enfin, beaucoup de blocages viennent d’un détail banal : un nom de page pas cohérent, un import oublié, ou une navigation écrite dans le mauvais format. Quand une page ne s’ouvre pas, le problème n’est pas toujours la commande ionic g page. Le souci apparaît souvent juste après, au moment où on raccorde la page au reste de l’application.

Pourquoi le mot clé this change-t-il de sens en JavaScript ?

En JavaScript, this fait partie de ces mots clés qui paraissent simples au début, puis deviennent flous dès qu’on change un peu de contexte. On croit souvent que this désigne toujours l’objet dans lequel on se trouve. En réalité, il dépend surtout de la façon dont une fonction est appelée. C’est ce décalage qui crée la plupart des incompréhensions.

Dit autrement, this n’est pas fixé une bonne fois pour toutes au moment où l’on écrit une fonction. Il est résolu au moment de l’exécution. C’est pour ça qu’un même morceau de code peut se comporter différemment selon l’endroit d’où il est invoqué. Si vous avez déjà lu un article sur les fonctions JavaScript, vous pouvez aussi jeter un oeil à cet autre billet sur JavaScript, qui complète bien le sujet.

Pourquoi this perturbe autant de développeurs ?

Le vrai piège, c’est qu’on confond facilement this avec le contexte lexical. Dans beaucoup d’autres situations en JavaScript, ce qui compte est l’endroit où le code est écrit. Pour this, ce n’est pas la bonne intuition. Ce qui compte, c’est l’appel.

Prenons un cas simple :

const utilisateur = {
  nom: 'Yvon',
  afficherNom() {
    console.log(this.nom);
  }
};

utilisateur.afficherNom(); // 'Yvon'

Ici, tout va bien. La méthode est appelée via utilisateur.afficherNom(), donc this pointe sur utilisateur. Beaucoup de développeurs s’arrêtent à cette première lecture et pensent que this référence toujours l’objet qui contient la méthode. Le problème arrive dès qu’on détache la fonction :

const fn = utilisateur.afficherNom;
fn(); // undefined dans la plupart des cas modernes

Cette fois, la fonction n’est plus appelée comme méthode d’un objet. Elle est appelée seule. Le lien avec utilisateur a disparu, et this ne vaut plus ce qu’on imaginait.

Ce que this représente vraiment

const personne = {
  nom: "Yvon",
  parler() {
    console.log(this.nom)
  }
}

Pour bien comprendre, il faut retenir une règle simple : this dépend de la forme d’appel.

  • Appel simple : fn() donne un this global ou undefined selon le contexte.
  • Appel comme méthode : objet.fn() fait pointer this vers objet.
  • Appel forcé : call, apply et bind permettent de choisir la valeur de this.
  • Fonction fléchée : elle ne crée pas son propre this, elle récupère celui du contexte englobant.

Cette distinction change tout. Une fonction classique peut donc être très pratique quand on veut un this dynamique, mais elle peut aussi devenir source de bugs dans un callback, un timer ou un gestionnaire d’événement mal attaché.

Si vous aimez revoir les bases avec des exemples progressifs, cet article lié peut aussi aider à remettre les concepts dans l’ordre avant de revenir sur this.

L’ancienne technique self ou that

Avant l’arrivée massive des fonctions fléchées, on utilisait souvent une astuce très répandue : stocker this dans une variable comme self ou that. L’idée était simple : capturer la bonne référence avant d’entrer dans une fonction interne.

const compteur = {
  valeur: 0,
  incrementer() {
    const self = this;

    setTimeout(function () {
      self.valeur++;
      console.log(self.valeur);
    }, 1000);
  }
};

Cette technique a longtemps été utile, surtout quand une fonction interne perdait le bon contexte. Elle marche encore, et il est bon de la connaître parce qu’on la rencontre dans beaucoup d’anciens projets. Mais aujourd’hui, elle sert surtout à comprendre l’histoire du langage et à lire du code existant.

La version moderne avec les fonctions fléchées

const objet = {
  nom: "Yvon",
  init() {
    const self = this
    setTimeout(function () {
      console.log(self.nom)
    }, 100)
  }
}

Dans la plupart des cas modernes, une fonction fléchée rend l’intention plus claire :

const compteur = {
  valeur: 0,
  incrementer() {
    setTimeout(() => {
      this.valeur++;
      console.log(this.valeur);
    }, 1000);
  }
};

Ici, la fonction fléchée ne redéfinit pas this. Elle reprend celui de incrementer(). Résultat : pas besoin de self, pas besoin de bricoler, et le code se lit plus facilement.

Attention quand même : cela ne veut pas dire que les fonctions fléchées sont toujours meilleures. Si vous définissez une méthode d’objet directement en fléché, vous pouvez obtenir l’effet inverse de celui recherché, car la fonction fléchée ne recevra pas le this de l’objet au moment de l’appel.

const utilisateur = {
  nom: 'Yvon',
  afficherNom: () => {
    console.log(this.nom);
  }
};

Dans ce cas, this ne représente pas utilisateur. C’est une erreur fréquente chez les débutants.

Un réflexe utile pour éviter les erreurs

Quand vous voyez this, ne demandez pas d’abord « dans quel objet suis-je ? » Demandez plutôt : « comment cette fonction est-elle appelée ? » Ce réflexe évite beaucoup de confusion. Il aide aussi à comprendre pourquoi deux fonctions qui se ressemblent peuvent produire des résultats différents.

En pratique, retenez trois idées. D’abord, this change de contexte selon l’appel. Ensuite, la technique self ou that reste utile pour lire du vieux code. Enfin, les fonctions fléchées simplifient souvent la situation, sans régler tous les cas à votre place.

Une fois cette logique intégrée, les comportements de this paraissent beaucoup moins arbitraires. On commence à voir qu’il ne s’agit pas d’un mot clé capricieux, mais d’une règle d’appel qu’il faut apprendre à reconnaître dans le code courant.

Quelle différence entre une fonction avec et sans parenthèse ?

En JavaScript, une petite paire de parenthèses peut tout changer. Quand on écrit maFonction, on parle de la fonction elle-même. Quand on écrit maFonction(), on demande au moteur JavaScript de l’exécuter tout de suite. Cette différence paraît minime au début, mais elle revient partout : dans les événements, les callbacks, setTimeout, map, ou encore dans React.

C’est souvent là que les bugs commencent. On pense “je donne une fonction”, alors qu’en réalité on l’appelle immédiatement. Ou l’inverse : on pense qu’un morceau de code va s’exécuter, alors qu’on a juste passé une référence. Si vous avez déjà hésité devant un onclick, un addEventListener ou un then, c’est exactement le point à comprendre.

Référencer une fonction, c’est passer la fonction elle-même

function direBonjour() {
  console.log("Bonjour")
}

const action = direBonjour
const resultat = direBonjour()

Quand vous écrivez seulement le nom d’une fonction, sans parenthèses, vous passez une référence vers cette fonction.

function direBonjour() {
  console.log("Bonjour");
}

const action = direBonjour;

Ici, action ne contient pas le résultat de direBonjour. Elle contient la fonction. Autrement dit, on stocke “quoi exécuter plus tard”. La fonction n’a pas encore tourné.

C’est très utile quand une autre partie du code doit décider du moment où elle appellera cette fonction. Par exemple, un bouton peut attendre un clic, ou un minuteur peut attendre quelques secondes avant de déclencher l’action.

On retrouve cette logique dans beaucoup d’exemples plus larges autour des fonctions JavaScript, notamment quand on manipule des callbacks ou des fonctions anonymes. Si vous voulez creuser cette famille de notions, jetez un œil à cet article sur les fonctions en JavaScript.

Appeler une fonction, c’est exécuter son code immédiatement

Dès que vous ajoutez des parenthèses, vous appelez la fonction.

function direBonjour() {
  console.log("Bonjour");
}

direBonjour();

Là, JavaScript entre dans la fonction et exécute ce qu’elle contient. Si elle renvoie une valeur avec return, cette valeur remplace l’appel.

function addition(a, b) {
  return a + b;
}

const resultat = addition(2, 3);

Dans cet exemple, resultat vaut 5. Ce n’est pas la fonction qui est stockée, c’est le résultat de son exécution.

C’est vraiment la distinction la plus simple à garder en tête :

  • maFonction : la fonction
  • maFonction() : le résultat de la fonction

Pourquoi cette différence pose autant de problèmes en pratique ?

button.addEventListener("click", direBonjour)
setTimeout(direBonjour, 1000)

Parce que beaucoup d’API JavaScript attendent une fonction à appeler plus tard, pas un résultat calculé tout de suite.

Prenons un cas classique :

button.addEventListener("click", direBonjour);

Ici, c’est correct. On donne à addEventListener une fonction à lancer au moment du clic.

Maintenant, regardez cette version :

button.addEventListener("click", direBonjour());

Cette fois, direBonjour() s’exécute immédiatement, au moment où la ligne est lue. Le plus souvent, cela affiche déjà “Bonjour”, puis passe à addEventListener non pas une fonction, mais la valeur renvoyée par direBonjour. Et si cette valeur est undefined, l’événement ne fonctionnera pas comme prévu.

Le piège est le même avec setTimeout :

setTimeout(direBonjour, 1000);   // bon
setTimeout(direBonjour(), 1000); // mauvais dans ce cas

Dans la première ligne, la fonction sera appelée dans une seconde. Dans la seconde, elle part tout de suite.

Que faire si vous devez passer des arguments ?

C’est souvent là qu’on comprend pourquoi la nuance existe. Si vous avez besoin d’arguments, vous ne pouvez pas écrire directement la référence simple direBonjour si la fonction doit être appelée avec une valeur précise plus tard.

function saluer(prenom) {
  console.log(`Bonjour ${prenom}`);
}

Si vous écrivez :

button.addEventListener("click", saluer("Yvon"));

vous exécutez saluer tout de suite. Pour retarder l’appel, il faut envelopper l’exécution dans une autre fonction :

button.addEventListener("click", function () {
  saluer("Yvon");
});

ou, plus souvent aujourd’hui :

button.addEventListener("click", () => saluer("Yvon"));

Cette fonction fléchée sert ici d’intermédiaire. On passe bien une fonction au gestionnaire d’événement, et cette fonction appellera saluer("Yvon") au bon moment.

Une image mentale simple pour ne plus se tromper

Vous pouvez voir une fonction référencée comme une télécommande posée sur la table. Elle est disponible, mais rien ne se passe tant que personne n’appuie sur le bouton. Une fonction appelée, c’est le moment où on appuie réellement dessus.

Cette image marche bien avec les tableaux aussi :

const nombres = [1, 2, 3];
const doubles = nombres.map(x => x * 2);

map attend une fonction. Il va l’utiliser pour chaque élément. Si vous débutez avec ce style de code, cela rejoint la différence entre déclaration, passage et exécution d’une fonction, un sujet proche de cet autre article consacré au JavaScript.

Retenez surtout ceci : sans parenthèses, vous transmettez un outil ; avec parenthèses, vous l’utilisez. Dès que vous repérez une API qui dit “donnez-moi une fonction”, la bonne question à se poser est simple : est-ce que je veux exécuter cette fonction maintenant, ou est-ce que je veux laisser quelqu’un d’autre l’exécuter plus tard ?

Dans la vraie vie, c’est presque toujours cette question qui fait la différence entre un code qui réagit au bon moment et un code qui part trop tôt.

Comment utiliser Greasemonkey pour automatiser votre navigateur ?

Pourquoi Greasemonkey reste utile pour automatiser des tâches simples ?

// ==UserScript==
// @name         Auto clic exemple
// @match        https://example.com/*
// ==/UserScript==

Quand on parle d’automatisation du navigateur, on pense souvent à des outils lourds, à des extensions complexes ou à des scripts de test destinés aux développeurs. Pourtant, pour beaucoup de besoins du quotidien, Greasemonkey reste une solution étonnamment accessible. Le principe est simple : vous ajoutez un script utilisateur à votre navigateur, et ce script modifie le comportement d’une page web une fois qu’elle est chargée.

C’est précisément là que l’idée de window.onload garde tout son intérêt. Cette fonction permet d’attendre que la page soit entièrement chargée avant d’exécuter une action : remplir un champ, cliquer sur un bouton, masquer un bloc gênant ou réorganiser l’affichage. Dit autrement, vous ne changez pas le site à la source : vous adaptez ce que votre navigateur fait, pour vous faire gagner du temps.

Cette approche est idéale pour automatiser des actions répétitives. Si vous visitez souvent une même interface d’administration, un intranet, un tableau de bord ou un service web dont l’ergonomie vous ralentit, Greasemonkey peut devenir votre petit assistant discret. C’est moins spectaculaire qu’un robot complet, mais souvent plus rapide à mettre en place.

Comment fonctionne un script utilisateur avec Greasemonkey ?

window.onload = function () {
  const bouton = document.querySelector("button")
  if (bouton) bouton.click()
}

Un script utilisateur est un petit fichier JavaScript exécuté seulement sur les pages que vous avez choisies. Vous pouvez donc cibler un site précis, voire une seule URL. Une fois Greasemonkey installé, vous créez ou importez un script, puis vous définissez son périmètre d’action. Ensuite, à chaque chargement de la page, votre code s’exécute automatiquement.

Un exemple très simple consiste à attendre la fin du chargement :

window.onload = function () {
  const champ = document.querySelector('#recherche');
  if (champ) {
    champ.value = 'texte prérempli';
  }
};

Ce modèle reste intéressant parce qu’il est facile à comprendre. La page se charge, puis votre script prend la main. Pour quelqu’un qui veut découvrir l’automatisation du navigateur sans entrer tout de suite dans des frameworks spécialisés, c’est une excellente porte d’entrée.

Dans la pratique, on peut aller un peu plus loin : détecter un bouton, injecter un lien, corriger une mise en page, ou automatiser une séquence courte. Si vous vous intéressez déjà aux personnalisations du navigateur, vous pouvez aussi parcourir cet autre article sur les usages pratiques côté web, qui complète bien cette logique d’adaptation fine de l’interface.

Quels usages concrets peut-on automatiser sans complexifier son navigateur ?

La force de Greasemonkey, ce n’est pas de remplacer une suite d’automatisation industrielle. Sa vraie valeur apparaît dans les micro-gains du quotidien. Par exemple, vous pouvez :

  • préremplir automatiquement certains champs d’un formulaire ;
  • mettre en surbrillance des informations importantes sur une page dense ;
  • masquer des encarts répétitifs ou des blocs inutiles ;
  • ajouter un raccourci vers une action souvent utilisée ;
  • déclencher une petite routine au chargement de la page.

Sur un outil interne d’entreprise, cela peut faire gagner quelques secondes à chaque visite, ce qui finit par représenter un vrai confort. Sur un site personnel ou un back-office, cela permet aussi de compenser des limites ergonomiques sans attendre qu’une mise à jour officielle arrive.

Il faut simplement garder à l’esprit qu’un script utilisateur dépend de la structure HTML de la page. Si le site change de design ou renomme ses éléments, le script peut cesser de fonctionner. C’est le prix de la souplesse : on automatise vite, mais il faut parfois ajuster le script après une refonte.

Pourquoi l’approche window.onload reste pédagogique et efficace ?

On pourrait objecter qu’il existe aujourd’hui des méthodes plus fines que window.onload, notamment avec des écouteurs d’événements plus ciblés ou l’observation dynamique du DOM. C’est vrai. Mais pour un article d’initiation, et pour beaucoup de cas simples, window.onload garde deux qualités importantes : la lisibilité et la robustesse de départ.

Quand on débute, il est rassurant de savoir que le script s’exécutera une fois la page chargée. Cela évite bien des erreurs liées à des éléments encore absents du document. C’est aussi une bonne manière de structurer sa logique : d’abord on attend, ensuite on repère les éléments utiles, puis on applique l’automatisation.

Cette simplicité n’empêche pas d’évoluer ensuite vers des scripts plus raffinés. Greasemonkey peut donc servir de terrain d’apprentissage très concret. On écrit quelques lignes, on recharge la page, on observe immédiatement le résultat. Pour progresser sur ce type de personnalisation du navigateur, vous pouvez également lire ce billet connexe sur les extensions et les ajustements du web au quotidien, qui s’inscrit dans le même esprit pratique.

Que faut-il surveiller avant d’automatiser un site avec Greasemonkey ?

Automatiser son navigateur est pratique, mais cela demande un minimum de méthode. D’abord, mieux vaut tester sur un site que vous utilisez souvent et dont vous comprenez la structure générale. Inutile de viser trop ambitieux au départ. Un petit script qui ajoute un bouton ou remplit un champ est largement suffisant pour commencer.

Ensuite, il faut faire attention à la sécurité. Un script utilisateur a le pouvoir de modifier ce que vous voyez et ce que vous envoyez sur une page. Il est donc préférable de n’installer que des scripts dont vous comprenez l’origine, et si possible le fonctionnement. Lorsque vous écrivez le vôtre, gardez le code simple, commenté et limité à quelques actions précises.

Enfin, il faut accepter qu’un script utilisateur vive au rythme du site qu’il modifie. Si la page évolue, votre automatisation devra peut-être être revue. Ce n’est pas forcément un défaut : c’est aussi ce qui fait la souplesse de Greasemonkey. Vous pouvez ajuster votre outil au fur et à mesure, sans dépendre d’un logiciel externe ni d’une infrastructure compliquée.

Par où commencer si l’on veut un navigateur un peu plus intelligent ?

Le plus simple est de repérer une tâche répétitive, pénible, mais très localisée. Par exemple : cliquer toujours sur la même option, corriger automatiquement un affichage, ou remplir un formulaire avec une valeur par défaut. À partir de là, un script Greasemonkey basé sur l’idée window.onload suffit souvent pour obtenir un premier résultat concret.

Cette logique a un avantage appréciable : elle reste compréhensible. On ne cherche pas à transformer le navigateur en robot universel. On lui apprend juste à effectuer, au bon moment, une petite action utile sur une page précise. C’est souvent exactement ce qu’il faut pour rendre la navigation plus fluide, plus personnelle et un peu moins répétitive au quotidien.

Pourquoi un Set JavaScript ne fait-il pas de deep compare sur les objets ?

Quand on découvre Set en JavaScript, on imagine souvent un comportement simple : si deux valeurs se ressemblent, le set n’en garde qu’une. C’est vrai pour les primitives les plus évidentes, mais ça casse très vite avec les objets. Deux objets qui ont exactement les mêmes propriétés, dans le même ordre, avec les mêmes valeurs, sont quand même considérés comme différents. C’est là que commence la vraie question : comment obtenir une unicité logique quand Set ne compare pas le contenu profond des objets ?

Le sujet peut paraître anecdotique, pourtant il revient partout : déduplication de résultats d’API, fusion de filtres, mise en cache, construction de listes uniques côté front. Si vous êtes tombé sur ce comportement en manipulant des collections, vous pouvez aussi jeter un oeil à cet article lié pour rester dans la même famille de problèmes JavaScript.

Pourquoi deux objets identiques restent différents dans un Set ?

Set n’utilise pas un deep compare. Il utilise une logique d’identité de référence pour les objets. En clair, JavaScript ne se demande pas : « est-ce que ces deux objets ont le même contenu ? » Il se demande : « est-ce que c’est exactement le même objet en mémoire ? »

const a = { id: 1, name: 'Ada' };
const b = { id: 1, name: 'Ada' };

const set = new Set([a, b]);
console.log(set.size); // 2

Le résultat surprend souvent, mais il est cohérent avec le fonctionnement du langage. a et b se ressemblent, oui, mais ce sont deux références distinctes. À l’inverse, si vous ajoutez deux fois la même variable, Set n’en gardera qu’une.

const user = { id: 1, name: 'Ada' };
const set = new Set([user, user]);
console.log(set.size); // 1

Autrement dit, Set garantit l’unicité des références, pas l’unicité métier. C’est une nuance importante. Si votre règle d’unicité dépend d’un id, d’une combinaison de champs, ou d’une structure complète, Set seul ne suffit pas.

Pourquoi JavaScript ne fait pas ce deep compare automatiquement ?

const a = { id: 1 }
const b = { id: 1 }
const set = new Set([a, b])
console.log(set.size) // 2

Parce que ce serait coûteux, ambigu, et parfois même contre-productif. Comparer profondément deux objets n’est pas une opération gratuite. Il faut parcourir les propriétés, gérer les objets imbriqués, les tableaux, les dates, parfois les cycles. Et surtout, il faut décider ce qui compte comme « égal ». Est-ce que l’ordre des clés compte ? Est-ce que undefined doit être traité comme une absence de propriété ? Est-ce qu’une date et sa version sérialisée doivent être vues comme équivalentes ?

Le langage préfère donc une règle simple et prévisible : pour les objets, l’égalité dans un Set repose sur la référence. C’est brut, mais c’est stable.

Quelles options pour simuler un deep compare dans un Set ?

En pratique, il y a trois approches courantes.

La première consiste à dédupliquer sur une clé métier. Si chaque objet possède un identifiant fiable, c’est souvent la meilleure solution.

const users = [
  { id: 1, name: 'Ada' },
  { id: 1, name: 'Ada' },
  { id: 2, name: 'Linus' }
];

const unique = [...new Map(users.map(user => [user.id, user])).values()];

Ici, on ne compare pas tout l’objet. On décide que id définit l’unicité. C’est souvent plus clair qu’un deep compare complet.

La deuxième approche consiste à sérialiser les objets avant de les passer dans un Set.

const unique = [...new Set(users.map(user => JSON.stringify(user)))].map(str => JSON.parse(str));

Ça marche pour des cas simples, mais il faut rester prudent. JSON.stringify a des limites : les fonctions disparaissent, undefined est ignoré dans certains cas, les dates deviennent des chaînes, et l’ordre des clés peut vous piéger si les objets ne sont pas normalisés. C’est pratique pour un script rapide, moins pour une logique sensible.

La troisième approche, plus propre quand les cas deviennent sérieux, consiste à écrire une fonction de comparaison ou à utiliser une librairie spécialisée. Vous parcourez alors la liste manuellement et vous n’ajoutez un objet que s’il n’existe pas déjà selon votre propre définition de l’égalité.

const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);

const unique = [];
for (const item of users) {
  if (!unique.some(existing => deepEqual(existing, item))) {
    unique.push(item);
  }
}

Ce n’est pas un vrai Set au sens strict, mais c’est souvent la bonne réponse au besoin réel.

Le piège du faux deep compare

const unique = new Map()
for (const item of items) {
  unique.set(JSON.stringify(item), item)
}
const result = [...unique.values()]

Beaucoup de développeurs disent « je vais faire un deep compare » alors qu’ils font juste une comparaison JSON. Ce n’est pas toujours équivalent. Pour des objets plats issus d’une API bien propre, ça peut suffire. Pour des données plus riches, non. Si votre logique d’unicité a de l’importance, mieux vaut définir explicitement la règle : unicité par id, par couple type + value, ou par structure normalisée.

C’est aussi pour ça que le vrai sujet n’est pas seulement Set. Le vrai sujet, c’est : qu’est-ce qu’un doublon dans votre domaine ? La réponse technique découle de cette réponse métier. Sur un thème proche, cet autre article peut prolonger la réflexion si vous creusez les comportements parfois contre-intuitifs du langage.

Quand garder Set, et quand changer d’approche ?

Gardez Set si vous manipulez surtout des primitives, ou si vous voulez éliminer des doublons de référence. C’est rapide, lisible, natif. En revanche, dès que vous avez des objets recréés à la volée, reçus de plusieurs sources, ou reconstruits à chaque rendu, il faut sortir du réflexe new Set(array).

Dans ces cas-là, un Map indexé par clé métier est souvent plus solide. Une fonction de déduplication maison peut aussi être plus honnête qu’un pseudo deep compare caché dans un coin. Le point important, c’est d’assumer la règle plutôt que d’espérer que Set lise dans vos intentions.

Si on résume sans faire de grande morale : Set ne sait pas reconnaître deux objets « égaux » au sens humain du terme. Il reconnaît seulement deux références identiques. Dès qu’on a compris ça, on arrête de lutter contre le langage et on choisit une stratégie de déduplication adaptée au vrai besoin.

javascript

Node.js, Express et MongoDB : une meilleure alternative pour créer une application web ?

Pendant longtemps, le trio Apache, Linux, MySQL et PHP a été la porte d’entrée presque évidente pour créer un site dynamique. C’était simple à héberger, bien documenté et assez robuste pour faire tourner des milliers de projets. Pourtant, dès qu’on veut une application plus interactive, avec beaucoup d’échanges en temps réel entre le navigateur et le serveur, on regarde souvent ailleurs. C’est là que Node.js, Express et MongoDB entrent en scène. Non pas comme une mode de plus, mais comme une vraie alternative pour celles et ceux qui veulent construire une application web moderne sans changer de langage entre le front et le back.

Pourquoi quitter le confort du couple PHP MySQL ?

Le stack Apache/Linux/MySQL/PHP a encore de solides arguments. Il reste fiable, mature et parfaitement adapté à beaucoup de sites vitrines, de blogs ou d’applications métiers classiques. Le problème n’est donc pas que cette pile soit dépassée. Le problème, c’est qu’elle n’est pas toujours la plus agréable quand le projet demande une interface très vivante, des mises à jour fréquentes côté client, une API JSON propre et une montée en charge plus souple.

Avec Node.js, le JavaScript ne s’arrête plus au navigateur. Il passe aussi côté serveur. Pour un développeur web, c’est un changement assez confortable. On manipule le même langage pour valider un formulaire, appeler une API, traiter une requête côté serveur ou transformer des données avant affichage. Ce n’est pas magique, mais c’est pratique, et dans une petite équipe ça évite pas mal de frictions.

Si vous avez déjà un site construit sur des bases plus classiques, vous pouvez d’ailleurs voir cette transition comme une évolution naturelle, pas comme une rupture totale. Dans le même esprit, cet article sur la création d’un site web pose bien les bases de ce qu’on attend d’un projet propre dès le départ.

Node.js pour le moteur, Express pour la route

npm init -y
npm install express mongoose
node server.js

Node.js sert à exécuter du JavaScript sur le serveur. Sa force, c’est son modèle non bloquant, très à l’aise quand une application doit gérer beaucoup de requêtes courtes, des appels API, des flux de données ou des connexions simultanées. Là où un développement plus classique peut donner une impression de lourdeur à mesure que l’application grossit, Node garde souvent une sensation de fluidité, surtout pour les projets centrés sur l’échange de données.

Express vient simplifier tout ça. C’est un framework minimaliste, assez souple pour ne pas vous noyer sous les conventions, mais suffisamment structuré pour éviter de coder n’importe comment. Avec quelques routes, un peu de middleware et une organisation claire des contrôleurs, on obtient rapidement une base propre pour une application web ou une API REST.

Un exemple très simple suffit à comprendre pourquoi ce duo plaît autant. En quelques lignes, on peut démarrer un serveur, définir une route, récupérer une requête et renvoyer une réponse JSON. Là où certains environnements demandent plus de configuration au départ, Express donne vite quelque chose de concret. Pour un prototype, un back office ou un service connecté à une application front en React ou Vue, c’est franchement agréable.

MongoDB change la manière de penser les données

const express = require("express")
const app = express()
app.get("/", (req, res) => res.send("Bonjour"))
app.listen(3000)

Le contraste avec MySQL se voit surtout ici. MySQL repose sur un modèle relationnel très solide, avec des tables, des jointures et un schéma bien cadré. C’est excellent quand les relations entre les données doivent rester strictes et parfaitement normalisées. MongoDB, lui, fonctionne avec des documents JSON-like. On stocke des objets proches de ce que l’application manipule déjà dans le code.

Ce choix plaît beaucoup dans les applications web modernes parce qu’il réduit la sensation de traduction permanente entre la base de données et le code métier. Un utilisateur, un panier, un article ou un commentaire peuvent être représentés sous une forme très proche de celle utilisée dans l’application. Résultat, on avance souvent plus vite sur les premières versions.

Attention, cela ne veut pas dire que MongoDB remplace MySQL dans tous les cas. Si votre projet repose sur des relations complexes, des contraintes fortes et des requêtes analytiques très précises, une base relationnelle reste parfois plus logique. En revanche, pour une application web orientée API, avec des structures qui évoluent vite, MongoDB apporte une souplesse vraiment appréciable.

Le trio Node Express MongoDB fonctionne bien ensemble

Ce qui rend cette stack intéressante, ce n’est pas seulement la popularité de chaque outil pris séparément. C’est la cohérence d’ensemble. Le navigateur envoie des données en JSON, Express les reçoit facilement, Node les traite sans changer d’univers mental, puis MongoDB les stocke dans un format proche. On perd moins de temps à faire correspondre des couches qui parlent des dialectes différents.

Pour une équipe qui construit une application web aujourd’hui, ce confort compte. Il permet de lancer plus vite un MVP, de brancher une authentification, de créer une API pour mobile, ou d’ajouter plus tard des fonctions en temps réel comme un chat, des notifications ou un tableau de bord mis à jour en direct. Ce n’est pas un hasard si cette pile revient souvent dès qu’on parle de produits web qui doivent bouger vite.

Si le sujet vous intéresse, vous pouvez aussi prolonger la lecture avec ce billet sur les outils et méthodes pour créer un site internet. Il complète bien la réflexion, surtout si vous hésitez encore entre une approche traditionnelle et une stack plus orientée application.

Pour quel type de projet cette alternative a du sens ?

Node.js, Express et MongoDB conviennent bien aux applications où l’expérience utilisateur repose sur la réactivité. Un espace membre, une plateforme de réservation, un petit SaaS, un tableau de bord métier, une messagerie interne ou une application avec API publique sont de bons candidats. La stack marche aussi très bien pour lancer une première version rapidement, tester une idée puis faire évoluer l’architecture ensuite.

En réalité, l’intérêt du trio ne tient pas à une opposition caricaturale avec Apache/Linux/MySQL/PHP. L’ancienne pile reste très bonne dans beaucoup de contextes. Mais si vous cherchez une alternative plus fluide pour développer une application web moderne, plus centrée sur les échanges de données, le JavaScript partout et la rapidité d’itération, Node.js, Express et MongoDB forment un ensemble cohérent, simple à prendre en main et assez agréable à faire évoluer au quotidien.

Connaitre la différence entre NodeList et HTMLCollection

Lorsque vous sélectionnez plusieurs éléments HTML en se basant sur le nom de la balise ou d’un nom de classe, vous recevez plusieurs éléments. Par exemple on a le code ci-dessous :

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

let liste = document.querySelectorAll('li')

let liste2 = document.getElementsByTagName('li')

A priori liste 1 et liste2 sont pareil, mais si nous loggons chaque variable on a HTMLCollection pour liste2 et NodeList pour liste. Quelle est la différence entre les deux?

NodeList est plus général que HTMLCollection

En effet un node peut désigner un élément HTML certes, mais peut aussi désigner un commentaire HTML qui est on le sait pas un élément HTML. Le texte à l’intérieur d’un élément HTML est un node, mais pas un élément HTML

<div>
Je suis un texte
    <p>Une phrase</p>
    <p>une seconde phrase</p>
</div>

Dans l’exemple ci-dessus « Je suis un texte » n’est pas un élément HTML mais un Node.

Un HTMLCollection ou un Nodelist n’est pas un tableau mais un Iterable !

Vous connaissez sans doute la méthode map relative aux tableaux, si vous essayer d’appliquer map à une Nodelist ou un HTMLCollection, vous aurez une erreur.

liste.map( e=> e ) // erreur
for(let i=0;i< liste.length;i++){
    console.log(liste[i])
}

ça marchera aussi pour liste2.

Les index et la propriété length marchent mais c’est tout, ce n’est donc pas un tableau ! On peut boucler dessu, donc c’est un Itérable. Un tableau est un Iterable mais avec plus de propriété.

On peut cependant transformer ces Iterable en tableau avec la fonction Array.from.

HTMLCollection reflète le changement dans le DOM

Soit le HTML:

<p>Paragraph Un</p>
<p>Paragraph Deux</p>
<p>Paragraph Trois</p>


// returns an HTMLCollection
const paragraphs = document.getElementsByTagName('p')

console.log("BEFORE UPDATE: ", paragraphs)   // 3 paragraphes
  
const newParagraph = document.createElement('p')
document.body.appendChild(newParagraph)

console.log("AFTER UPDATE: ", paragraphs) // 4 paragraphes

NodeList quant à lui est généralement statique

Soit le HTML:

<p>Paragraph Un</p>
<p>Paragraph Deux</p>
<p>Paragraph Trois</p>


// returns an HTMLCollection
const paragraphs = document.querySelectorAll('p')

console.log("BEFORE UPDATE: ", paragraphs)   // 3 paragraphes
  
const newParagraph = document.createElement('p')
document.body.appendChild(newParagraph)

console.log("AFTER UPDATE: ", paragraphs) // 3 paragraphes

Exception avec getElementsByName

<body>
    <p name="toto">Paragraph Un</p>
    <p name="toto">Paragraph Deux</p>
    <p name="toto">Paragraph Trois</p>
</body>

    // returns an HTMLCollection
    const paragraphs = document.getElementsByName('toto')

    console.log("BEFORE UPDATE: ", paragraphs)   // 3 paragraphes

    const newParagraph = document.createElement('p')
    newParagraph.setAttribute('name', 'toto')
    document.body.appendChild(newParagraph)

    console.log("AFTER UPDATE: ", paragraphs) // 4 paragraphes

Connaitre l’objet global Javascript Object et ses propriétés

L’objet de tous les objets

En Javascript vous avez dû sans doute croiser Object, c’est un peu l’origine de tous les objets e, Javascript.

const obj = new Object()
console.log(obj)
{}
[[Prototype]]:Object

Si on creuse un peu plus l’objet :

Vous voyez toutes les propriétés nombreuse de cet object même primitif. Tous les objet en Javascript héritent de cet objet (Date, String etc).

Méthodes statique d Object

Les méthodes static peuvent peuvent être invoquées depuis Object sans instanciation

Object.keys({a:1,b:2})  // ['a','b']

Object.assign(target,…sources)

Copie des prpriétés d’un objet à un autre

const user = { name: "John" };
const details = { age: 30, country: "France" };

const merged = Object.assign({}, user, details);
console.log(merged);
// { name: 'John', age: 30, country: 'France' }

Object.create(proto,[propertiesObject])

Crée un nouvel objet avec en paramètre un prototype

const person = { greet() { console.log("Hello"); } };
const user = Object.create(person);
user.greet(); // Hello

Object.keys(obj)

Retourne un tableau de clé de l’objet

Object.keys({a:1, b:2}); // ['a', 'b']

Object.values(obj)

Retourne un tableau de valeurs de l’objet

Object.values({a:1, b:2}); // [1, 2]

Object.entries(obj)

Retourne un tableau de clé / valeur

Object.entries({a:1, b:2});
// [['a',1], ['b',2]]
// avec destructuring
for (const [key, val] of Object.entries({a:1, b:2})) {
  console.log(key, val);
}

Object.freeze(obj)

Après un freeze, on ne peut plus ajouter,effacer ou modifier une propriété

const config = { debug: true };
Object.freeze(config);
config.debug = false; // inopérant

Intéressant pour un objet configuration que vous voulez rendre immuable

Object.seal(obj)

Empêche l’ajout ou suppression de propriété, mais autorise la modification

const user = { name: "John" };
Object.seal(user);
user.name = "Jane";  // Ok
user.age = 30;       //  ignoré

Object.getOwnPropertyNames(obj)

Retourne tous les noms des propriétés (énumérable et non énumérables)

const obj = Object.create({}, { hidden: { value: 42, enumerable: false } });
console.log(Object.getOwnPropertyNames(obj)); // ['hidden']

Ne donne que les nom de niveau 1

Object.getOwnPropertyDescriptors(obj)

Retourne les informations de chaque propriété

const obj = { a: 1 };
console.log(Object.getOwnPropertyDescriptors(obj));

Utile pour le clonage incluant getter et setters

Object.getPrototypeOf(obj)

Retourne le prototype (en gros le parent, ou parent du parent etc) d’un objet donné

const arr = [];
console.log(Object.getPrototypeOf(arr) === Array.prototype); // true

Object.setPrototypeOf(obj,prototype)

Sette le prototype à un objet

const animal = { speak() { console.log("hi"); } };
const dog = {};
Object.setPrototypeOf(dog, animal);
dog.speak(); // hi

C’est comme ça qu’on fait de l’orienté objet en Javascript et pas avec des classes, qui sont plus familières à la plupart des programmeurs.

Object.hasOwn(obj,prop)

(Ecmascript 2022 ) alternative plus safe pour hasOwnProperty

const user = { name: "Alice" };
console.log(Object.hasOwn(user, "name")); // true

Object.fromEntries(entries)

Convertit un tableau de [key,value] en un objet

const entries = [["name", "Bob"], ["age", 25]];
const user = Object.fromEntries(entries);
console.log(user); // { name: 'Bob', age: 25 }

Object.is(value1,value2)

Dit si deux objets sont les même (comme ===, mais dans le cas de NaN est plus safe

Object.is(NaN, NaN); // true
Object.is(0, -0);    // false

Object.defineProperty(obj,prop,descriptor)

Définit ou modifie une propriété avec descripteur.

const user = {};
Object.defineProperty(user, "name", {
  value: "Alice",
  writable: false,
  enumerable: true
});

Object.defineProperties(obj,props)

Définit de multiple propriétés

Méthodes d’instance

Les méthodes d’instance ne sont invocable que sur un objet instantié.

hasOwnProperty(prop)

Vérifie si la propriété existe directement dans l’objet.

const user = { name: "Alice" };
console.log(user.hasOwnProperty("name")); // true

isPrototypeOf(obj)

Vérifie si l’objet existe dans la chaine d eprototype d’un autre objet

function Animal() {}
function Dog() {}

Dog.prototype = Object.create(Animal.prototype);

const d = new Dog();
console.log(Animal.prototype.isPrototypeOf(d)); // true

propertyIsEnumerable(prop)

regarde si la propriété est énumérable

const obj = { x: 1 };
console.log(obj.propertyIsEnumerable("x")); // true

toString()

Retourne une représentation string de l’objet

console.log({}.toString()); // [object Object]

Utiliser Object.prototype.toString.call(value) pour détecter le type:

Object.prototype.toString.call([]);      // [object Array]
Object.prototype.toString.call(123);     // [object Number]

valueOf()

retourne la valeur primitive d’un objet (dans un contexte arithmétique)

const obj = { valueOf: () => 42 };
console.log(obj + 8); // 50

Applications pratiques de quelques méthodes

Clone an Object (shallow)

const copy = Object.assign({}, original);

Deep Clone (simple version)

const deepCopy = JSON.parse(JSON.stringify(original));

Merge Configurations

const defaultConfig = { debug: false, port: 8080 };
const userConfig = { debug: true };
const config = Object.assign({}, defaultConfig, userConfig);

Create Object from Key/Value Pairs

const pairs = [["id", 1], ["name", "John"]];
const obj = Object.fromEntries(pairs);

Immutable Constant Object

const roles = Object.freeze({ ADMIN: "admin", USER: "user" });

Le requêtes cross domaine faciles avec le formation JSONP (JSON with Padding)

Si vous faites des applications Javascript modernes, et requêtez avec la fonction fetch(), vous êtes sans doute confronté à des problèmes de CORS (Cross Origin Request Forgery), en clair vous faites une requête AJAX depuis une nom de domaine différent du nom de domaine du webservice.

Le CORS est une feature et pas un bug : Par exemple, avec le site OpenWeatherMap.org, vous pouvez requêter des données météo depuis votre ordinateur local, qui a un nom de host forcément différent de OpenWeatherMap.org. On fait du CORS. Mais parfois c’est un peu délicat, notamment beaucoup n’ont pas les connaissances théoriques sous jacentes. ça peut devenir compliqué, surtout avec le requêtes PREFLIGHT qui consistent à envoyer un pré-requête pour « tester » la température de l’eau.

Mais le JSONP peut nous être utile, on va voir comment.

Le principe du JSONP

Le JSONP n’est pas une forme de JSON, c’est du JSON, mais utilisé de façon astucieuse en conjonction entre le serveur et le navigateur.

Un exemple simple de JSONP

Le fichier front end jsonpfront.php :

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON P</title>
</head>

<body>
    <div id="demo"></div>
</body>
<script>
    function myFunc(myObj) {
        document.getElementById("demo").innerHTML = myObj.name;
    }
</script>

<script src="jsonp.php"></script>

</html>

Le fichier back jsonp.php.

<?php
$myJSON = '{ "name":"John", "age":30, "city":"New York" }';

echo "myFunc(".$myJSON.");";
?>

La balise script du front inclut le fichier du back jsonp.php, Ce fichier s’il est exécuté, revoit un réponse texte myFunc({ « name »: »John », « age »:30, « city »: »New York » })

myFunc est définie plus haut dans le script du front, donc en fait elle va être exécutée au moment du chargement de la page.

Un exemple plus intéressant dynamique de JSONP

Ok on a vu que ça marchait, mais c’est un peu statique tout ça, par exemple le snippet d’inclusion du est statique, on va le rendre dynamique, après tout lorsque qu’on fait une requête AJAX c’est souvent sur demande de l’utilisateur non?

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON P</title>
</head>

<body>
    <div id="demo"></div>
    <a href=# onclick="clickButton();">Click</a>
</body>
<script>
    function myFunc(myObj) {
        document.getElementById("demo").innerHTML = myObj.name;
    }

    function clickButton() {
        let s = document.createElement("script");
        s.src = "jsonp.php?x=Yvon";
        document.body.appendChild(s);
    }
</script>


</html>

Il faut cliquer sur le bouton pour inclure à la demande le fichier jsonp.php.

Et le nouveau fichier du backend, qui renvoit un content-type application/json, comme une vraie réponse AJAX du serveur. De plus on passe au fichier du backend un paramètre GET, pour simuler une demande d’une information particulière (par exemple les informations d’un utilisateur.). On simule succinctement une requêtes à la base de données en fait.

<?php
header("Content-Type: application/json; charset=UTF-8");
$obj = json_decode($_GET["x"], false);
$outp = ["name" => $_GET["x"], "age" => 30, "city" => "New York"];
echo "myFunc(" . json_encode($outp) . ")";

Ok ça marche aussi ! La réponse du serveur est à la demande, comme une requête de type GET.

Quid du cross domaine?

Je vous avait promis du cross domaine au début voyons voir comment ! C’est tout simple on va dans le fichier front modifier le endpoint de fichier jsonp.php, et mettre ce dernier sur un serveur sur Internet. Mon front sera toujours en local, donc on est bien en situation de cross domaine.

    function clickButton() {
        let s = document.createElement("script");
        s.src = "http://glottr.com/jsonp.php?x=Yvon";
        document.body.appendChild(s);
    }

C’est là que c’est intéressant. Vous voyez comment on peut inclure le fichier jsonp.php ? C’est dans la balise <script> et dans ce cas, on peut inclure un script de n’importe quel domaine.

Bougeons le fichier et regardons, le résultat est le même !

On peut faire plus sophistiqué avec JSONP

Jusqu’ici la fonction myFunc était codée dans le serveur, comment ferait on si on ne sait pas à l’avance la fonction à invoquer côté client? Vous pouvez passer le nom de la fonction callback en paramètre GET





Comment ferait on en AJAX?

Démarrer une application Node/Express Typescript

Bootstrapping du projet Node Express

Avec NodeJS, il existe plusieurs façon de faire une application web

#créer le répertoire de projet
mkdir nodeexpressts
# initaliser le projet en répondant oui à tout
npm init -y

npm init -y    // dit oui à tout
{
  "name": "nodeexpressts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  // Ensuite créer un fichier index.js pour commencer
touch index.js
ouvrir VSCode dans ce répertoire projet
code .

On va installer Express

npm install express

On va code le fichier index.js

//index.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}!`);
});

Passons à Typescript !

Installation de Typescript

npm install typescript

Ensuite renommons le fichier index.js en index.ts !

En survolant les variable maintenant vous allez voir que pas mal de variable sont de type any, ça prouve qu’on est passé nen mode Typescript.

Installaton des fichier de type pour nodeJS et Express

npm install @types/express @types/node

modification des façons d’importer

Dans Typescript on n’utilise pas require mais import

import express, { Express, Request, Response } from 'express';
const app: Express = express();
const port = process.env.PORT || 3000;

app.get('/', (req: Request, res: Response) => {
    res.send('Hello World! in Typescript Now'); // modifié
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}!`);
});

A ce stade en survolant la varaible express, on voit que c’est typé.

Illustration, vous ne pouvez pas mettre n’importe quoi avec la notation pointée, si l’objet ne possède pas la méthode, vous aurez une erreur.

app.toto  << erreur souligné en rouge dans VSCode

Pour aller plus loin dans le typage et aider Typescript à savoir de quel type est une varaible on postfix avec les deux point

app.get('/', (req : Request, res : Response) => {
    res.send('Hello World!');
});

Démarrage du serveur sour Typescript

Nous devons transpiler le Typescript en Javascript avant de démarrer le serveur. Invoquons tsc

npx tsc --init    // npx si on n'a pas tsc en global

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

Un fichier tsconfig.json est apparu, pour configurer la manière dont Typescript sera transpilé par exemple. Nous devons indiqué où se trouvent les fichier Js de sortie, ouvrons ce fichier

"outDir": "./dist"

Pour transpiler taper simplement npx tsc ou tsc (si on a installé en global, vérifiez le)

Si vous avez l’erreur suivante:

error TS18003: No inputs were found in config file 'E:/<chemin>/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '["E:/<chemin>/multiservice/dist"]'.

C’est que vous n’avez pas de fichier .ts (typescript) dans le projet, il faut renommer les fichier js en ts.

tsc
un répertoire dist est apparu, regarez le contenu ce sont des fichiers JS.
Maintenant pour démarrer nodeJS

node dist/index.js

Faisons comme les pro, comment avoir npm build par exemple

Ouvron spackage.json et configurons l’objet scripts:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "start": "node dist/index.js"
  },

Modifions le script

app.get('/', (req: Request, res: Response) => {
    res.send('Hello World! in Typescript Now WITH NPM RUN');
});

Et en ligne de commande faisons

npm run build
> nodeexpressts@1.0.0 build
> tsc
//puis
npm run start

Installons Nodemon ! (en devDependencies)

npm install -D nodemon

Maintenant ça va redémarrer à chaque fois qu’on va modifier un fichier.

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc -w & nodemon dist/index.js"
  },

npm run dev

Le & simple va exécuter en background

Il se peut que ça ne marche pas, en effet le & tout seul n’est pas une commande Windows mais Linux. Remplacer par :

"dev": "tsc -w && nodemon dist/index.js

il est probable que ça ne marche pas non plus, car tsc -w (w comme watch) peut ne pas rendre la main à nodemon. Dans ce cas il faut installer concurrently

npm install concurrently --save-dev
    "dev": "concurrently \"tsc -w\" \"nodemon dist/index.js\""

C’est un peu lourd comme syntaxe mais ça devrait résoudre vos problèmes.

Nettoyer le dossier dist avec rimraf

Si on veut s’assurer que le dossier soit entièrement nettoyer, on va installer rimraf (comme rm -rf !)

npm i -D rimraf


puis on modifie package.json

    "build": "rimraf dist &&tsc",

Mettez un fichier dans dist puis exécutez npm run build, le fichier va disparaître. Modifions le script pour ajouter un prestart et preserve, (et renommer dev en serve), qui vont simplement nettoyer le répertoire dist.

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rimraf dist &&tsc",
    "prestart": "npm run build",
    "start": "node dist/index.js",
    "preserve": "npm run build",
    "serve": "concurrently \"tsc -w\" \"nodemon dist/index.js\""
  },

En lançant npm run serve, c’est preserve qui est lancé en premier. Start est pour la production, serve est pour le développement.

Ce tuto est inspiré de cette vidéo Youtube

Uploader en ajax un fichier avec barre de progression

Vous savez sans doute uploader via un formulaire une image vers un serveur, et aussi via ajax (c’est un peu plus compliqué). Mais il est possible d’uploader en Ajax progressivement par morceau (chunk en anglais).

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BLOB upload</title>
</head>

<body>
    <form action="#">
        <input type="file" value="" id="file" name="file"><br>
        <input type="submit" id="uploadBtn" value="UPLOAD">
    </form>
</body>
<script>
...
</script>
</html>

Grâce à l’attribut name on peut faire référence au fichier. Dans la balise script on a le code suivant :

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })


</script>

La fonction uploadBlob() va prendre le fichier qui est passé en argument, puis le décomposer dans une boucle en plusieurs chunk de taille fixe. Notre boucle se termine quand on a atteint la fin du fichier.

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })
  
  function uploadBlob(file) {
        
        const chunkSize = 100_000;
      
        let start = 0

        for (let start = 0; start < file.size; start += chunkSize) {

            const chunk = file.slice(start, start + chunkSize);
            const fd = new FormData();
            fd.set('file', chunk,);


            fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => alert('Blob Uploaded'))
                .catch(err => alert(err));
        }
    }
</script>

Note boucle for va saucissonner le fichier en portions égales (à l’exception du dernier tronçon) à chaque itération. Ensuite ce morceau sera uploadé.

Côté backend pour la réception des fichiers en PHP

<?php
//  blobupload.php
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
$result = file_get_contents('php://input');
$written = file_put_contents('cpu.png', $result, FILE_APPEND);
echo $written;

C’est plutôt simple côté backend, le code est synchrone. Il retourne la quantité de bytes écrits ( 1 byte = 1 octet)

Essai d’upload par paquet en javascript

Nous allons prendre un fichier qui fait plus de 100000 octets pour déclencher la découpe. Pour l’exemple j’ai une image de 811 Ko nommée cpu4.png. Cela fait combien de paquets (chunks) à envoyer? Simple division 811000 / 100000 = 9. Le dernier paquet fait moins de 100000 octets ou bytes.

Image que nous allons envoyer.

On ouvre les devtools, et effectivement j’ai 9 envois.

Si vous essyez d’ouvrir l’image cependant, vous ne pourrez pas la visualiser. En fait l’image uploadée est différente de l’image choisie, vous n’avez qu’à comparer leurs checksum sur un outil comme celui-ci.

Mais comment est ce possible? Regardez l’image ci-dessus, le fichier php renvoit la taille écrite en guise de réponse, et l’avant dernier paquet montre 29669 octets, alors que ce devait être le dernier paquet qui doit montrer ce nombre ! Bref les paquets sont envoyés dans le désordre !! Nous allons corriger cela simplement avec async/await, qui va rendre un peu de synchronisme dans la boucle for.

Async/await à la rescousse.

Nous allons modifier le code de la façon suivante : uploadBlob sera async et on « awaitera » fetch.

    async function uploadBlob(file) {
        //reader = new FileReader() // le file reader va lire à partir d'une position donnée
        const chunkSize = 100_000;
        // const filename = file.name;
        let start = 0
        let i = 0

        for (let start = 0; start < file.size; start += chunkSize) {
            console.log("morceau ", i, "uploadé")
            i++
            let end = start + chunkSize
            const chunk = file.slice(start, end);
            const fd = new FormData();
            fd.set('file', chunk,);


            await fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => response.text())

        }
    }

Maintenant le dernier paquet est bien le plus petit. Tous les paquets sont envoyés dans l’ordre. Nous pouvons vérifier que l’image s’ouvre bien. Vérifiez les checksum de ces deux fichiers, ils sont pareil.

Il est à noter que dans le cas des envois en désordre, la taille du fichier reçu est variable !

Utiliser Rollup pour fusionner vos fichiers javascript

Application d’origine sans bundle JS

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bundler with Parcel</title>
</head>

<body>
    <form action="#">
        <label for="email">Email</label><br>
        <input type="text" value="" id="email"><br>
        <label for="password">Mot de passe</label><br>
        <input type="text" value="" id="password"><br>
        <input type="submit" id="btn" valu="Soumettre">
    </form>
</body>
<script src="./script1.js"></script>
<script src="./script2.js"></script>
<script src="./script3.js"></script>

</html>

Les différents scripts JS

//script1.js
let email = document.querySelector('#email')
let password = document.querySelector('#password')
let btn = document.querySelector('#btn')


//script2.js
email.addEventListener('keyup', (event) => {
    if (!isEmailValid(email.value)) {
        console.log('Email is invalid')
    } else {
        console.log('Email is valid')
    }
})

password.addEventListener('keyup', (event) => {

    console.log('Password length ', password.value.length)

})

// Vérifie si l'email est correct
const isEmailValid = (email) => {
    return email.toLowerCase()
        .match(
            /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
        );
}

//script3.js
btn.addEventListener('click', (event) => {
    event.preventDefault()
    if (isEmailValid(email.value) && password.value.length > 8) {
        alert("Form is valid")
    } else {
        alert("Form is invalid")

    }
})

Si votre page HTML fait appel à l’inclusion de beaucoup de fichier JS, vous aurez envie de fusionner tous ces fichiers en un seul pour le rendre plus compact. je vous propose d’utiliser Rollup, qui est simple comparé à Webpack. On va installer Rollup

npm install rollup @rollup/plugin-node-resolve --save-dev

Ensuite créer un fichier de configuration

import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [resolve()]
};

Dans le fichier main.js vous importez les fichier Javascript

import './script1.js';
import './script2.js';
import './script3.js';

// etc

Si vous avez un message du genre

Ajoutez une propriété dans package.json

{
    "scripts": {
        "build": "rollup -c"
    },
    "devDependencies": {
        "@rollup/plugin-node-resolve": "^15.2.3",
        "rollup": "^4.18.1"
    },
    "type": "module"
}

Désormais dans le fichier HTML l’inclusion du Js ressemble à ça

//index.html
...
</body>
<script src="./dist/bundle.js"></script>
</html>

A quoi ressemble le bundle.js

(function () {
    'use strict';

    document.querySelector('#email');
    document.querySelector('#password');
    document.querySelector('#btn');

    email.addEventListener('keyup', (event) => {
        if (!isEmailValid$1(email.value)) {
            console.log('Email is invalid');
        } else {
            console.log('Email is valid');
        }
    });

    password.addEventListener('keyup', (event) => {

        console.log('Password length ', password.value.length);

    });

    // Vérifie si l'email est correct
    const isEmailValid$1 = (email) => {
        return email.toLowerCase()
            .match(
                /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
            );
    };

    btn.addEventListener('click', (event) => {
        event.preventDefault();
        if (isEmailValid(email.value) && password.value.length > 8) {
            alert("Form is valid");
        } else {
            alert("Form is invalid");

        }
    });

})();

Comprendre les sockets avec Socket.io

Qu’est qu’un socket?

Un socket est une interface de communication entre deux noeuds dans un réseau informatique. Plus concrètement, entre deux ordinateurs, et pour les développeurs web entre un serveur et un client, aka votre ordinateur et plus précisément le navigateur.

Côté serveur

On va pour simplifier utiliser Nodejs, avec un script en local on va pouvoir en quelques ligne mettre en oeuvre un socket grâce à la librairie socket.io

Côté client

Grâce à une librairie socket.io-client, on va pouvoir se connecter à un socket.

Quelle est la différence entre un socket et une requête AJAX?

Dans le monde du HTTP, une communication entre client et serveur ne dure qu’un instant, entre deux communications il n’y a rien. Vous faites une requête AJAX, le client initie une demande (Request), le serveur réponse avec un Response, et puis c’est fini.

Dans le cas d’un socket, une fois que le client initie une demande de connexion, la communication s’établie et dure tant que l’on n’a pas arrêté, elle ne s’arrête pas toute seule. Le socket sert à maintenir une connexion persistente, avec un flot continu de données de part et d’autre.

Le socket répond au besoin de recevoir contnuellement des données, une application comme une courbe de bourse en temps réel a besoin d’un socket, cela permet une mise à jour temps réel du cours de bourse. Bien sû on pourrait faire toute les secondes une requête AJAX, mais c’est très consommateur de données, de par la nature du protocole HTTP. Socket ne fait pas partie de la technologie HTTP.

Démarrer un projet avec socket.io

Créez un nouveau répertoire de projet, ouvrez le avec VSCode, ouvrez un terminal sous VSCode.

Créer un fichier package.json comme ci-dessous:

{
  "name": "app-socket",
  "version": "0.1",
  "description": "mon socket.io app",
  "dependencies": {}
}

Installez Express

Express est un framework pour NodeJS en MVC.

npm install express@4

Créez le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

vous pouvez lancer le serveur NodeJS avec la commande:

node index.js

ça va initialiser un serveur qui écoute sur le port 3000 (par défaut pour NodeJS). Si dans votre navigateur, vous ouvrez l’url http://localhost:3000. Gardez un oeil sur le terminal de VSCode également.

Ajout d’un fichier HTML

Nous allons améliorer notre code avec une page HTML à servir

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages > li { padding: 0.5rem 1rem; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Changez le code d’index.js

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

Relancez votre serveur avec node index.js

Note : Pour éviter de redémarrer à la main à chaque fois qu’il y a une mise à jour du code, je vous suggère d’installer nodemon, un démon NodeJS, qui va redémarrer le serveur automatiquement à chaque enregistrement de fichier.

npm install --save-dev nodemon  // installe en tant que dépendance de développement
// redémarrer le serveur
nodemon ./index.js localhost 3000

Intégration de socket.io

Maintenant on va allez voir les choses intéressantes, l’intégration de socket !

Il nous faut agir sur deux fronts, sur le serveur et sur le client. Il y a donc deux partie à la librairie socket.io. (nous sommes toujours en architecture client/serveur)

Socket.io côté serveur

npm install socket.io

et éditons le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// début de code de gestion du socket
io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

// fin de code de gestion du socket

server.listen(3000, () => {
  console.log('listening on *:3000');
});

Notez qu’on crée le serveur de socket en lui passant dans le constructeur le server NodeJS. « connection » est l’événement qui se déclenche lorsque la page dans le front end se charge pour la première fois et qu’un message donc est envoyé au serveur.

Socket.io côté client

Ajoutez le snippet dans le fichier HTML avant la fermeture de body

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

ça y est notre application web est équipée!

Maintenant ouvrez la page a port 3000, votre terminal nodeJS va afficher ‘a user connected’. Ce qui se passe c’est que lorsque la page est loadée, la librairie socket.io va envoyer un signal vers le serveur, qui intercepte le message. Si vous bne voyez rien, rafraichissez la page.

Construction de l’application de messagerie instantanée

Maintenant côté client, écrivons le code JS pour émettre les messages entrés au clavier, dans le formulaire:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();

  var form = document.getElementById('form');
  var input = document.getElementById('input');

  form.addEventListener('submit', function(e) {
    e.preventDefault();
    if (input.value) {
      socket.emit('chat message', input.value);
      input.value = '';
    }
  });
</script>

et côté serveur on va afficher le message, ajoutez le snippet suivant :

io.on('connection', (socket) => {
...
  socket.on('chat message', (msg) => {
    console.log('message: ' + msg);
  });
});

Tapez les messages dans le formulaire, observez les logs côté serveur.

Le broadcasting côté serveur

Intéressons nous maintenant sur comment le serveur va émettre un message pour tous les client connectés, à la manière d’une station radio, d’où le terme ‘broadcasting’. Il faut utiliser la méthode emit() de l’objet io. Modifiez le code :

io.on('connection', (socket) => {
    console.log('a user connected');

    socket.on('disconnect', () => {
        console.log('user disconnected');
    });

    socket.on('chat message', (msg) => {
        console.log('message du client: ' + msg);
        io.emit('retour', { propriete1: 'valeur 1', propriete2: 'valeur2' })
    });
});

Configurer VSCode pour débugger

Il ya plusieurs façons de débugger NodeJS avec VSCode, le plus simple (mais peut être le plus ennuyant) est d’utiliser l’auto Attach, dès qu’un process NodeJS est démarré, VSCode se met en écoute. On va utiliser cette méthode. Pour plus d’information sur les autres méthodes allez sur cette page.

Utiliser les dev tools pour visualiser les communication via le socket

Vous pouvez voir les connexion socket comme vous pouviez le faire avec les requêtes Ajax. Dans les devtools, allez dans l’onglet Réseau, cliquez sur WS dans les filtres (à côté de XHR, Images etc.

Dans l’onglet Réponse, vous pouvez voir en fait les allées et venues des messages.

Programmation orientée événéments en Javascript

Ce que tous les développeurs web connaissent, ce sont les événements du DOM, les clics de souris, les survol, les chargements de page. Soit la page HTML suivante, un formulaire avec une div cachée qui se révèle dès que le formulaire change

<body>
    <style>
        #popup {
            border: black 1px solid;
            width: 50%;
            height: 10vh;
            border-radius: 5px;
            margin: 10px;
            display: none;
        }
    </style>
    <form action="#" id="formulaire">
        <label for="email">Email</label>
        <input type="text" id="email"><br>
        <label for="name">Name</label>
        <input type="text" id="name"><br>
        <input type="submit" value="Send">
    </form>
    <div id="popup">
        <h1>Form filled !</h1>
    </div>
</body>
    let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')

    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.style.display = 'block'
    })

Mais il est aussi possible de lancer des événement depuis n’importe quel objet en Javascript, et ceci va nous permettre de coder notre application avec des événements, permettant une grande souplesse et découplage.

Par exemple, imaginez un formulaire avec login et mot de passe, di les deux champs sont validés, nous voulion faire apparaitre une popup.

    let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')
    popup.addEventListener('formEvent', (e) => {
        popup.style.display = 'block'
    })
    //create the CustomEvent
    const formEvent = new CustomEvent('formEvent', { detail: "les détails" })



    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.dispatchEvent(formEvent)
        // popup.style.display = 'block'
    })

Il faut mettre bubble à true pour que l’événement se propage

  let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')
    popup.addEventListener('formEvent', (e) => {
        popup.style.display = 'block'
    })
    //create the CustomEvent
    const formEvent = new CustomEvent('formEvent', { detail: "les détails",bubbles:true })



    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.dispatchEvent(formEvent)
        // popup.style.display = 'block'
    })

Déjouer les piège du mot clé this en Javascript

Le mot clé this en javascript sert dans 3 contextes différents, et son comportement diffère selon les contexte.

le mot clé this dans une classe

C’est sans doute le plus facile à appréhender, car c’est comme dans les autres langages de programmation comme Java ou PHP.

class Rectangle {
  constructor(hauteur, largeur) {
    this.hauteur = hauteur;
    this.largeur = largeur;
  }
}

Le mot clé this dans une fonction

function test() {
    console.log('this in a function', this);
}

const test2 = () => {
    console.log('this in arrow function', this);

}
test2() // objet window
test() // objet Window

Comme on est dnas l’espace global (Window) le mot clé this représente Window. En effet les deux fonction sont définie dans l’espace de nom global et non dans un objet.

Le mot clé this dans l’espace de nom global

dans une fonction, la valeur de this dépend de comment la fonction est appelée. Considérez this comme un paramètre caché de la fonction. this est l’objet dans lequel la fonction est appelée.

Le cas des fonctions flèches

function test() {
    console.log('this in a function', this);
}

const test2 = () => {
    console.log('this in arrow function', this);

}
// test2() // undefined
// test() // objet Window
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.test = test
obj2.test2 = test2
console.log(obj1.test()) // this est l'objet obj1
console.log('arrow ', obj2.test2()) // this est l'objet Window

Dans les fonctions flèche, le this représente toujours l’objet global Window, alors que dans les fonctions classiques this représente l’objet dans lequel la fonction est définie.

Le cas du strict-mode

"use strict";

function show() {
    console.log(this);
}

show(); // undefined

This dans un event listener

Ici c’est plus courant, vous avez déjà vu avec Jquery le $this, en Javascript pur c’est pareil avec this

<span id="btn">Click Me</span>
<script>
const btn = document.querySelector("#btn")
btn.addEventListener('click',function(event){

console.log(this)

}

</script>

Modifier la valeur de this avec bind,call et apply

const person1 = {
    name: 'Paul',
    surname: 'Auchan',
    sayName: function () {
        return this.name + " " + this.surname;
    }
}

const person2 = {
    name: 'Justin',
    surname: 'News'
}

console.log(person1.sayName.call(person2)); // le this sera le this de person2
//appel avec arguments

const person2 = {
    name: 'Justin',
    surname: 'News'
}

const person3 = {
    name: 'Paul',
    surname: 'Auchan',
    sayName: function (ville) {
        return this.name + " " + this.surname + ', ' + ville;
    }
}
console.log(person3.sayName.call(person2, "Paris")); // le this sera le this de person2

Cas de bind

Autant call et apply se ressemblent, bind n’exécute pas la fonction , il faut l’invoquer manuellement. bind ne fait que binder une variable

//bind
const person4 = {
    name: 'Jules',
    surname: 'Sanchez',
    sayName: function() {
        return this.name + " " + this.surname
    }
}

const person5 = {
    name: 'Rudy',
    surname: 'Hess'
}

const sayPerson2Name = person1.sayName.bind(person2)

console.log(sayPerson2Name())

Simuler le clic d’une souris avec Javascript

Non je ne parle pas de l’événement « click » ou « onclick », mais carrément de créer un événement souris. En javascript nous disposons de MouseEvent, qui ne se limite pas au seul clic gauche ou droit, mais tient compte aussi des déplacements de la souris.

var clickEvent = new MouseEvent('click', {
    bubbles: true,
    cancellable: true,
    clientX: 32,
    clientY: 32,
})

Voici le la documentation officielle de MouseEvent. Cependant je dois aussi vous dire qu’il y a un autre événement appelé PointerEvent. Cet événement est plus générique, car il peut gérer les événement touch, multi contact, alors que MouseEvent ne peut pas. Mais dans cet article nous allons nous concentrer sur MouseEvent.

Description de MouseEvent

Les 3 coordonnées de la souris dans une page web

Le référentiel d’un élément HTML

Le référentiel de la fenêtre

Le référentiel du document

Mise en apllication de MouseEvent

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MouseEvent</title>
    <script src="MouseEvent.js" defer></script>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <div class="container">
        <div class="header"></div>
        <div class="main">
            <h1>Titre</h1>
            <div class="card">
                <p>Ceci est un exemple pour illustrer MouseEvent</p>
            </div>
        </div>
    </div>
</body>


</html>

function simulateClick() {
    let titre = document.querySelector('h1')

    let clickEvent = new MouseEvent('click', {
        bubbles: true,
        cancellable: true,
        /*    offsetX: 20,
            offsetY: 20,*/
    })


    console.log('dispatch')
    titre.dispatchEvent(clickEvent)
    console.log('clicked')

}

let h2 = document.querySelector('h1').addEventListener('click', function (e) {
    alert('toto')
})

setTimeout(simulateClick, 300)

La fonction simulateClick() va sélectionner l’élément H1 et la mettre dans la variable titre, construit un objet Event (MouseEvent pour être plus précis, MouseEvent héritant de Event), ensuite et c’est là qu’il faut être attentif, c’est titre qui va émettre (dispatch) l’événement clickEvent.

Dans le programme principal, h2 qui représente le même élément que titre reçoit une simulation de click et donc une alert() va faire apparaitre une popup. au bout de 300 millisecondes.

Liens en rapport avec l’article:

/////////////////////// DOM onunload ////////////////////
https://stackoverflow.com/questions/11177233/javascript-event-that-runs-before-page-changes
https://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node-in-javascript-or-in-debugging
https://stackoverflow.com/questions/4386300/javascript-dom-how-to-remove-all-event-listeners-of-a-dom-object/64484951#64484951

Comprendre les modules en javascript

Qu’est ce qu’un module en Javascript?

Un module est un fichier tout simplement rien de plus. On parle de module en javascript moderne, en effet ce concept n’existait pas avant ES6. Les modules existent en NodeJs également. Mais dans cet article je ne vais parler que des modules côté client.

Avant l’existence des modules, on ne pouvait pas importer un fichier Javascript dans un autre fichier Javascript comme on peut le faire en PHP.

Ici on ne va parler que des modules front end, dont la syntaxe est différente des modules dans NodeJS, dans ce dernier il existe au moins deux façon de faire, avec require() (commonJs) et import (AMD).

Comment on faisait avant pour importer un script Javascript?

#index.html
<html>
...
<body>
..
<script src="index.js"></script>
<script src="helper.js"></script>
...

On se servait de la balise script our importer les fichiers, index.js ne pouvait pas importer helper.js. La conséquence est que dans un fichier HTML on pouvait avoir facilement plus d’une dizaine d’import de fichier javascript. Cela pouvait avoir un impoact sur la rapidité du site.

Et vinrent les modules Javascript

Dès qu’il y a un import de modules, on observe au minimum deux fichiers. Soient deux fichiers, un principal.js, et un helper.js

//index.html
<script type="module">
import {sayHi} from './helper.js';

sayHi('John');
</script>
// helper.js
export function sayHi(name){
     console.log('Hello ' +  name)
}

On peut importer une fonction à condition qu’elle ait été exportée depuis le fichier helper.js. Dans la balise script on doit ajouter l’attribut type= »module » pour que cela marche.

Les caractéristiques de modules

Les modules utilisent le mode strict

Dans un code Javascript vous utilisez le mode strict pour forcer une plus grande rigueur dans la programmation

Par exemple dans un fichier HTML

<script>
a = 5  // autorisé pas d'erreur dans la console
</script>

Par contre

<script>
"use strict"
a = 5  // ReferenceError: a is not defined
</script>

de même dans un module
<script type="module">
a = 5 // ReferenceError: a is not defined  bien qu'on n'ait pas eu à mettre use strict 
</script>

Le scope des variables

Chaque module a son scope pour ses variables (je parle des variable globales du module, pas les variable locales des fonctions), les modules ne communiquenet donc pas leur variables, autrement dit, un module ne peut utiliser une variable globales d’un autre module.

// user.js
let user = "Jules"

// main.js
console.log(user) //  

//index.html
<script type="module" src="user.js"></script>
<script type="module" src="hello.js"></script>

la console va afficher "SyntaxError: The requested module './lib.js' does not provide an export named 'user'"

//on modifie dans user.js
export let user = "Jules"  // correct

//Pour illustrer directement dans une page HTML
<script type="module">
    export let user = "Jules"
</script>
<script type="module">
    console.log(user)  // Erreur,et on ne peut faire import du module puisqu'il est inline, la seule façon urait été de faire window.user = "Jules"
</script>

Evaluation une seule fois du module à l’import

//index.html
<script type="module">
    window.test = 0
</script>
<script type="module">
    import { val } from './util.js'

</script>
<script type="module">
    import { val } from './util.js'

    console.log(val)
</script>

// util.js
window.test++
export let val = window.test

bien que l’on ait importé deux fois le module util.js, le code va afficher 1 et non 2

Prenons un autre exemple, qui va montrer que cet aspect est en fait pratique, permet aux différents scripts de se « partager  » une variable

// config.js
export let config = {};

// 1.js
import { config } from './config.js'
config.user = "user1"

//2.js
import { config } from './config.js'
config.url = "http://local.test/user1"

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(config)
</script>

L’exemple ci-dessus montre que les fichier 1.js et 2.js apportent des nouvelles propriétés

La propriété meta de import

// en reprenant l'exemple précédent

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(import.meta)
</script>

On obtient quelques informations sur le module courant.

Le mot clé this dans un module

Dans un module le mot clé this n’existe pas, il est undefined

<script>
  alert(this); // window
</script>

<script type="module">
  alert(this); // undefined
</script>

Les script module sont deferred dans un client

Ce qui suit n’est valable que dans un navigateur

Vous connaissez l’attribut defer dans la balise script.

<script type="module">
  alert(typeof button); // ce module est defer, il se télécharge immédiatement, mais il attend que le HTML finisse de se télécharger avant de s'exécuter.
</script>


<script>
  alert(typeof button); // s'exécute immédiatement après le téléchargement, avant que le HTML du bouton ne vienne
</script>

<button id="button">Button</button>

Toujours préciser le chemin,

Même si le script est adjacent à votre page HTML, il faut préfixer avec un point slash

import {compteur} from 'util.js'; // dans un navigateur ceci n'est pas autorisé,TypeError: Failed to resolve module specifier "util.js". Relative references must start with either "/", "./", or "../".
import {compteur} from './util.js'; 

scripts externes et CORS

L’inclusion de script externe en tant que module doit se faire avec précaution dans le cas des modules. Pour les scripts externes de même origine que la page HTML, pas de soucis, mais si le script vient d’un autre nom de domaine, alors le serveur d’origine

<script type="module" src="https://site.com/script.js"></script>
il faut que site.com ait activé le CORS.

async et les modules

Le mot clé async permet de télécharger en parallèle un script javascript externe, mais ne s’utilise pas pour les script inline (à même la page HTML). dans le cas des modules, le mot clé async s’applique aussi aux Javascript inline.

async Javascript import
<script async type="module">
  import {counter} from './utils.js';

  counter.count();
</script>

Async, defer, preload, prefetch : tout savoir sur ces attributs

Ces attributs lorsqu’il sont utilisés sur une page HTML on tous un point commun : ils permettent de télécharger de façon asynchrone les fichiers liés depuis la page web, dans le but d’accélérer le téléchargement de la page et son exécution.

Async et Defer s’appliquent à la balise <script> uniquement

Comment utiliser ces deux attribut dans un script ? voici un exemple

<script src="init.js" defer></script>

ou 

<script src="init.js" async></script>

Quelle est la différence entre async et defer? Regardez les deux schémas tirés de ce site,

async ne vas pas attendre la fin du téléchargement de la page HTML pour s’éxécuter, alors sur defer va attendre la fin du téléchargement de la page HTML (le parsing plus exactement).defer va être utile si vous avez besoin que tous les éléments du DOM soient mis en place.

Preload et Prefetch sont pour les balises <link>

<link rel="preload" href="style/style.css">
<link rel="preload" href="main.js">

preload permet de télécharger de façon asynchrone les ressources indiquée (css, javascript), mais il ne sont pas exécutés. Ils seront exécutés par le navigateur en cas de nécessité, ce qui est une bonne forme d’optimisation. Pour en savoir plus sur les détails techniques, cette page de la documentation officielle sur MDN.

Prefetch pour pré-télécharger des pages web

<link rel="prefetch" href="/articles/" as="document">

Cet attribut a pour but de prétélécharger une page (même si on ne va pas la visiter) pour que lorsqu’on clique sur le lien, on n’ai pas à la télécharger. Il faut bien sûr s’assurer que c’est une page souvent visitée. La page prétéléchargée sera dans le cache prefetch.

L’image ci-dessus explique le fonctionnement (source : lien de la source).

Quelle différence entre le Node et les Elements dans le DOM?

lorsque vous faites des requêtes sur les éléments d’une page HTML et en faisant un affichage des éléments que vous avez obtenus, parfois vous avez des nodelist et parfois vous avez des Element, cela peut prêter à confusion parce que à première vue ce sont deux choses qui sont similaires, mais alors si elles sont similaires pourquoi elle portent deux types différents?

Node (Nœuds) dans le DOM

Dans le DOM, tout est un nœud. Un nœud peut être un élément, un attribut, un texte, un commentaire, un document, ou tout autre type d’objet DOM. Les nœuds sont organisés dans une structure arborescente, avec le nœud de document en haut et tous les autres nœuds en découlant.

Les nœuds ont des propriétés et des méthodes qui vous permettent de les manipuler ainsi que leurs nœuds enfants. Par exemple, vous pouvez utiliser la méthode appendChild() pour ajouter un nœud enfant à un nœud existant.

Éléments dans le DOM

Les éléments sont un type spécifique de nœud qui représente un élément HTML ou XML. Les éléments ont toutes les propriétés et méthodes d’un nœud, mais ils ont également des propriétés et des méthodes supplémentaires qui leur sont propres.

Par exemple, les éléments ont une propriété tagName qui spécifie le nom de l’élément, tel que « div » ou « span ». Les éléments ont également des attributs, qui peuvent être accédés en utilisant la méthode getAttribute() ou simplement en accédant à l’attribut comme une propriété de l’élément.

Il existe 12 types de noeuds

Source StackOverflow

Element est un type de noeud, il y a aussi les noeuds TEXT, COMMENT, etc. Mais le plus familier pour nous est le type Element, les éléments de la page HTML.

Quand obtient l’un ou l’autre?

Lorsque vous faite une requêtes qui peut retourner plusieurs item, vous obtenex un NodeList (ce n’est pas un tableau !)

    <div class="container">
        <ul id="liste">
            <li id="un">Un</li>
            <li id="deux">Deux</li>
            <li id="trois">Trois</li>
        </ul>
    </div>


let el = document.getElementById('un')   
console.log(el)  // retourne un Element

let nodelist = document.querySelectorAll('li')
console.log(nodelist)   // retourne un nodeList

Dans l’exemple ci-dessus, même si le second exemple retourne un NodeList, les items de cette nodelist sont des Elements ! Vous pouvez le vérifier avec instanceof

console.log(nodelist[0] instanceof Element)  // true
console.log(nodelist[0] instanceof Node)  // true
console.log(el instanceof Node)  // true

Mais alors pourquoi dans ce cas on ne fait pas un objet ElementList? c’est un choix des ingéieurs qui ont fait Javascript.

Par contre HTML5 définit un objet HTMLCollection, qui est un objet qui ne contient que des Element, qui exclut tous les autres types de noeuds. Vous pouvez voir HTMLCollection à l’oeuvre avec l’exemple suivant:

let el = document.getElementById('liste') 

En pratique vous n’avez pas à vous soucier de ces subtiles différences.

Quand utiliser Redux

Quand ne pas utiliser Redux

  • budget peu élevé
  • application de taille petite à moyenne
  • UI simple, flux de données simple
  • données statiques

Pours et contres de Redux:

Pour :

  • débug facilité
  • test
  • faire et défaire
  • persistence de state
  • préserve le state d’une page

Contre:

  • complexité
  • verbosité

Débuter avec Greasemonkey

C’est un plugin navigateur qui vous permet d’exécuter du code Javascript sur la page où vous vous trouvez. Vous connaissez les extensions de navigateur? Greasemonkey vous permet facilement de retrouver les mêmes capacités sans avoir à coder toute l’extension.

Installez Greasemonkey version 4 (sur Chrome c’est TamperMonkey).

Pour aller au plus simple, cliquez sur l’icône du singe, vérifiez qu’il est bien activé puis cliquez sur Nouveau Script

// ==UserScript==
// @name     Anonyme Script 813510
// @version  1
// @grant    none
// ==/UserScript==


Sion on veut de l'ajax (cross domain en plus !)
//@grant GM.xmlHttpRequest

Vous pouvez personnaliser les annotations, @name pour le nom du script, @version qui n’aura pas d’influence sur votre scripts, et surtout @grant est très important, si vous voulez par exemple faire une requêtes Ajax, par défaut vous ne pouvez pas. Je vous joins la page de documentation du plugin:

Débugger sous NodeJS avec VSCode

VSCode est vraiment l’éditeur de code couteau suisse, son avantage est que s’il y a un langage même confidentiel, vous trouverez un plugin. Même s’il n’est pas le meilleur dans chaque catégorie, par exemple je trouve PHPStorm mieux pour le PHP, il est un très bon outil gratuit de surcroît.

Pour NodeJs, c’est un peu plus simple que pour Xdebug en PHP, il y ap lusieur façons de déclencher le débugger de NodeJS, on va voir l’auto déclenchement du débugger dès qu’un process node est en cours d’exécution.

Je suppose que vous ayez un fichier index.js, à exécuter via la commande shell, pour le lancer vous faites node index.js. Si vous ne l’avez pas fait vous pouvez démarrer un projet NodeJS.

Et là vous devrez avoir la barre de status orange, indiquant que le débug est actif.

Configuration de VSCode pour le debug automatique (Auto attach)

Pour activer l’auto attach, faites la combinaison de touche : SHIFT + CTRL + P, une boite de recherche va apparaitre, tapez « Toggle Auto Attach » pour trouver l’option et cliquez dessus.

Maintenant chaque fois que vous démarrez un process NodeJS, VSCode se met en mode debug.

Comment déployer votre propre package sur NPM?

Nous allons voir comment créer et déployer soi-même son package NPM, plutôt cool comme idée non? Voici les étapes :

  • Créer un compte sur le site de NPM
  • écrire notre package
  • tester localement
  • publier notre package sur NPM
  • test grandeur nature de notre package

Créer un compte sur le registre NPM

Voici un fichier par default de package.json, personnalisez le :

{
  "name": "npmpackage",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

packge.json est le fichier important sans lui vous ne pouvez pas publier de package.

Le fichier index.js est le point d’entrée de votre package.

Le nom du package doit être unique dans tout le repository NPM, cela a donné à l’affaire du npmgate

Dans la barre de recherche de NPM vous pouvez chercher par nom de package.

Créez le fichier index.js

On va faire simple un seul fichier:

let uniqueArr = [];

function removeArrayDuplicates(arr) {
    // Accepts an array from which the duplicates
    // will be removed

    if (!Array.isArray(arr)) {
        arr = [];
    }

    let theSet = new Set(arr);

    arr.filter((num) => {
        if (!uniqueArr.includes(num)) {
            uniqueArr.push(num)
        }
    })

    return uniqueArr;
}

/* code de test */
let myNums = [1, 2, 3, 1, 4, 1, 2, 5, 3, 4];
let uniqueNums = removeArrayDuplicates(myNums)
console.log(uniqueNums);

Mettez à jour le package.json

{
  "name": "remove-dup-arr",
  "version": "0.1.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "remove",
    "array",
    "duplicate"
  ],
  "author": "refschool <[referencementschool@gmail.com](mailto:referencementschool@gmail.com)>",
  "license": "ISC"
}

Publication de votre package NPM

Pour cela inutile d’aller sur le site web de NPM ! vous pouvez tout faire en ligne dans votre terminal. Enlevez le code inutile d’application qui vient après la fonction.

npm login
// entrez vos identifiants

npm publish

j’ai eu un problème car le nom du paquet existe déjà, donc ce qu’on peut faire c’est de changer vers un nom qui n’existe pas encore

{
  "name": "openweather-wrapper",
  "version": "0.1.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "openweather",
    "meteo",
    "demo"
  ],
  "author": "refschool <[referencementschool@gmail.com](mailto:referencementschool@gmail.com)>",
  "license": "ISC"
}

PS E:\npmpackage> npm publish 
Debugger attached.
npm notice 
npm notice �📦  openweather-wrapper@0.1.0
npm notice === Tarball Contents ===
npm notice 521B index.js    
npm notice 359B package.json
npm notice === Tarball Details ===
npm notice name:          openweather-wrapper
npm notice version:       0.1.0
npm notice filename:      openweather-wrapper-0.1.0.tgz
npm notice package size:  586 B
npm notice unpacked size: 880 B
npm notice shasum:        ca698456c2b3c8691534f370f7539d0c42662724
npm notice integrity:     sha512-oCm4uzyLUsJC3[...]b1V0cR834Cchg==
npm notice total files:   2
npm notice
npm notice Publishing to https://registry.npmjs.org/
+ openweather-wrapper@0.1.0
Waiting for the debugger to disconnect...

Vérifions sur le site NPM la présence de notre package qui est visible pour le monde entier :

Test grandeur nature de notre package NPM

Maintenant que notre package est déployé je me suis rendu compte que quelque chose n’allait pas, il n’est pas utilisable car pas de directive export !

Remédions à cela à la fin du fichier index.js

module.exports = removeArrayDuplicates;

et republions.

Maintenant créons un autre répertoire (qui n’a rien à voir avec notre précédent répertoire) dans lequel nous allons tester notre package

npm install openweather-wrapper

Un répertoire node_modules va être créé.

Créez un fichier test.js avec le code suivant :

const tab = [8, 4, 2, 65, 2, 33, 33, 33, 22, 8, 8];
const removeArrayDup = require('openweather-wrapper');
let unik = removeArrayDup(tab)

console.log(unik)

Félicitation vous avez réussi !

javascript

Mémento de Fetch

Faire une requête GET avec fetch

L’expression la plus simple pour une requête GET avec fetch est :

fetch("https://google.fr")
.then(function(response){
    return response.json
})
.then()

Pour une requête GET pas la peine d’indiquer à la fonction car c’est celui par défaut

Faire une requête POST avec fetch

Le body

let apiUrl = "http://localhost/api/"
            fetch(apiUrl, {
                method: "POST",
                body: {
                    name : 'toto'
                   }
            })

La requête ci-dessus va envoyer un objet JSON contenat l’attribut « name », via la méthode POST (comme si c’était un formulaire)

Retour en haut