#18987

C'est quoi tous ces divs ? | CommitStrip

Normalement on met le HTML à afficher sur une image, qu’on envoie au nav en base64 dans du JSON. Côté client, on utilise une lib de décodage base64 (pas que tous les nav ont ça désormais, mais c’est mieux avec une lib de 600 ko) puis un scanner OCR de 3 Mo et ses dépendances (2 Mo, dont une implémentation de pong en webGL que personne n’a demandé) avant d’incorporer ça dans le DOM.

http://www.commitstrip.com/fr/2019/09/20/whats-going-on-with-all-these-divs/

#18419

URL - Référence Web API | MDN

Parfois, c’est vraiment des demeurés ceux qui font les spec JS.

"URL" c’est la nouvelle API pour produire/parser une URL. C’est l’équivalent de « pathinfo() » en PHP.

On peut récupérer le chemin, le domaine, le port, l’utilisateur, le mot de passe, le protocole, le hash, les paramètres…
…mais pas le nom du fichier courant.

Yup, ils y ont tout mis… sauf le "basename".

Du coup on est obligé de mettre un

url.pathname.substring(url.pathname.lastIndexOf('/')+1)

Normalement, faut pas ajouter des méthodes aux prototypes, mais je pense que je vais faire une exception ici. Cette lacune est ridicule.

https://developer.mozilla.org/fr/docs/Web/API/URL

#18412

iro.js demo

Un color picker en JS.

Sympa.

Sauf qu’à première vue, les 10 lignes de JS ne sont pas tout : y a une lib qui est incluse (voir les settings). Et la lib seule fait 30 ko…

Ouais, à ce tarif, je préfère mon color picker JS/CSS : https://lehollandaisvolant.net/tout/tools/color/
Ça fait 15 ko (HTML + JS + CSS), et il convertit en RGB/HSL/CMYK/HEX dans n’importe quel sens.
Bon par contre, ça fonctionne pas dans IE.

https://codepen.io/rakujira/pen/WZOeNq

#18411

JavaScript loose comparison (==) step by step

Des exemples du fonctionnement du "==" en JS, sachant que « 2 == "2" » retournera true, il y a tout un tas de procédures qui entrent en jeu pour convertir les deux membres jusqu’à obtenir un résultat.

(PS : connaître le fonctionnement interne d’un ordi, d’un CPU, d’un langage ou d’un environnement d’exécution peut beaucoup aider pour optimiser ses scripts)

https://felix-kling.de/js-loose-comparison/

#18401

[PWA] fetch-serviceworkers.png (image) - 1354x1577px

Les Services Workers JS sont pratiques, mais bordel que c’est chiant à débugguer !

Je suis obligé de mettre des console.log() à toutes les lignes pour savoir ce qui se passe :o

Ici, c’est *juste* la fonction qui intercepte les requêtes réseau de la page (toutes les requêtes : XHR, mais aussi les fichiers scripts, les CSS, les fonts, les background-image()…).

La fonction permet de voir :
– si une requête doit être mise en cache (fichier, image) ou pas (requête de type json/ajax).
– si elle doit être mise en cache, alors on regarde si le fichier s’y trouve. Si oui ? retourne le fichier. Si non ? fait une connexion réseau. La connexion, fonctionne-t-elle ? Si oui : récupère la réponse et la met en cache, puis retourne la réponse. Si non, emet une erreur.

Le truc c’est que pour que le script puisse prétendre être une bonne PWA, aucune requête ne doit finir sur une erreur (404 ou autre). Si l’app est offline, la requête ne marchera pas, mais l’erreur doit être gérée (normale, me diriez-vous, mais c’est assez lourd quand-même).

