Outils CSS/JS avancés

Quelques astuces CSS et JS avancées pour faire des choses pratiques..

[CSS][JS] Éviter d’avoir à retaper le mot de passe

Idée générale

Il est courant dans les formulaire de faire taper le mot de passe à deux reprises afin de vérifier qu’on le tape bien correctement. Cette pratique est très ancienne mais est assez lassante, surtout quand le mot de passe est assez long et qu’on est pressé.
Une solution est alors de placer un bouton pour rendre le mot de passe visible.

On peut faire ceci de plusieurs façons :

Dans ce qui suit, c’est la première option qui est retenue : en effet, elle demande une action concrete, un clic, pour révéler le mot de passe. Il n’y a donc pas de risques que le mot de passe s’affiche à l’écran de façon accidentelle.

L’idée est d’obtenir quelque chose comme ceci : formulaire avec nom et mot de passe

Si on montre chaque élément, cela donne ceci : formulaire avec nom et mot de passe

Principe

Ici, on part du principe que le champ n’est pas situé dans le label comme on le voit parfois. Il reste lié au label par le biais des attributs for et id.

Dans le principe de fonctionnement, c’est très simple : le label et le champ sont sur des lignes différentes. Pour cela, pas besoin de les mettres dans un paragraphe : il suffit d’afficher le label en block, et le champ de placera en dessous de façon automatique.

Les champs feront ici 250 pixels de large.
Les champs de mot de passe feront 200 pixels et le bouton pour révéler le mot de passe mesurera 50 pixels de large.

Pour avoir un comportement homogène, tous ces éléments devront avoir la même hauteur, les mêmes bordures et les mêmes marges. Le code contient tout ça, qui constituera une sorte de mini-css-reset pour unifier tous les éléments.

Le code

Le HTML :

<label for="username">Choose an username:</label>
<input type="text" name="username" id="username" size="30" value="John Doe" placeholder="John Doe" />

<label for="password">Choose a password: </label>
<input type="password" name="password" id="password" class="password" size="30" value="Password" placeholder="Password" /><button type="button" class="unveilpw" onclick="revealpass('password');"></button>

Le CSS :

/* The small CSS-reset */

label,
input,
button.unveilpw {
   border: 0;
   height: 2.5em;
   padding: 0 5px;
   background: white;
   line-height: 2em;
   box-sizing: border-box;
}

input,
button.unveilpw {
   border-bottom: 1px solid silver;
}

label {
   display: block;
}
input {
   display: inline-block;
   width: 250px;
}
.password {
   width: 200px;
}
.unveilpw {
   width: 50px;
   background-color: rgba(0, 0, 0, .02);
}

/* Show/Hide text is put in CSS, so we can change it in CSS */
/* Password is hidden */
.unveilpw::before {
   content: "Show";
}

/* Unveiling activated */
input[type="text"]+.unveilpw::before {
   color: red;
   content: "Hide";
}

/* Focus effect: Blue border on field when focus */
input:focus,
input:focus+.unveilpw {
   border-bottom-color: #2196F3;
   box-shadow: 0px -1px 0px #2196F3 inset;
}

Le JavaScript :

'use strict'
function revealpass(fieldId) {
   var field = document.getElementById(fieldId);
   field.type = (field.type == "password") ? "text" : "password";
   field.focus();
   field.setSelectionRange(field.value.length, field.value.length);
}

Demo

[CSS] Masquer une liste horizontale par la gauche

Idée générale

Ce que l’on veut faire ici, c’est masquer par la gauche des éléments d’un menu horizontal.

Normalement, quand on a une liste d’éléments placés horizontalement, les éléments les plus à droite et qui ne tiennent pas sur la ligne sont refoulés sur la ligne suivante. Il peut arriver que l’on ne veut qu’une seule ligne de données et on masque donc tout ce qui dépasse (avec un overflow: hidden par exemple).
Les éléments les plus à droite sont donc masqués.

Pour mon exemple, je dispose d’une liste de blocs qui représentent les mois de l’année. L’idée est de vouloir conserver les derniers mois, donc ceux à droite. Ceux à masquer sont donc ceux de gauche :
des blocs horizontaux

Principe

Il va falloir agir sur le HTML ici. Les blocs doivent être placés en ordre inversé dans le code source.

L’ordre « correct » sera ensuite remis grâce au CSS. On va utiliser ici l’arangement flexible sur la liste, et changer l’ordre grâce à ça. Si ceci n’est pas possible, on peut utiliser le changement d’ordre d’écriture avec direction: rtl; (ne pas oublier le direction: ltr; sur les éléments dans la liste ensuite) :

Le code

Le HTML :

<ul class="liste">
   <li>décembre</li>
   <li>novembre</li>
   <li>octobre</li>
   <li>septembre</li>
   <li>août</li>
   <li>juillet</li>
   <li>juin</li>
   <li>mai</li>
   <li>avril</li>
   <li>mars</li>
   <li>février</li>
   <li>janvier</li>
</ul>

Le CSS (uniquement ce qui permet le changement de l’ordre et le positionnement sous la forme d’une liste horizontale) :

.liste {
   overflow: hidden;
   display: flex;
   flex-wrap: wrap;
   flex-direction: row-reverse;
   justify-content: flex-end;
}

