Fermer

novembre 19, 2019

Créez vos propres panneaux de contenu en expansion et en soustraitance


À propos de l'auteur

Ben Frain est un développeur, un auteur et un conférencier occasionnel. Un responsable technique UI / UX sur bet365.com, ses livres “Responsive Web Design with HTML5 and CSS3”…
Plus d'informations sur
Ben
Frain

Dans UI / UX, un schéma courant qui a souvent besoin d’être est celui d’un simple panneau d’ouverture et de fermeture animé, ou «tiroir». Vous n’avez pas besoin d’une bibliothèque pour les créer. Avec quelques bases HTML / CSS et JavaScript, nous allons apprendre à le faire nous-mêmes.

Nous les appelons jusqu'ici un "panneau d'ouverture et de fermeture", mais ils sont également décrits comme des panneaux d'extension ou plus Simplement, élargir les panneaux.

Pour clarifier ce dont nous parlons, voyons l'exemple de CodePen:

Tiroir facile à afficher / masquer (Multiples) de Ben Frain sur CodePen .

Tiroir facile à afficher / cacher (Multiples) de Ben Frain sur CodePen .

C'est ce que nous Construisez ce court tutoriel.

Du point de vue de la fonctionnalité, il existe plusieurs façons de réaliser l’ouverture et la fermeture animées que nous recherchons. Chaque approche a ses avantages et ses inconvénients. Je vais partager les détails de ma méthode "aller à" en détail dans cet article. Examinons d'abord les approches possibles.

Approches

Il existe des variantes de ces techniques, mais grosso modo, les approches se divisent en trois catégories:

  1. Animation / transition de la hauteur ou ] max-height du contenu
  2. Utilisez transformation: translation: pour déplacer des éléments dans une nouvelle position, en leur donnant l’illusion d’une fermeture de panneau, puis en rendant à nouveau le DOM une fois la transformation terminée. avec les éléments dans leur position finale.
  3. Utilisez une bibliothèque qui fait une combinaison / variation de 1 ou 2.

Considérations sur chaque approche

Du point de vue des performances, utiliser une transformation est plus efficace que d'animer ou de faire une transition. la hauteur / hauteur maximale. Avec une transformation, les éléments en mouvement sont pixellisés et déplacés par le GPU. Il s'agit d'une opération simple et peu coûteuse pour un processeur graphique, de sorte que les performances ont tendance à être bien meilleures.

Les étapes de base de l'utilisation d'une approche de transformation sont les suivantes:

  1. Obtenir la hauteur du contenu à réduire.
  2. Déplacer le contenu. et tout ensuite par la hauteur du contenu à réduire en utilisant transform: translateY (Xpx) . Utilisez la transformation avec la transition de choix pour obtenir un effet visuel agréable.
  3. Utilisez JavaScript pour écouter l'événement transitionend . Quand il déclenche, affiche: néant le contenu, supprime la transformation et tout doit être au bon endroit.

Cela ne vous semble-t-il pas trop mauvais, n'est-ce pas?

Considérations relatives à cette technique, de sorte que j'ai tendance à l'éviter pour les mises en œuvre occasionnelles, sauf si la performance est absolument cruciale.

Par exemple, avec l'approche transform: translateY vous devez prendre en compte l'indice z des éléments. Par défaut, les éléments qui se transforment se trouvent après l'élément déclencheur du DOM et apparaissent donc au-dessus de ce qui les précède lorsqu'ils sont traduits.

Vous devez également prendre en compte le nombre d'éléments qui apparaissent après le contenu que vous souhaitez réduire dans le DOM. Si vous ne voulez pas de gros trous dans votre mise en page, il vous sera peut-être plus facile d'utiliser JavaScript pour envelopper tout ce que vous voulez déplacer dans un élément conteneur et le déplacer simplement. Gérable mais nous venons d'introduire plus de complexité! Cependant, c’est le genre d’approche que j’ai choisi pour déplacer les joueurs de haut en bas dans In / Out . Vous pouvez voir comment cela s'est fait ici .

Pour des besoins plus occasionnels, j'ai tendance à aller de l'avant avec la hauteur maximale du contenu. Cette approche ne fonctionne pas aussi bien qu'une transformation. La raison en est que le navigateur interpelle la hauteur de l'élément collapsant tout au long de la transition; cela entraîne beaucoup de calculs de mise en page qui ne sont pas aussi économiques pour l'ordinateur hôte.