Autrement, une fois qu’on a notre fonction, comme ça, c’est à peu près tout ce qu’il faut pour transformer n’importe quelle page web en PWA avec capacité de faire du offline (du moment qu’on séparre bien le shell de la page des données, d’où l’intérêt de travailler avec des requêtes Ajax qui envoie des données (json ou xml, html…) qui sont ensuite incorporées dans la page.

Le rendu est spectaculaire et 'achement rapide.

Regardez sur le site de Stéphanie Walter (sous Firefox) : c’est rapide comme l’éclair ! https://stephaniewalter.design/

Et pour cause : la gestion du cache est gérée par la page web (le service-worker) : il suffit de naviguer quelques pages pour que toutes les données soient mises en cache et après c’est super rapide : seulement 14 ko de données sont transférées pour la page d’accueil !

Et normalement ça fonctionne aussi en offline.

Oui, le navigateur a son propre cache… mais il est imprévisible, d’une part, et d’autre part il y a toujours des requêtes réseau de faites, qui vérifient si les cookies sont bons, si les ETAG sont modifiées, si le fichier n’a pas changé, etc.

Ici, les requêtes réseaux sont interceptées par le Service Worker.

Bien-sûr, ce n’est pas une raison pour se lâcher et arrêter d’optimiser ses pages : cette rapidité doit servir à permettre un usage offline du site (c’est pratique pour lecteur RSS en JS par exemple). J’ajoute simplement le raccourcis sur le bureau android (ou iOS, ou Windows 10 comme "webapp") et c’est bon !

Si je suis en ligne, il rapatrie les données mises à jour, autrement il utilise ce qu’il possède en cache.

En plus, les navigateurs utilisent des bases de données locales pour les données en cache : on peut donc très bien utiliser des BDD pour nos données (pas besoin de rester au JSON/XML).

https://lehollandaisvolant.net/img/35/fetch-serviceworkers.png

#18363

Navigator.sendBeacon() - Web APIs | MDN

À utiliser plutôt qu’un « onbeforeunload » dans lequel on met une dernière requête XHR sur lequel on désactive le async (sinon ça marchait pas). Ces derniers retardaient (de façon tout à fait logique) la fermeture de l’onglet et ça gênait le chargement de la page suivante.

Avec sendBeacon(), le navigateur ferme l’onglet et charge la page suivante, mais la « dernière requête » est faite quand le navigateur a le temps et de façon asynchrone.

J’utilise une « dernière requête » dans mon lecteur RSS : la liste des éléments lus est gardée dans un tampon mémoire local. Une requête n’est faite que quand on a 10 éléments : ça m’évite de faire une requête à chaque fois qu’on lit un article, et de surcharger le réseau pour rien.

Si le tampon n’est pas vide au moment de fermer l’onglet, j’utilise donc le .sendBeacon() qui envoie les derniers éléments lus au serveur.

window.addEventListener("beforeunload", function() {
	var formData = new FormData();

	// ajouter les champs du formulaire ici, comme avec une requête Ajax normale

	navigator.sendBeacon('ajax/rss.ajax.php', formData);
});

De même j’ai découvert ça :

document.addEventListener("visibilitychange", function() {
	// do stuff
});

Le « visibilitychange » c’est pour quand on passe à un autre onglet, ou si on met une autre application au premier plan (sur mobile).

Dans mon cas, je mets la même chose que dans le « beforeunload » : comme ça la requête est faite aussi quand l’utilisateur ferme l’onglet.

Faut pas oublier de tester le « document.visibilityState » qui peut prendre différentes valeurs. Dans mon cas, je teste pour « hidden » : inutile de lancer une requête si le visibilityChange concerne une remise au premier plan du navigateur.

Ce truc est utilisé par certains sites pour mettre en pause des vidéos quand on clic l’onglet. C’est d’ailleurs assez chiant quand c’est utilisé à ces fins là. Même chose pour les sites de téléchargement avec des décomptes de 30 secondes : le décompte est pausé quand on change d’onglet.

Je découvre pas mal en ce moment, et c’est vraiment cool tout ce qu’on peut faire en JS natif aujourd’hui :3

https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon

#18345

[JS] - Note

Je me note :

En JS, avec element.querySelector(), pour cibler un descendant direct de element, ceci ne marche pas :

element.querySelector('> .class');

Si element possède un id lui-même on peut faire ça :

element.querySelector('#id > .class');

Mais il y a une méthode dédiée :

element.querySelector(':scope > .class');

:scope est censée représenter l’élément sur lequel on utiliser querySelector(). C’est une notation CSS similaire similaire à « :root », et d’ailleurs, à ce jour, il est égal à :root (il n’a pas encore d’autres usages).

C’est censé marcher (même si je n’ai pas réussis sous Firefox).

Sinon, on peut toujours utiliser

element.firstElementChild

(attention, si querySelector() renvoie une nodeList, firstElementChild() renvoie une HTMLCollection.)

https://lehollandaisvolant.net/?mode=links&id=20190221013812

#18342

Note : lenteur JS

Rhaaa…

(oui, encore sur le JS)

Les fonctions sur les dates en JS sont pratique. Je parle celles des formats et l’internationnalisation, celle qui quand on lui dit "Fr" nous sort « mercredi 20 février » et quand on lui dit “de” nous sort « Mittwoch, 20. Februar ».

Voyez là :
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/toLocaleDateString
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/DateTimeFormat

Mais bordel, qu’est-ce que c’est LENT !!

J’utilise ça pour afficher la date dans mon lecteur RSS.
Bah ça me prenait 700 ms pour afficher ~650 posts.

Vous ne voyez pas le problème : 1 ms par post, c’est pas énorme à première vue. Mais moi si : en mettant la fonction qui définit le format en dehors de la boucle, je ne mets plus que 50 ms pour afficher 650 posts.

Aaah, voilà qui est mieux =).

