Des labels flottants en CSS pur #CodingPartyAtHome 💕🤣🤜🤛😎

Depuis que mon grand est en fac d'info, on a un nouveau sujet de conversation et j'ai ENFIN un interlocuteur dans le domaine à la maison !

Du coup, il arrive le weekend avec les TP qu'il a eus pendant la semaine et me pose des questions sur les difficultés qu'il a.

En ce moment, il commence PHP et CSS/HTML...

On me   nomme ?

Du coup, aujourd'hui, il travaillait sur la page de login pour le projet final, une todolist en PHP+HTML+CSS sans JS.

Il voulait faire des labels flottants parce qu'il avait vu que c'était joli... Comme il découvre le monde merveilleux du frontend, on s'y est mis à deux et on a improvisé un petit cours.

Il a appris les subtilités du ciblage, les pseudo éléments, l'usage de :not() et :has()...

Car OUI  on a réussi des labels flottants en pur CSS.png

TLDR;

Pour la page de démo: c'est par là.

Pour le code : c'est sur snippetvamp.

Petit résumé du problème

En gros, on veut que le label soit dans l'input, comme un placeholder, lorsque il est vide mais que le label reprenne une place normale lorsque l'utilisateur clique dans l'input pour le remplir.

Capture du 2024-03-17 15-15-33.png Capture du 2024-03-17 15-15-57.png
Pour ça, j'ai créé un label contenant un span avec le texte et l'input correspondant:
<label><span>Username</span>
    <input type="text" name="login" value="" placeholder=" " >
</label>

Ensuite, je déplace le span vers l'intérieur de l'input:

label span{
    position: relative;
    top:2em;
    left:24px;
    transition:all 500ms;/* et on fait une transition douce, merci*/
}

Puis on utilise :has() pour cibler le span du label contenant un input ayant le focus.

label:has(input:focus) span
{
    color:grey;
    top:0;
    left:0;
    transition:all 500ms;
}

À ce stade, quand l'utilisateur clique dans l'input, le label glisse vers le haut pour sortir de l'input.

Toutefois, le problème c'est que lorsque l'input perd le focus, le label revient à l'intérieur même si l'input a été complété... et les deux textes se chevauchent hideusement...

Et la,   c'est le drame

La logique voudrait qu'on cible alors le span du label contenant un input vide, genre avec input[value=""] ... sauf que ça ne marche pas car le fait de remplir un input ne modifie pas l'attribut value de la balise input...

Zut flute et   cacaboudin

Heureusement, on peut gruger...

Puisqu'on ne peut pas cibler un changement de l'attribut value, on peut cibler... le placeholder ! Enfin... styler en fonction de la visibilité du placeholder...

Ainsi, en utilisant :placeholder-shown, on peut ajouter une règle de ciblage au CSS précédent:

label:has(input:focus) span,
label:has(input:not(:placeholder-shown)) span
{
    color:grey;
    top:0;
    left:0;
    transition:all 500ms;
}

Et là, les plus observateurs d'entre-vous - qui se demandaient avec une angoisse et un mépris non dissimulés pourquoi j'avais collé un placeholder=" " dans mon HTML - comprennent l'astuce: si le placeholder est visible, c'est que l'input est vide...

Et ça marche, tout est supporté dans la plupart des navigateurs. En plus, c'est léger, ne demande pas une structure HTML alambiquée ou des règles CSS à la mords-moi le zboub...

Si ça peut servir, c'est cadeau