Cependant, cette approche est gagnante du point de vue de la simplicité. L'avantage informatique susmentionné est que la redistribution par le DOM prend en charge la position et la géométrie de tout. Nous avons très peu de calculs à faire et le JavaScript nécessaire pour bien l'exploiter est relativement simple.

L'éléphant dans la pièce: détails et éléments récapitulatifs

Ceux qui ont une connaissance intime des éléments HTML sauront il existe une solution HTML native à ce problème sous la forme d'éléments de résumé et . Voici quelques exemples de balises:

Cliquez pour ouvrir / fermer     Voici le contenu qui est révélé en cliquant sur le résumé ...

Par défaut, les navigateurs fournissent un petit triangle d'affichage en regard de l'élément de résumé. cliquez sur le résumé pour afficher le contenu ci-dessous.

Bien, hey? Les détails prennent même en charge l’événement de basculement en JavaScript pour vous permettre de faire ce genre de choses pour exécuter différentes choses selon qu’il soit ouvert ou fermé (ne vous inquiétez pas si ce genre d’expression JavaScript vous semble étrange; nous ' J'y reviendrai plus en détail prochainement):

 details.addEventListener ("toggle", () => {
    détails.ouvert? thisCoolThing (): thisOtherThing ();
}) 

OK, je vais mettre fin à votre enthousiasme. Les détails et les éléments de résumé ne s'animent pas. Pas par défaut et il n'est actuellement pas possible de les ouvrir / fermer avec des CSS et JavaScript supplémentaires.

Si vous savez le contraire, j'aimerais beaucoup avoir tort.

Malheureusement, nous avons besoin d'un En ouvrant et en fermant l'esthétique, nous devrons nous retrousser les manches et faire le travail le plus simple et le plus accessible possible avec les autres outils à notre disposition.

Bien, avec les nouvelles déprimantes à portée de main, continuons à faire cette chose arrive.

Modèle de balisage

Le balisage de base va ressembler à ceci:

Tout le contenu ici     

Nous avons un conteneur externe pour emballer le expandeur et le premier élément est le bouton qui sert de déclencheur à l'action. Remarquez l'attribut type dans le bouton? J'inclus toujours que comme par défaut un bouton à l'intérieur d'un formulaire effectuera une soumission. Si vous perdez quelques heures à vous demander pourquoi votre formulaire ne fonctionne pas et que des boutons sont impliqués dans votre formulaire; assurez-vous de vérifier l'attribut type!

L'élément suivant après le bouton est le tiroir de contenu lui-même; tout ce que vous voulez cacher et montrer

Pour donner vie aux choses, nous allons utiliser les propriétés personnalisées CSS, les transitions CSS et un peu de JavaScript.

Logique de base

