Tagjavascript

[JavaScript] Exécuter une fonction seulement une fois

js/utils.js:

[JavaScript] Mediaqueries et breakpoints en JS ES6+ avec la librairie breakpoint-helper

Base de l’initialisation du helper

Utiliser la méthode listen() avec un breakpoint unique

(Mobile first!) On définit un breakpoint unique à (max-width: 1199px) au-delà duquel on exécute du code pour les Desktops.

La clé/name 'large' correspond à une largeur de 1200px. Le paramètre useMax permet l’ajout et le calcul de la valeur max-width.

Une autre solution si vous souhaitez une meilleure séparation du code exécuté par breakpoint

Cette solution alternative vous permet d’inclure du code pour nettoyer votre DOM dans le listener propre au breakpoint pour lequel vous initialisez également des choses.

[Webpack Encore] Optimiser vos bundles CSS avec PurgeCSS et JS et le tree shaking

Testé fonctionnel Webpack Encore v1.1.2 (tournant sous Webpack v5.34.0).

Utiliser PurgeCSS avec Webpack Encore

Source: blog de PHP Roberto – Integrating Purgecss with Symfony Encore (version PDF: phproberto.com-Integrating Purgecss with Symfony Encore).

Le tree shaking avec Webpack Encore

Quelques ressources en ligne:

Oui, le tree shaking est activé par défaut en mode « production » dans Webpack Encore. C’est juste que la documentation officielle sur symfony.com oublie de le mentionner (grrr…)!

Vous pouvez effectuer le test qui est décrit ici (aka « le tree shaking dans Webpack Encore expliqué en 30 secondes ») pour vous en assurer.

Pour exemple, si vous avez les deux fichiers suivants:

Exécuter la commande yarn encore dev devrait générer quelque chose de similaire à ceci:

Cependant, en exécutant la commande yarn encore production le commentaire /* unused harmony export testB */ présent avec la précédente commande sera pris en compte par UglifyJs qui va en conséquence supprimer l’export en relation lors de la phase de minification. Dans ce cas, on ne devrait pas retrouver la chaîne de caractère « Test B » dans le fichier généré:

Activer les commentaires /* unused harmony export */ en développement avec le paramètre config.optimization.usedExports = true;

Ceci vous permettra de vérifier avec du code humainement lisible quels sont les parties de votre code qui ne sont pas exploitées et qui seront supprimées dans le bundle minifié de production.

Tout à la fin de votre fichier webpack.config.js:

Vous pouvez tout-à-fait laisser cette config activée. Elle ne portera pas préjudice au poids final de votre build de production.

[Webpack Encore] Intégration d’UIkit v3 dans un projet Symfony pour un bundle à la carte

Méthode préférée de création d’un bundle des composants du framework front-end UIkit v3 avec Webpack

Testé fonctionnel UIkit v3.6.19!
Voir aussi: la méthode la plus simple, mais extrêmement limitée en terme de choix des composants qu’on souhaite importer (embarque tous les composants JS du core, embarque les styles d’absolument tous les composants).

Suite à différents tests de génération d’un bundle entièrement à la carte des composants d’UIkit v3 que j’ai effectué et à quelques soucis rencontrés avec l’affichage des icônes, je me suis rendu à l’évidence que le framework open-source développé par YOOTheme n’allait pas pouvoir être intégré à mon projet Symfony dans les règles de l’art.