Note pour plus tard - The Zen of Python | Python.org

  • Préfère :
    • la beauté à la laideur,
    • l'explicite à l'implicite,
    • le simple au complexe
    • et le complexe au compliqué,
    • le déroulé à l'imbriqué,
    • l'aéré au compact.
  • Prends en compte la lisibilité.
  • Les cas particuliers ne le sont jamais assez pour violer les règles.
  • Mais, à la pureté, privilégie l'aspect pratique.
  • Ne passe pas les erreurs sous silence,
  • ... ou bâillonne-les explicitement.
  • Face à l'ambiguïté, à deviner ne te laisse pas aller.
  • Sache qu'il ne devrait [y] avoir qu'une et une seule façon de procéder,
  • même si, de prime abord, elle n'est pas évidente, à moins d'être Néerlandais.
  • Mieux vaut maintenant que jamais.
  • Cependant jamais est souvent mieux qu'immédiatement.
  • Si l'implémentation s'explique difficilement, c'est une mauvaise idée.
  • Si l'implémentation s'explique aisément, c'est peut-être une bonne idée.
  • Les espaces de nommage ! Sacrée bonne idée ! Faisons plus de trucs comme ça.
  • Texte d'origine

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one-- and preferably only one --obvious way to do it.
  • Although that way may not be obvious at first unless you're Dutch.
  • Now is better than never.
  • Although never is often better than -right- now.
  • If the implementation is hard to explain, it's a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Notes perso JS : usage de fetch()

    Je me note ici pour une prochaine fois parce que fetch n'est pas forcément très intuitif...

    2 façons d'utiliser fetch :

    la plus tendance et élégante : en enchaînant les then()

    fetch("index.php", { method: 'POST', body: formData })
      .then((response)=>{
            // on attend l'arrivée de la réponse et on la traite
            return response.text(); // ou response.json();
      })
      .then((text)=>{
             // on attend la fin du traitement de la réponse  et on en traite le contenu
           console.log(text);
      });

    la plus lisible (?) : avec async et await

    En gros, on crée une fonction asynchrone pour pouvoir utiliser les await.

    const fetchAPI = async(URL) => {
        const response = await fetch(URL);  // on attend l'arrivée de la réponse 
        const data = await response.json();  // on attend la fin du traitement de la réponse
        console.log(data)
    }
    fetchAPI("https://jsonplaceholder.typicode.com/todos/1")

    CSS - 5t3ph@front-end.social - I'm not sure enough folks realize that :is()/:where(), and :not() can check _ancestor_ and _previous sibling_ conditions.p:is(h2 + *) = paragraphs that are directly after an h2p:not(blockquote *) = paragraphs that are not within blockquotesI've used these techniques to simplify what would otherwise be rather unmaintainable selectors or may have previously required additional conditional classes.https://codepen.io/5t3ph/pen/KKEmMXR

    Pas mal cette astuce ! On peut avoir des conditions qui prennent en compte un élément parent ou un élément frère pour cibler un élément...

    • p:is(h2 + *) : seulement les paragraphes directement après un H2
    • p:not(blockquote *) : les paragraphes ne se trouvant pas dans des blockquotes...

    Ça peut carrément simplifier certains cas !

    SVG images are accepted but not sanitized · Issue #38 · berthubert/trifecta · GitHub

    Ménon... Mais dites-moi que c'est pas vrai...

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
      <circle cx="250" cy="250" r="210" fill="#fff" stroke="#000" stroke-width="8"/>
      <script type="text/javascript">alert(1);</script>
    </svg>

    Donc, si on colle du JS dans un SVG, le JS serait exécuté à l'affichage de l'image ?!

    Je teste ci-dessous avec l'exemple donné sur la page...

    omg.svg

    Sur mon site, l'image s'affiche normalement sans exécuter le code... par contre, si on ouvre l'image dans un autre onglet...

    Comment c'est possible de laisser un truc pareil dans la nature ?!

    Via https://sebsauvage.net/links/

    MediaRecorder : Bienvenue dans IE6/7/8

    En ce moment je bosse sur l'appli de documents/exercices que j'utilise en cours (cf https://warriordudimanche.net/article1686/lappli-documents-que-jai-codee-pour-taf pour mémoire).

    J'implémente toute une nouvelle rubrique et de nouveaux items afin de couvrir le spectre de l'expression orale de façon simple en ajoutant une sorte de labo de langue simplifié.En gros, je veux que le gamin puisse travailler à l'écoute de mots modèles mais également qu'il puisse s'enregistrer et se réécouter simplement et quelque soit le support...

    Les frontend qui me lisent voient de suite où ça va couiller (merci les copaines)

    Et oui, j'ai codé un truc qui fonctionne très bien, relativement propre et simple, facile à utiliser... sous Firefox, opera, vivaldi, chromium et sous Android.

    Hélas, ma solution utilise l'API MediaRecorder qui semble présenter des difficultés sous certains navigateurs, vous devinez lesquels...

    J'ai testé sur safari (enfin epiphany passque j'ai pas d'apple sous la pogne, tu te doutes...) et ça refuse de fonctionner, vu que MediaRecorder y est bloqué pour nôôôtre saicuritay...

    J'ai ensuite testé sur Edge avec mon portable et ça n'a pas fonctionné... toutefois, quand j'ai installé le deb d'Edge sur mon linux ⬇⬇⬇ ...

    Parfois, la vie vous envoie   des épreuves, vous savez...

    ...là, ça a fonctionné... Mais si ça marche, ça doit venir de Linux et pas de Edge (mauvaise foi inside )

    Donc en gros, on revient à des soucis de compatibilité avec le nouvel IE ?! (ouelcome to aoueur fanne tasse tique taïme meuchine !)

    Nous voila   revenus aux   joies d'Internet   Explorer ?!

    Pourtant, canIuse me dit que ça devrait fonctionner, même sous Safari:

    Capture du 2024-01-19 12-02-36.png

    Bon, avec le navigateur à la pomme, il y a bien une manip à faire dans la configuration avancée pour les devs qui pourrait débloquer la situation mais je me vois mal demander ça à des élèves qui éprouvent déjà des difficultés non négligeables à discerner la barre d'adresse ou qui peinent - la sueur au front - à taper une majuscule sans passer par la touche de verrouillage...

    Allez bouffer   vos morts   micro$oft   et apple
    Beut ouate        iz ze   solouchonne ?

    Donc, là, pour le moment, rien à faire dans l'immédiat pour utiliser simplement MediaRecorder avec safari ou Edge... j'ai donc opté pour une «solution» temporaire: en cas d'absence de l'API, j'ajoute une classe spécifique et je disable les recorders de la page, puis j'affiche un message d'avertissement avec des liens vers des navigateurs compatibles (firefox et forks en tête)...

    Après, j'ai bien trouvé un polyfill qui pourrait faire le job ( https://github.com/ai/audio-recorder-polyfill ) mais je ne l'ai pas testé, c'est un peu lourd à installer et pis... c'est pas moi qui l'ai fait...

    Si quelqu'un/e a une idée... je prends...

    (...)

    5 extraits CSS que tout développeur front-end devrait connaître en 2024

    Il y a de ces trucs en CSS quand même...

    Quelques notes perso ici...

    :has()

    styler le parent en fonction du nombre d'enfants

    Pour modifier une grid par exemple...

    main:has(> :nth-child(5)) {…}

    styler un élément selon l'état d'une checkbox

    Pour appliquer un darkmode

    html:has(#dark-mode:checked) {…}

    styler un form dont au moins un des items est invalide

    form:has(:user-invalid) .error {
      display: block;
    }

    L'imbrication CSS

    Et moi qui pensais que ce n'était réservé qu'à des sass & consorts !

    Voilà qui permettra de mieux organiser le code et le rendre plus lisible...

    /*AVANT*/
    .nesting {
      color: hotpink;
    }
    .nesting > .is {
      color: rebeccapurple;
    }
    .nesting > .is > .awesome {
      color: deeppink;
    }
    /* MAINTENANT*/
    .nesting {
      color: hotpink;
    
      > .is {
        color: rebeccapurple;
    
        > .awesome {
          color: deeppink;
        }
      }
    }

    [EDIT] Par contre, ce n'est pas forcément supporté par les navigateurs pas à jour... https://caniuse.com/css-nesting

    text-wrap:balance

    en gros, pour rendre plus homogène un texte sur plusieurs lignes... Attention, pas pour un texte de plus de 10 lignes apparemment.

    via https://bookmarks.ecyseo.net/?DunHiA

    Astuces & liens pour HTML, JS et toussa

    Petite liste tirée des RSS que je suis en ce moment.

    FormData API | 12 Days of Web

    Un petit résumé perso de cette page sur l'objet FormData en javascript.

    Créer un objet FormData à partir d'un formulaire

    let form = document.querySelector('form');
    let data = new FormData(form);

    Parcourir l'objet FormData

    C'est un itérable, donc on le parcourt avec un for et on récupère des ['clé','valeur']

    for (let entry of data) {
        console.log(entry);
    }

    ou

    for (let [key, value] of data) {
        console.log(key);
        console.log(value);
    }

    parcourir les clés / les valeurs

    for (let key of data.keys()) {
      console.log(key);
    }

    Get, set, update des valeurs

    GET

    Récupérer une valeur (sans rire ?!)

    • S'il y a un seul champ au nom de la clé: let title = data.get('title')
    • S'il y en a plusieurs : let titles = data.getAll('title');

    SET

    Ajouter une nouvelle valeur à l'objet FormData

    data.set('date', '2022-12-25');

    APPEND

    Ajouter une valeur supplémentaire à une entrée existante (si l'entrée n'existe pas, elle est créée)

    data.append('tags', 'vacations');

    Divers

    Tester si un champ existe

    let hasID = data.has('id');

    Récupérer un tableau

    • des clés : let arr = Array.from(data.keys());
    • des valeurs : let arr = Array.from(data.values());

    Convertir un FormData en objet clés valeurs

    Si votre FormData ne contient pas plusieurs champs ayant le même nom.

    let serialized = Object.fromEntries(data);

    Sinon, on paufine

    let obj = {};
    for (let [key, value] of data) {
        if (obj[key] !== undefined) {
            if (!Array.isArray(obj[key])) {
                obj[key] = [obj[key]];
            }
            obj[key].push(value);
        } else {
            obj[key] = value;
        }
    }

    Une fois converti en objet, on peut le transformer en JSON

    let stringified = JSON.stringify(obj);

    How can I listen for a click outside of an element in JavaScript? - 30 seconds of code

    Pour écouter un click hors d'un élément, on utilise l'event delegation: on vérifie si la target de l'event correspond ou pas à l'objet qu'on veut.

      document.addEventListener('click', e => {
        if (!element.contains(e.target)) callback();
      });
    };
    onClickOutside('#my-element', () => console.log('Hello'));
    // Will log 'Hello' whenever the user clicks outside of #my-element

    How can I listen for an event only once in JavaScript? - 30 seconds of code

    Tiens, je ne savais pas qu'addEventListener acceptait des paramètres en option, en particulier un { once : true} qui permet de n'exécuter la fonction attachée à l'évènement qu'une seule fois...

    
    const listenOnce = (el, evt, fn) =>
      el.addEventListener(evt, fn, { once: true });
    
    listenOnce(
      document.getElementById('my-btn'),
      'click',
      () => console.log('Hello!')
    ); // 'Hello!' will only be logged on the first click
    Fil RSS des articles