Donc faut faire comme ça :


var DateTimeFormat = new Intl.DateTimeFormat('fr', {year: "numeric", weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "numeric"});

feedList.forEach(function(item) {
    …
    …
    li.querySelector('.post-head > .date').textContent = DateTimeFormat.format(item.datetime);
    …
}

Ça semble logique, mais c’est tellement con et je m’en tape la tête contre les murs >_<.

C’est comme vider un nœud de ses éléments :

element.innerHTML = "" // trèèès lent.
// très rapide !
while (elementfirstChild) {
    elementremoveChild(element.firstChild);
}

=_=

https://lehollandaisvolant.net/?mode=links&id=20190220163207

#18337

Note : rapidité d’affichage d’un lecteur RSS

Je maintiens toujours mon propre lecteur RSS. C’est cool et ça me permet de faire face à des problèmes parfois incongrus pour des petits projets.

Déjà, faut savoir que ma connexion est pourrie (<2 Mo/s) et que je ne souhaite pas encombrer mon serveur de requêtes trop lourdes. Pour le moment, mon lecteur RSS tourne donc en local.
Si je met ça en ligne, ça me prendrait beaucoup de temps pour ne serait-ce qu’ouvrir le lecteur RSS.

Certains lecteurs RSS font une requête à chaque ouverture d’un post ou d’un site. C’est impensable pour moi : je ne veux pas attendre 3 secondes à chaque clics.

Depuis le début, l’ensemble des flux RSS "non lus" sont envoyés au navigateur. Généralement, ça fait entre 2 et 3 Mo, mais comme c’est en local, c’est instantané.

Je cherche à améliorer ça.

Je suis aussi sur le point de transformer mon lecteur RSS en PWA (application mobile en HTML/JS/CSS). Pour ça, je dois scinder l’interface de l’application des données. Vu que je bosse intégralement en JSON, c’est très simple.

Concernant la vitesse, en quelques lignes de JS j’ai ma page qui s’affiche et une fois chargée, fait une requête vers le serveur avec les données. C’est pas plus lent qu’avant (mais je fais un pas de plus vers la PWA).

Là où je m’interroge, c’est comment accélérer ça encore plus ?

Sur les 3 Mo transférés, la plus grande partie provient du contenu des articles, le reste étant plutôt des méta-données : date, ID, nom du flux et le titre de l’article.

J’essaye donc :
– à l’affichage de la page, l’interface s’affiche.
– Pendant ce temps, une première requête qui récupère titre + métadonnées et qui suffisent pour afficher les flux dans une liste.
– une seconde requête est ensuite faite qui récupère le contenu des articles et les attache à la liste principale.

La première requête suffit pour afficher la liste des flux : l’utilisateur peut commencer à lire les titres et à trier visuellement les articles qu’il souhaite lire (du moins, perso je fonctionne comme ça).
Pendant qu’il repère les articles qu’il va lire, la page récupère le contenu des articles (2,5 Mo).

Dans l’ensemble, ça prend peut-être quelques ms de plus pour charger, mais beaucoup moins de temps pour s’afficher : l’interface s’affiche instantanément, par exemple. C’est une question de perception de rapidité.

PS :
Quand je combine le résultat des deux requêtes, je fais deux boucles imbriquées, pour vérifier l’égalité tableau1[i].id === tableau2[j].id.
C’est un super-exemple d’utilisation du « break » dont je parle dans cet article. Une fois qu’il y a une égalité, on sort de la boucle et on passe à l’élément suivant.

Résultat : j’ai 471 éléments RSS à parser, donc 471×471 = 221 841 tours de boucles à faire. Avec le break, je prédisais qu’on gagne environ 50 % de la charge. Ça se vérifie sur cet exemple : le nombre de tours de boucle est de 111 214. Le gain est de 49,86 %. On y est.

En fait, je même beaucoup mieux : comme les deux requêtes renvoie deux tableaux de la même base de donnée rapidement à la suite, il est fort probable que les deux tableaux (triés en SQL) comportent la même indexation (sauf si une mise à jour des données RSS s’est glissée pile entre les deux requêtes).

On peut donc faire ça :


// si les ID sont identique, on ne reboucle pas (les deux tableaux comparent à la position « i »)
if (tableau1[i].id === tableau2[i].id) {
	# code here
}
// autrement, on recherche dans tout le tableau (tableau 1 sur « i », et le tableau2 sur « j »)
else {
	for (var j=0, len2 = _this.feedList.length ; j<len2 ; j++) {
		if (newFeeds[i].id === _this.feedList[j].id) {
			# code here
			break;
		}
	}
}

Dans le cas idéal, on passe à 471 tours de boucle (pour 471 éléments)
… au lieu de 111 214…
… au lieu de 221 841.

C’est bien non ?

PPS :
Finalement, le truc où je fais deux requêtes séparées c’est pas une bonne idée.
Ça marche, et l’idée peut fonctionner ailleurs, mais ici le gain n’est pas aussi important que je pensais. En fait, je viens de voir que les données sont déjà compressées par Gzip/deflate par le serveur (réduisant le poids de 2,5 Mo à 0,6 Mo environ).
Ça reste beaucoup de données, mais même avec une connexion pourrie, le temps que la connexion se fasse et que le serveur exécute la requête, j’en suis déjà à 1/3 du temps. Autant éliminer une des deux requêtes et n’en faire qu’une seule : ça reste plus rapide.

https://lehollandaisvolant.net/?mode=links&id=20190219154857

#18328

Why I Write CSS in JavaScript

J’ai pas de mots.

Le commentaire qui résume bien la situation, c’est ça :

AKA : too lazy to learn CSS specificity
https://mxstbr.com/thoughts/css-in-js/

#18293

Can You Find The Bug in This Code? - victorzhou.com

Always use semicolons.

+1

Rien que pour ça, j’aime que l’on apprenne du C à l’école : au moins y a une forme de rigueur qui devrait s’appliquer partout.

Il y a ça et y a l’indentation. Pour ça, faudrait du python (où c’est l’indentation qui délimite les bloc de code).

Les langages laxistes sont moins chiants, dans du code simple, mais quand s’aventure un peu au delà, ça finit par nous retomber dessus.

Ah et mettez le « 'use strict' » dans vos scripts aussi. Ça oblige à ne pas faire des trucs louches sortis d’un chapeau.

https://victorzhou.com/blog/a-javascript-bug-i-had-once

#18189

How browser rendering works — behind the scenes – LogRocket

Un article qui explique succintement comment fonctionne un moteur de rendu d’un navigateur, en particulier comment il traite le JS, l’arbre DOM (le HTML) et le CSSOM (le CSS).

La connaissance de ceci permet de savoir où placer les différents éléments.

Par exemple : le HTML commence à charger, mais le JS est bloquant : dès qu’il y a du JS dans la page (inline, ou non), alors le parsage du HTML se pause : ceci, car le JS peut modifier le HTML. Il est donc inutile de parser un truc qui peut être changé par la suite.

Or, le JS peut également toucher au CSS. Pour ça, le CSSOM doit être prêt. Donc le CSS doit être parsé pour que le JS puisse être éxécuté, et le JS doit être exécuté si on peut que le HTML soit parsé.

Dit autrement, le navigateur doit avoir fini de charger dans cet ordre :
– le CSS
– le JS (se finit après le JS)
– le HTML (terminé à la fin, quand la dernière balise se ferme)

Aussi, si on veut que la page s’affiche vite pour que le lecteur le lise rapidement, il faut donc que le CSS soit fini le plus tôt possible pour que l’information (portée par le HTML) soit affichée correctement.
Enfin, vu que le JS est bloquant, l’information utile de la page doit être affichée avant l’exécution des scripts.

Du coup, on voit bien que le CSS doit être placé au début du document et le JS à la fin : https://lehollandaisvolant.net/?d=2015/08/27/18/46/54-pourquoi-mettre-le-javascript-a-la-fin-et-le-css-au-debut

https://blog.logrocket.com/how-browser-rendering-works-behind-the-scenes-6782b0e8fb10