La logique de base est la suivante: [19659012] Laisser la page se charger et mesurer la hauteur du contenu.

  • Définissez la hauteur du contenu sur le conteneur en tant que valeur d'une propriété personnalisée CSS.
  • Cachez immédiatement le contenu en ajoutant un aria- caché: "vrai" lui attribue. L'utilisation de aria-hidden permet de s'assurer que la technologie d'assistance sait que le contenu est également masqué.
  • Câblez le CSS afin que la hauteur maximale de la classe de contenu soit la valeur de la propriété personnalisée. .
  • En appuyant sur notre déclencheur, la propriété cachée par aria est remplacée par la valeur vraie ou fausse, qui bascule à son tour la hauteur maximale du contenu entre 0 et la hauteur définie dans la coutume propriété. Une transition sur cette propriété fournit le flair visuel – ajustez au goût!
  • Note: Maintenant, ce serait un cas simple de basculement d'une classe ou d'un attribut si max-height: auto égalait la hauteur du contenu. Malheureusement, ce n'est pas le cas. Allez crier ça au W3C ici .

    Voyons comment cette approche se manifeste dans le code. Les commentaires numérotés indiquent les étapes logiques équivalentes à partir du code ci-dessus.

    Voici le code JavaScript:

     // Récupère l'élément contenant
    const conteneur = document.querySelector (". conteneur");
    // Obtenir du contenu
    const content = document.querySelector (". content");
    // 1. Obtenez la hauteur du contenu que vous souhaitez afficher / masquer
    const heightOfContent = content.getBoundingClientRect (). height;
    // Récupère l'élément déclencheur
    const btn = document.querySelector (". trigger");
    
    // 2. Définir une propriété personnalisée CSS avec la hauteur du contenu
    container.style.setProperty ("- containerHeight", `$ {heightOfContent} px`);
    
    // Une fois que la hauteur est lue et définie
    setTimeout (e => {
        document.documentElement.classList.add ("height-is-set");
        3. content.setAttribute ("aria-hidden", "true");
    }, 0);
    
    btn.addEventListener ("click", fonction (e) {
        container.setAttribute ("affichage de tiroir de données", container.getAttribute ("affichage de tiroir de données") === "true"? "false": "true");
        // 5. Basculer entre aria-hidden
        content.setAttribute ("aria-hidden", content.getAttribute ("aria-hidden") === "true"? "false": "true");
    }) 

    Le CSS:

     .content {
      transition: hauteur maximale 0.2s;
      débordement caché;
    }
    .content [aria-hidden="true"] {
      hauteur maximale: 0;
    }
    // 4. Définit height à la valeur de la propriété personnalisée
    .content [aria-hidden="false"] {
      hauteur maximale: var (- containerHeight, 1000px);
    } 

    Points importants

    Qu'en est-il des tiroirs multiples?

    Lorsque vous avez un certain nombre de tiroirs ouverts et masqués sur une page, vous devez les parcourir en boucle car ils auront probablement des tailles différentes.

    Pour gérer cela, nous devrons faire un querySelectorAll pour obtenir tous les conteneurs, puis réexécuter votre réglage de variables personnalisées pour chaque contenu dans un pourEach .

    This setTimeout

    J'ai une setTimeout avec une durée 0 avant de définir le conteneur à masquer. C'est sans doute inutile, mais je l'utilise comme une approche «ceinture et accolades» pour s'assurer que la page a été rendue en premier, de sorte que les hauteurs du contenu soient disponibles pour la lecture.

    Ne l'utilisez que lorsque la page est prête

    Si vous avez d’autres tâches en cours, vous pouvez choisir d’envelopper votre code de tiroir dans une fonction qui est initialisée au chargement de la page. Par exemple, supposons que la fonction tiroir ait été encapsulée dans une fonction appelée initDrawers . Nous pourrions le faire:

     window.addEventListener ("load", initDrawers); 

    En fait, nous ajouterons cela dans peu de temps.

    Attributs data- * supplémentaires sur le conteneur

    Il existe un attribut de données sur le conteneur externe qui est également basculé. Ceci est ajouté au cas où quelque chose doit changer avec le déclencheur ou le conteneur lorsque le tiroir s'ouvre / se ferme. Par exemple, nous souhaitons peut-être changer la couleur de quelque chose, ou révéler ou basculer une icône.

    Valeur par défaut de la propriété personnalisée

    Il existe une valeur par défaut définie pour la propriété personnalisée en CSS de 1000px . . C’est un peu après la virgule dans la valeur: var (- containerHeight, 1000px) . Cela signifie que si - containerHeight est foutu en quelque sorte, vous devriez toujours avoir une transition décente. Vous pouvez évidemment définir ce paramètre selon votre cas d'utilisation.

    Pourquoi ne pas simplement utiliser une valeur par défaut de 100000px?

    Etant donné que max-height: auto ne fait pas la transition, vous pouvez demandez-vous pourquoi vous ne choisissez pas simplement une hauteur définie supérieure à celle dont vous auriez besoin. Par exemple, 10000000px?

    Le problème avec cette approche est qu’elle passera toujours de cette hauteur. Si la durée de la transition est définie sur 1 seconde, la transition se déplacera à 10000000 pixels en une seconde. Si votre contenu ne fait que 50 pixels de haut, vous obtiendrez un effet d'ouverture / fermeture assez rapide!

    Opérateur ternaire pour les bascules

    Nous avons eu recours à un opérateur ternaire à quelques reprises pour modifier les attributs. Certains les détestent mais moi, et d'autres je les aime. Ils peuvent sembler un peu bizarres et un peu de «code golf» au début, mais une fois que vous vous êtes habitués à la syntaxe, je pense qu'ils sont plus simples à lire qu'un standard if / else.

    Pour les non-initiés, un opérateur ternaire est une forme condensée de if / else. Ils sont écrits pour que la chose à vérifier soit d'abord, puis le ? sépare ce qu'il faut exécuter si le contrôle est vrai, puis le : pour distinguer ce qui doit être exécuté si le contrôle est false.

     isThisTrue? doYesCode (): doNoCode (); 

    Notre attribut bascule le travail en vérifiant si un attribut est défini sur "true" et, dans l'affirmative, définissez-le sur "false" sinon , réglez-le sur "true" .

    Que se passe-t-il lors du redimensionnement d'une page?

    Si un utilisateur redimensionne la fenêtre du navigateur, il y a de fortes chances que les hauteurs de notre contenu changent. Par conséquent, vous souhaiterez peut-être réexécuter la définition de la hauteur des conteneurs dans ce scénario. Maintenant que nous envisageons de telles éventualités, il semble que le temps soit venu de revoir un peu les choses.

    Nous pouvons créer une fonction pour définir les hauteurs et une autre pour gérer les interactions. Ensuite, ajoutez deux auditeurs sur la fenêtre; l'un pour le chargement du document, comme indiqué ci-dessus, puis un autre pour écouter l'événement de redimensionnement.

    All Together

    Avec le chargement de page, plusieurs tiroirs et la gestion d'événements de redimensionnement, notre code JavaScript ressemble à ceci: [19659055] var conteneurs;
    fonction initDrawers () {
        // Récupère les éléments qui contiennent
        conteneurs = document.querySelectorAll (". conteneur");
        setHeights ();
        wireUpTriggers ();
        window.addEventListener ("resize", setHeights);
    }

    window.addEventListener ("load", initDrawers);

    fonction setHeights () {
        containers.forEach (container => {
            // Obtenir du contenu
            let content = container.querySelector (". content");
            content.removeAttribute ("aria-hidden");
            // Hauteur du contenu à afficher / masquer
            let heightOfContent = content.getBoundingClientRect (). height;
            // Définir une propriété personnalisée CSS avec la hauteur du contenu
            container.style.setProperty ("- containerHeight", `$ {heightOfContent} px`);
            // Une fois que la hauteur est lue et définie
            setTimeout (e => {
                container.classList.add ("height-is-set");
                content.setAttribute ("aria-hidden", "true");
            }, 0);
        });
    }

    fonction wireUpTriggers () {
        containers.forEach (container => {
            // Récupère chaque élément déclencheur
            let btn = container.querySelector (". trigger");
            // Obtenir du contenu
            let content = container.querySelector (". content");
            btn.addEventListener ("click", fonction (e) {
                container.setAttribute ("affichage de tiroir de données", container.getAttribute ("affichage de tiroir de données") === "true"? "false": "true");
                content.setAttribute ("aria-hidden", content.getAttribute ("aria-hidden") === "true"? "false": "true");
            });
        });
    }

    Vous pouvez également jouer avec CodePen ici:

    Tiroir facile à afficher / masquer (Multiples) de Ben Frain sur CodePen .

    Tiroir / exposition facile (Multiples) de Ben Frain sur CodePen .

    Résumé

    Il est possible d'affiner et de personnaliser encore davantage et plus de situations, mais la mécanique de base pour créer un tiroir d’ouverture et de fermeture fiable pour votre contenu devrait maintenant être à votre portée. J'espère que vous connaissez également certains des dangers. L'élément details ne peut pas être animé, max-height: auto ne fait pas ce que vous espériez, vous ne pouvez pas ajouter de manière fiable une valeur max-height massive et attendre tout le contenu. panneaux à ouvrir comme prévu.

    Pour réitérer notre approche ici: mesurez le conteneur, stockez sa hauteur en tant que propriété personnalisée CSS, masquez le contenu, puis utilisez une simple bascule pour passer de à hauteur maximale de 0 et la hauteur que vous avez stockée dans la propriété personnalisée.

    Ce n'est peut-être pas la méthode la plus performante, mais j'ai trouvé que, dans la plupart des situations, il est parfaitement adéquat et qu'il est relativement simple à mettre en œuvre.

     Smashing Editorial (dm, yk, il)




    Source link

    Revenir vers le haut