.liste > li {
   display: inline-block;
}

Demo

Quand on clic sur une ancre, la page défile et place la cible en haut de l’écran. Un problème survient quand le haut de l’écran contient déjà une barre horizontale (comme un menu) : l’élément ciblé est alors masqué par le menu.

On peut ajouter une marge sur l’élément, mais si on re-défile la page vers le haut, alors cette marge est visible. C’est n’est donc pas totalement transparent comme solution. Il y en a pourtant une.

Plutôt qu’une marge, on va utiliser un pseudo-élément qu’on va coller sur l’élément ciblé. Le pseudo-élément est invisible, mais il va prendre de la place à l’écran et il sera également devenu la cible de l’ancre. On va placer le pseudo-élément au dessus de la cible par dessus ce qu’il y a avant. Comme ça, c’est le pseudo-élément qui sera masqué par notre barre horizontale fixe et la cible sera repoussée juste en dessous, visible.

Le HTML :
Je ne mets pas ici le menu, je me contente de mettre le code à placer pour la cible de l’ancre.

<h2 id="ancre">L’ancre ici</h2>

Le CSS :

:target::before {
   content: "";
   display: block;
   margin-top: -60px;
   height: 60px;
}

[JS] Mémo pour le formatage des dates

Idée générale

Dans une UI, plutôt que de traiter les dates directement dans le HTML et de détecter la locale (la langue du visiteur) côté serveur, on peut laisser ce travail à JavaScript, et profiter de la langue du navigateur. Ceci fonctionne déjà très bien dans Firefox (57+) et bientôt, j’espère dans les autres navigateurs.

Ici je présente très brièvement comment ça fonctionne.

Principe

On donne une date au format RFC2822 (c’est à dire 2017-12-08T18:30:12+01:00 pour le 8 décembre 2017, à 18:30:12 UTC+1) et on utilise JS pour transposer ça dans une date lisible.

Le code

On crée un nouvel objet de date, à partir de la date que l’on a dans la base de donnée (ou d’une variable, ou autre) :

var d = new Date('2017-12-08T18:30:12+01:00')

On peut travailler avec (ajouter des jours, des secondes…) mais pour le transformer dans un format localisé, on utilise ça :

d.toLocaleString();

Qui donnera 08/12/2017 à 18:30:12 dans un navigateur réglé pour respecter le format français.
Le format US donnera 12/8/2017, 6:30:12 PM.
On peut forcer le format US dans un navigateur français en spécifiant la langue à l’aide de son code langue :

d.toLocaleString("en-US"); // États-Unis
d.toLocaleString("fr-FR"); // France
d.toLocaleString("ko-KR"); // Coréen

Le cas en coréen donnera 2017. 12. 8. 오후 6:30:12. On peut utiliser tous les codes languages usuels.

On peut aller beaucoup plus loin : il est possible d’afficher une date dans le format vendredi 8 décembre à 18:30. Pour ça, on utilise des options. Il s’agit d’un tableau que l’on passe en second paramètre de la fonction .toLocaleString

var options = {weekday: "long", month: "long", day: "numeric", year: "numeric", hour: "numeric", minute: "numeric"};

d.toLocaleString("fr-FR", options); // vendredi 8 décembre 2017 à 18:30
d.toLocaleString("de-DE", options); // Freitag, 8. Dezember 2017, 18:30
d.toLocaleString("ja-JP", options); // 2017年12月8日金曜日 18:30

Les options peuvent être manipulées également. Un élément que l’on ne met pas n’est pas pris en compte. Par contre, pour ne mettre que la date, n’indiquez rien sur les heures et les minutes :

d.toLocaleString("fr-FR", {weekday: "long",  month: "long",  day: "numeric", year: "numeric",}); // vendredi 8 décembre 2017
d.toLocaleString("fr-FR", {weekday: "long",  month: "long",  day: "numeric"});                   // vendredi 8 décembre
d.toLocaleString("fr-FR", {weekday: "short", month: "short", day: "numeric"});                   // ven. 8 déc.

Ceci n’est qu’un mémo. Voyez la documentation pour constater qu’il existe plein d’autres possibilités : choix du calendrier (ISO, Boudhiste, Islamique, Grégorien…), du style de numérotation (arabe, persan, romain…)…

Une petite dernière chose : s’il vous est nécessaire d’afficher plein de dates dans un format spécifique, vous pouvez appeler la fonction précédente dans une boucle. Néanmoins, ceci finirait par être très lent (les fonctions Date() sont relativement gourmandes en ressources).
Il est largement préférable d’utiliser la formulation suivante, qui utilise un sorte de variable et la bibliothèque Intl, qui est beaucoup plus rapide :

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

// une longue boucle de dates à afficher
for (var i = 0 ; i < 100 ; i++ ) {
     DateTimeFormat.format( d );
}

Pour donner une idée, ma page avec 350 dates à afficher prenait 370 ms pour s’afficher. Avec cette méthode, elle en prend 40. Sans aucune date, elle en prend 36. Donc pour un grand nombre de dates, cette méthode est 70 fois plus rapide.

Page créée un avril 2016. Mise à jour le vendredi 8 décembre 2017.
Adresse de la page : https://lehollandaisvolant.net/tuto/css-adv-1/