Pour pouvoir sélectionner à la carte les composants et les icônes qui doivent figurer dans notre bundle (et surtout: retirer tous ceux qui ne vont pas servir!!!) tout en consignant les informations sur notre sélection directement dans le Git de notre projet, nous allons notamment devoir:

  • Créer un dossier mirroir des sources d’UIkit (dossier ./node_modules/uikit/*) dans le dossier qui contient le code spécifique à notre projet (dossier ./assets/uikit/* dans mon cas).
  • Commenter tous les composants dont nous n’aurons pas besoin pour notre build custom dans les fichiers assets/uikit/src/scss/theme/_import.scss et assets/uikit/src/scss/components/_import.scss (mirroirs de ceux du dossier source ./node_modules/uikit/src/scss/*).
  • Commenter tous les composants dont nous n’aurons pas besoin pour notre build custom dans les fichiers assets/uikit/src/js/core/index.js et assets/uikit/src/js/components/index.js (mirroirs de ceux du dossier source ./node_modules/uikit/src/js/*).
  • Consigner notre sélection d’icônes dans des dossiers mirroirs de ceux du dossier source ./node_modules/uikit/src/images/*
  • Exécuter un script bash qui va s’occuper de:
    • Remplacer les fichiers source de ./node_modules/uikit/* par leurs versions mirroirs consignées dans ./assets/uikit/* (et ajouter les nouveaux fichiers si il y en a).
    • Lancer, depuis ./node_modules/uikit, les commandes de build fournies dans les sources d’UIkit pour notre sélection de composants et d’icônes.
    • Lancer, depuis la racine du projet Symfony, la commande Webpack de build des sources SCSS, JS et icônes de notre projet (yarn encore dev).

Cette technique s’inspire du concept de fork, à la différence qu’ici nous n’aurons pas à maintenir un fork à chaque montée de version d’UIkit (un nouveau tag tous les 15 jours au moment où j’écris ces lignes).
Ici, les modifications relatives aux sources d’UIkit sont également versionnées directement dans les sources de notre projet custom alors qu’avec un fork doit avoir par définition son propre dépôt (donc un seul repository à gérer au lieu de deux).

Il s’agit de générer de nouvelles sources « dist » d’UIkit, taillées sur-mesure sur la base de notre propre sélection de composants et d’icônes.

C’est la source présente dans le dossier ./node_modules/uikit/dist qui est importée par défaut par Webpack lorsque nous déclarons la ligne import UIkit from 'uikit'; dans le fichier assets/app.js.

Note: en procédant ainsi il devrait (je n’ai pas encore testé) être possible d’ajouter également vos propres composants, développés sur les bases d’UIkit, au build custom.

La configuration de Webpack

Fichier webpack.config.js

Dans la configuration de Webpack, ajouter l’alias pour le chemin vers les sources SCSS d’UIkit dans le dossier ./node_modules:

Le bundle à la carte des styles (format SCSS)

Note: le build standard des styles d’UIkit (format de sortie/dist CSS) a ceci de particulier qu’il est réalisé à partir des sources LESS. Le framework est bien fourni avec une version SCSS des styles, mais il s’agit juste d’une conversion faite à partir des sources LESS et générée via un script.

En d’autres termes, si nous souhaitons travailler à partir du format SCSS, le build pour cette partie devra être réalisé côté Webpack.

Fichier assets/styles/app.scss

Note: selon votre propre organisation de vos fichiers SCSS, l’import de _uikit-explicit-pieces.scss devrait intervenir très tôt (en tout premier?) dans votre chaîne d’imports.

Fichier assets/styles/_uikit-explicit-pieces.scss

Créer un fichier _uikit-explicit-pieces.scss qui suit à la lettre les directives de mise en place des styles relatifs au framework front-end UIkit.

L’alias @uk-scss déclaré plus haut dans le fichier webpack.config.js est utilisé ici comme racine du chemin vers les fichiers sources.

Edit: l’utilisation de PurgeCSS avec Webpack rend le reste de la partie SCSS de cet article totalement OBSOLèTE!

Attention: les styles propres à la grille d’UIkit comportent des déclarations de type .uk-child-width-1-4@m. L’arobase n’étant pas présent dans la regex du defaultExtractor de PurgeCSS, il faudra faire comme moi dans le code ci-dessous et le rajouter si vous voulez éviter que les styles de la grille soient considérés comme non-utilisés et dégagés du bundle.

En effet, plus besoin d’éditer les fichiers _import.scss des sources d’UIkit pour commenter les composants dont nous n’avons pas besoin! PurgeCSS se charge de faire le boulot à notre place en scannant les chemins et fichiers que nous allons pointer dans sa configuration pour repérer les IDs et classes que nous utilisons et purement et simplement supprimer toutes les autres de notre bundle!

Voici un exemple à rajouter à la toute fin de votre fichier webpack.config.js:

Cette technique reste une suggestion, mais reconnaissez que c’est tout-de-même bien pratique!

Fichier assets/uikit/src/scss/theme/_import.scss

Ce fichier est, à la base, une copie conforme du fichier source https://github.com/uikit/uikit/blob/v3.6.19/src/scss/theme/_import.scss.

Commenter tous les composants dont vous n’aurez pas besoin pour votre build custom (où commenter carrément l’import de ce fichier dans assets/styles/_uikit-explicit-pieces.scss si vous pensez que rien ne va vous servir):

Fichier assets/uikit/src/scss/components/_import.scss

Ce fichier est, à la base, une copie conforme du fichier source https://github.com/uikit/uikit/blob/v3.6.19/scss/components/_import.scss.

Commenter tous les composants dont vous n’aurez pas besoin pour votre build custom:

Les fichiers JS

Fichier assets/app.js

  • On importe le fichier SCSS « racine/maître » app.scss. C’est celui qui va concentrer l’ensemble des @import des différents composants.
  • On importe le bundle* de notre sélection de composants JS via la directive import UIkit from 'uikit';.
  • On importe le bundle* de notre sélection d’icônes via les directives import Icons from 'uikit/dist/js/uikit-icons'; UIkit.use(Icons);
  • On initialise un composant Notification (test)

*: j’explique ça juste en-dessous.

Fichier assets/uikit/src/js/core/index.js

Ce fichier est, à la base, une copie conforme du fichier source https://github.com/uikit/uikit/blob/v3.6.19/src/js/core/index.js.

Commenter tous les composants dont vous n’aurez pas besoin pour votre build custom:

Fichier assets/uikit/src/js/components/index.js

Ce fichier est, à la base, une copie conforme du fichier source https://github.com/uikit/uikit/blob/v3.6.19/src/js/components/index.js.

Commenter tous les composants dont vous n’aurez pas besoin pour votre build custom:

Les icônes

Structure des dossiers (à respecter)

Attention: la documentation d’UIkit vous explique comment remplacer des icônes existantes ou ajouter de nouvelles icônes à votre projet.
Cependant, cette dernière fait mention d’un dossier custom/ dans lequel placer les nouvelles icônes à ajouter à votre projet (avant d’effectuer votre build)… sans préciser à quel endroit de l’arborescence des fichiers du framework celui-ci devrait se trouver (edit: dans src/custom/icons/)!

La manipulation des icônes fonctionnera très bien si vous déposez vos fichiers de remplacement et vos nouveaux fichiers dans l’un des 3 dossiers suivants:

  • Icônes des… erm… (reste à définir): ./assets/uikit/src/images/backgrounds/
  • Icônes des composants: ./assets/uikit/src/images/components/
  • Icônes de la bibliothèque d’icônes: ./assets/uikit/src/images/icons/

Note: dans le cas où une même icône est utilisée pour un ou plusieurs composants ET dans la bibliothèque (la croix de fermeture par exemple, présente au minimum dans le composant Notification ET dans la bibliothèque), le fichier doit être présent dans les deux dossiers ./assets/uikit/src/images/components/close-icon.svg et ./assets/uikit/src/images/icons/close.svg (libellé différent, et le code peut-être différent également pour une même icône, cf. ici (components/close-icon.svg) et là (icons/close.svg)).

Fichier assets/uikit/build/util.js

Ce fichier est, à la base, une copie conforme du fichier source https://github.com/uikit/uikit/blob/v3.6.19/build/util.js.

Je ne donne ci-dessous que la constante const svgo = new SVGO qui est à modifier avec l’ajout de deux nouveaux paramètres, mais l’intégralité du code issu du fichier source est, bien entendu, à conserver!:

  • removeAttrs qui va s’occuper de nettoyer le fichier SVG source en supprimant des attributs, inutiles pour nous, qu’on va par exemple retrouver dans les icônes issues de la populaire bibliothèque Font Awesome.
  • addAttributesToSVGElement qui va s’occuper d’ajouter les attributs width="20" et height="20", nécessaires au bon fonctionnement de la bibliothèque d’icônes UIkit, là où ils manquent. Attention: si ces attributs existent déjà avec des valeurs similaires ou d’autres valeurs, ils ne seront pas écrasés par ce paramètre!

Petit récapitulatif de l’arborescence attendue

…sous forme de capture d’écran.


Le script bash

Nouvelle méthode (au 04/05/2021):

Dans le fichier package.json qui devrait se trouver à la racine de votre projet, nous allons ajouter un script qui va générer automatiquement les sources dist optimisées d’UIkit:

Nous allons ensuite créer un fichier build/build-uikit.sh qui va contenir le script en question:

ATTENTION: afin de rendre ce script exécutable, il faut lancer depuis la racine de votre projet la commande chmod +x ./build/build-uikit.sh!

Désormais, vous pourrez lancer différentes commandes pour générer votre build à la carte d’UIkit:

  • yarn run uk: générer les sources JS et le sprite d’icônes SVG d’UIkit
  • yarn run uk uikit: générer uniquement les sources JS d’UIkit
  • yarn run uk icons: générer uniquement le sprite d’icônes SVG d’UIkit

Note: nous n’avons bien sûr pas de commande relative aux styles car ils sont générés directement via Webpack Encore.

Méthode précédente (dépréciée):

Attention: les sources du framework front-end UIkit consignées dans le dossier node_modules/ de votre projet ont leur propre fichier package.json. Pour exécuter les différents scripts de build, il convient donc d’avoir au préalable déployé les dépendances Node au sein du dossier à l’aide de la commande npm install. En d’autres termes, si le dossier node_modules n’est pas présent dans le dossier ./node_modules/uikit/, les commandes de build vont vous péter à la tronche :D.

Depuis la racine de votre projet:

On copie nos fichiers custom d’UIkit vers le dossier où sont consignées les sources issues du Git officiel (les fichiers initiaux existants seront écrasés par les nôtres; c’est ce qu’on veut):

On se rend dans le dossier où sont consignées les sources d’UIkit:

La manipulation suiffante n’est à effectuer que dans deux cas:

  1. Vous n’avez encore jamais lancé la commande npm install depuis ce dossier et le dossier node_modules n’est donc pas présent à la racine de ./node_modules/uikit/.
  2. Vous avez effectué des modifications dans le fichier ./node_modules/uikit/package.json (vérifier que ce fichier est présent dans ./assets/uikit/; ça peut être une bonne indication du fait qu’il faille lancer les deux commandes ci-dessous).

Effectuer un build non-minifié (paramètre -d – c’est Webpack Encore qui assurera la minification par la suite) des sources JS d’UIkit qui contient uniquement notre sélection de composants et la bibliothèque d’icônes:

Note: voir les autres paramètres possibles pour la commande node build/build.

On retourne à la racine de notre projet et on lance la commande de build propre à Webpack Encore:

Vos sources pour afficher votre site dans le navigateur sont prêtes!

Quelques chiffres suite à mes tests

Caveats connus!

  • Les icônes des composants, si elles proviennent d’une autre source que la bibliothèque UIkit, ne sont pas nettoyées par par les modifications apportées au script de build de cette dernière ce qui occasionne des soucis d’affichage. Solution de correction possible: passer au préalable par Webpack (plutôt que par une modification des scripts de build d’UIkit) pour nettoyer tous les SVG et les rendre « UIkit-compliant » avant de les envoyer vers ./node_modules/uikit/* et d’effectuer un build.

Méthode alternative (attention: limitations connues au niveau des icônes)

ATTENTION: plusieurs limitations au niveau des icônes avec cette méthode de bundle:

Diagnostique des erreurs:

M’affiche l’erreur suivante en console:

fastdom.js:67 TypeError: Cannot read property ‘cloneNode’ of undefined
at getIcon (icon.js:186)
at UIkitComponent.getSvg (icon.js:63)
at UIkitComponent.connected (svg.js:38)
at hooks.js:10
at Array.forEach ()
at UIkitComponent.UIkit._callHook (hooks.js:10)
at UIkitComponent.UIkit._callConnected (hooks.js:31)
at UIkitComponent.UIkit.$mount (instance.js:28)
at UIkitComponent.UIkit._init (state.js:23)
at new UIkitComponent (global.js:30)

Les icônes des composants sont générées séparément du sprite d’icônes utilisables à la carte (et qui, lui, fonctionne!).
Lorsque je compare aux sources JS fournies dans le dossier dist, je remarque l’absence du code suivant dans mon bundle Webpack:

Pas de résolution à ce jour.

Fichiers impliqués

Fichier assets/app.js

Fichier webpack.config.js:

Bundle Javascript – Fichier assets/uikit-explicit-pieces.js:

Bundle Styles – Fichier: uikit-explicit-pieces.scss:

[SEO] Obfuscation des liens pour le maillage interne

Article ayant servi de source à ce billet: Comment faire de l’obfuscation de liens pour votre maillage interne ? et version PDF – 410-gone.fr-Comment faire de lobfuscation de liens pour votre maillage interne.

[Magento 2] Les bases et patterns de l’utilisation de javascript (AMD, requireJS, widget jQuery, plugin, module, x-magento-init, etc…)

Intégrer et exploiter dans Magento 2 un plugin jQuery tiers dont le code source est structuré selon le pattern AMD

Pour illustrer cette partie, nous allons intégrer dans Magento 2 le plugin jQuery Slick Carousel, dont le code source est structuré selon le pattern AMD.

Remarque: l’intégration du plugin jQuery tiers va se faire dans un thème. Si vous comptez utiliser le plugin en question sur un projet multi-sites/multi-thèmes ou sur plusieurs projets, une meilleure stratégie consisterait à intégrer ce premier dans un nouveau module MyVendor_Slick qu’on placerait dans app/code/MyVendor/Slick.
Cette démarche n’est pas décrite ici, mais mériterait d’être envisagée en fonction de vos besoins.

Récupération du plugin sous Git et copie dans l’arborescence du thème

Récupérer la source minifiée du plugin Slick sous Git (tag v1.8.1 dans mon exemple, mais vous pouvez vérifier si il existe un tag plus récent) et copier-coller le fichier dans votre thème, au chemin suivant: app/design/frontend/MyVendor/mytheme/web/js/vendor/slick/slick.min.js.

Déclaration du plugin en tant que module via RequireJS

Dans le fichier app/design/frontend/MyVendor/mytheme/requirejs-config.js:

Création d’un fichier d’initialisation d’une instance du plugin

Dans votre thème, créer le fichier app/design/frontend/MyVendor/mytheme/web/js/init-slick.js qui va servir à appeller et à initialiser une instance du plugin javascript Slick et vous donnera accès à toutes les options de l’API originale:

Petite subtilité: ajouter une configuration spécifique par défaut à toutes les instances de votre plugin

Dans l’exemple ci-dessous, le fichier app/design/frontend/MyVendor/mytheme/web/js/init-slick.js est agrémenté d’un objet defaults qui surcharge (via les settings proposés de base par le plugin Slick Carousel prevArrow et nextArrow) l’aspect graphique des chevrons « slide précédent/slide suivant ».

L’intérêt d’ajouter une configuration spécifique par défaut à toutes les instances de votre plugin réside, pour l’exemple du Carousel, dans le fait qui si la charte graphique de votre projet prévoit les mêmes chevrons spés pour tous les sliders du site, vous allez pouvoir tous les initialiser sans avoir besoin de re-préciser à chaque fois que vous voulez surcharger les pictos fournis de base par ceux de votre charte.

Vous pouvez, bien entendu, les re-surcharger et utiliser d’autres options du plugin à la demande pour chaque nouveau Carousel que vous allez mettre en place.

Appeler et initialiser une instance du plugin sur un élément du DOM

Cette page de la documentation officielle vous expliquera comment appeler et initialiser une instance du plugin sur un élément du DOM dans Magento 2.

Elle aborde notamment la manière de procéder depuis un fichier template PHTML, depuis un fichier JS et comment exécuter data-amge-init et x-magento-init dans un cas où le DOM se met à jour dynamiquement.

Dans ce billet, je vais me contenter d’illustrer un exemple de notation déclarative avec x-magento-init depuis un fichier PHTML.

Exemple de notation déclarative avec x-magento-init depuis un fichier PHTML

Ici, j’initialise simplement mon module Slick sur l’élément du DOM qui porte l’ID home-slider. Le carousel va s’afficher soit avec les options de base fournies par le plugin Slick, soit avec les options de base fournies par le plugin Slick ET des pictos chevrons surchargés par ma configuration par défaut si j’en ai défini une.

Même exemple avec ajout d’options

Ici, je choisis de paramétrer mon carousel afin qu’il affiche 4 slides à la fois et qu’on scrolle d’une slide à chaque fois qu’on active les boutons prev/next ou tout autre mode de navigation fourni par le plugin. Les options slidesToShow et slidesToScroll sont fournies par le plugin Slick et exploitables immédiatement grâce à la magie d’AMD, de RequireJs et de Magento.

Ressources en ligne:

[Magento 2] Un module Javascript embarqué via RequireJS pour bloquer le scroll de la page à l’affichage d’un élément en surimpression

Testé fonctionnel Magento 2.4 CE. L’idée de ce billet est surtout de montrer comment Magento 2 permet l’accès à de petites fonctionnalités écrites en Javascript/jQuery de manière modulaire via RequireJS.

Le code source du plugin:

app/design/frontend///web/js/utils/page-scroll-state.js

Retenez le nom de la fonction handle() du plugin, car elle nous servira plus bas.

Déclarer le plugin via RequireJS:

app/design/frontend///requirejs-config.js

A noter: si l’on souhaite rendre notre plug-in disponible immédiatement dans toutes les pages du site, nous pouvons ajouter ceci au fichier requirejs-config.js (je ne sais pas si la présence d’une virgule en fin de ligne peut poser problème):

Exploiter le plugin dans un autre fichier JS, un PHTML, etc…:

  • ligne 3: on requiert notre plugin tel que déclaré dans la partie paths: du fichier app/design/frontend///requirejs-config.js
  • (ligne 2: on n’oublie pas la dépendance à jquery ou autre si besoin…)
  • ligne 4: on définit un alias pour notre plugin, à invoquer par la suite dans le code qui est encapsulé dans la fonction que nous ouvrons
  • lignes 10 et 18: on invoque la fonction .handle() de notre plugin, via l’alias pageScrollState (défini comme argument de la fonction en ligne 4)

Bonus: les styles!

[javaScript] Quelques expressions régulières (Regex) utiles

Source: Regex match string until whitespace Javascript.

Une regex qui extrait, à partir d’une chaîne de caractères de type level1 nav-1-1 category-item first active parent is-submenu-item is-drilldown-submenu-item la classe qui commence par nav- et se termine au prochain espacement (whitespace):

[Magento 2] Afficher/masquer le bouton « Effacer tous les filtres » dans la liste des catégories/produits

Testé fonctionnel Magento 2.4!

Dans mon exemple, l’ID « narrow-by-list » est présent deux fois dans le DOM (ce qui n’est pas du tout une bonne pratique) car la liste des filtres s’affiche dans deux zones bien distinctes de la page: 1. en mode horizontal au-dessus de la liste des catégories/produits et dans un volet glissant qui ne s’affiche qu’au clic sur un bouton de type « Voir plus de filtres ».

Il faudra donc adapter un peu mon code à votre propre utilisation.

Chemin: app/design/frontend/MyVendor/MyTheme/Magento_LayeredNavigation/templates/layer/view.phtml.

  • Je désactive la condition PHP <?php if ($block->getLayer()->getState()->getFilters()) : ?> qui sette la visibilité du bouton « Effacer tout », mais uniquement au rechargement de la page.
  • J’ajoute un ID <div id="filterActions" class="block-actions filter-actions">.

Dans le même fichier view.phtml, j’ajoute ce bout de code javaScript (il vous faudra probablement adapter le ciblage du selecteur suivant qui n’existera pas chez vous: let FILTER = $('#narrow-by-list.is-view-top').find('.active');):

On n’oublie pas le petit CSS qui va bien:

[Magento 2] Etendre un composant JS natif

Testé fonctionnel Magento 2.4. Source: Extending Magento 2 default JS components. Version PDF – inchoo.net-Extending Magento 2 default JS components.

Etendre le composant UI Tabs pour ajouter un effet de bordure animée sous les onglets:

app/design/frontend/MyVendor/mytheme/web/js/tabs-custom.js

app/design/frontend/MyVendor/mytheme/requirejs-config.js

app/design/frontend/MyVendor/mytheme/Magento_Theme/templates/html/sections.phtml

app/design/frontend/MyVendor/mytheme/web/css/source/tabs.less

app/design/frontend/MyVendor/mytheme/Magento_Sales/layout/sales_order_info_links.xml et app/design/frontend/MyVendor/mytheme/Magento_Sales/layout/sales_order_guest_info_links.xml

© 2021 devfrontend.info

Theme by Anders NorénUp ↑