65 votes

Comment détecter l'événement de retournement de flexibilité CSS

J'ai un conteneur flex avec des éléments à l'intérieur. Comment détecter l'événement de wrap flex ? Je souhaite appliquer un nouveau CSS aux éléments qui ont été enveloppés. Je suppose qu'il est impossible de détecter l'événement de wrap avec du pur CSS. Mais ce serait une fonctionnalité très puissante ! Je peux essayer de "capturer" cet événement de point de rupture par media query lorsque l'élément est enveloppé dans une nouvelle ligne/rangée. Mais c'est une approche terrible. Je peux essayer de le détecter par script, mais ce n'est pas non plus très bon.

Regardez l'image

Je suis très surpris, mais simplement $("#element").resize() ne fonctionne pas pour détecter les changements de hauteur ou de largeur du conteneur flex afin d'appliquer un CSS approprié aux éléments enfants. LOL.

J'ai trouvé que seul cet exemple de code jquery fonctionne jquery event listen on position changed

Mais c'est toujours terrible.

0 votes

Vous ne pouvez pas détecter l'enveloppe en CSS - que vous utilisiez flexbox ou float, vous feriez mieux avec les requêtes multimédias...

2 votes

Merci pour la réponse. Peut-être qu'il y a un moyen simple de le détecter par JS? Je pense qu'il est inutile de "capturer" le point de rupture/retournement flottant pour les éléments flexibles à travers une requête media.

1 votes

Qu'est-ce qui fait que les éléments se mettent en ligne - un redimensionnement de l'écran ? davantage d'éléments ajoutés ?

28voto

Brett DeWoody Points 3742

Voici une solution potentielle. Il peut y avoir d'autres pièges et cas particuliers que vous devez vérifier.

L'idée de base est de boucler à travers les éléments flex items et de tester leur position top par rapport au sibling précédent. Si la valeur top est plus grande (donc plus bas sur la page), alors l'élément a été enveloppé.

La fonction detectWrap renvoie un tableau d'éléments DOM qui ont été enveloppés, et pourrait être utilisée pour les styler comme désiré.

Idéalement, la fonction pourrait être utilisée avec un ResizeObserver (tout en utilisant window's resize event en tant que solution de rechange) comme un déclencheur pour vérifier l'enveloppement pendant le redimensionnement de la fenêtre ou lorsque des éléments dans la page changent en raison de scripts et d'autres interactions utilisateurs. Parce que la fenêtre de code de StackOverflow ne se redimensionne pas, cela ne fonctionnera pas ici.

Voici un CodePen qui fonctionne avec un redimensionnement de l'écran.

var detectWrap = function(className) {

  var wrappedItems = [];
  var prevItem = {};
  var currItem = {};
  var items = document.getElementsByClassName(className);

  for (var i = 0; i < items.length; i++) {
    currItem = items[i].getBoundingClientRect();
    if (prevItem && prevItem.top < currItem.top) {
      wrappedItems.push(items[i]);
    }
    prevItem = currItem;
  };

  return wrappedItems;

}

window.onload = function(event){
  var wrappedItems = detectWrap('item');
  for (var k = 0; k < wrappedItems.length; k++) {
    wrappedItems[k].className = "wrapped";
  }
};

div  {
  display: flex;
  flex-wrap: wrap;
}

div > div {
  flex-grow: 1;
  flex-shrink: 1;
  justify-content: center;
  background-color: #222222;
  padding: 20px 0px;
  color: #FFFFFF;
  font-family: Arial;
  min-width: 300px;
}

div.wrapped {
  background-color: red;
}

  A
  B
  C

4 votes

Merci pour votre aide! Je pense que cet extrait sera très utile pour tout le monde. Dans l'ensemble, je pense que flexbox a besoin d'une nouvelle propriété CSS comme "wrapped" ou quelque chose comme ça pour se débarrasser du spaghetti JS. De plus, cet extrait pourrait être amélioré. Par exemple, détecter instantanément tous les éléments enveloppés lorsque la page est chargée et n'a pas encore été redimensionnée. De plus, cela réécrit le style CSS précédent de chaque élément maintenant.

0 votes

Comment géreriez-vous le scénario suivant - disons que vous avez 3 articles. Le premier article prend toute la largeur. Ainsi, les 2ème et 3ème articles se trouvent sur la 2ème ligne. Le 3ème article serait-il considéré comme enveloppé ?

0 votes

Belle question. Le 3ème élément ne serait pas considéré comme enveloppé par rapport au 2ème élément. La meilleure façon dans ce cas est de vérifier si l'élément parent de la classe avec la propriété css "display: flex". Ensuite, vous pouvez vérifier chaque élément enfant qui a été enveloppé. J'ai besoin d'améliorer l'extrait.

9voto

mr.boris Points 448

Un peu d'amélioration sur un extrait de code jQuery dans ce but.

wrapped();

$(window).resize(function() {
   wrapped();
});

function wrapped() {
    var offset_top_prev;

    $('.flex-item').each(function() {
       var offset_top = $(this).offset().top;

      if (offset_top > offset_top_prev) {
         $(this).addClass('wrapped');
      } else if (offset_top == offset_top_prev) {
         $(this).removeClass('wrapped');
      }

      offset_top_prev = offset_top;
   });
}

0 votes

Si la direction de la flexbox est en colonne, alors changer les déclarations conditionnelles en : if (offset_top < offset_top_prev) {} else {}. De plus, si vous utilisez Bootstrap 4/5, le sélecteur doit être $('.d-flex > *') pour sélectionner tous les éléments flex.

4voto

Tropical Points 76

J'ai modifié le code de sansSpoon pour qu'il fonctionne même si l'élément n'est pas en haut absolu de la page. Codepen : https://codepen.io/tropix126/pen/poEwpVd

function detectWrap(node) {
    for (const container of node) {
        for (const child of container.children) {
            if (child.offsetTop > container.offsetTop) {
                child.classList.add("wrapped");
            } else {
                child.classList.remove("wrapped");
            }
        }
    }
}

Notez que le margin-top ne doit pas être appliqué aux éléments car il est pris en compte dans getBoundingClientRect et déclenchera l'application de la classe wrapped sur tous les éléments.

0 votes

Nous devrions utiliser for(of) au lieu de Array.prototype.forEach d'ailleurs. Utiliser .forEach introduit des fermetures et a de moins bonnes performances. De plus, lire et écrire dans le DOM dans une seule boucle est vraiment mauvais pour l'expérience utilisateur car le navigateur doit effectuer des opérations de mise en page entre chaque itération de la boucle. Au lieu de cela, utilisez deux boucles séparées : la première rassemble des informations de taille pour tous les éléments pertinents, la deuxième boucle modifie ensuite le DOM (comme basculer la classe wrapped) sans lire aucune propriété qui déclencherait une relayout.

0 votes

Oui, j'ai écrit ceci il y a un certain temps quand je n'étais pas conscient des inconvénients de Array.prototype.forEach(). Je devrais probablement réécrire ma solution.

0 votes

Très bien, vient de le mettre à jour.

2voto

sansSpoon Points 583

Je suis en train d'utiliser une approche similaire pour déterminer si un

  • a été enveloppé dans un dont l'affichage est défini sur flex.

    ul = document.querySelectorAll('.list');
    
    function wrapped(ul) {
    
        // boucle sur toutes les listes trouvées sur la page - Collection HTML
        for (var i=0; i 0 cela a été enveloppé.
                loc = li[j].offsetTop;
                if (loc > 0) {
                    li[j].className = 'enveloppé';
                } else {
                    li[j].className = 'non-enveloppé';
                }
            }
        }
    }

1voto

pbraswell352 Points 21

J'ai remarqué que les éléments s'enveloppent généralement par rapport au premier élément. Comparer le décalage vers le haut de chaque élément par rapport au premier élément est une approche plus simple. Cela fonctionne pour wrap et wrap-reverse. (Ne fonctionnera probablement pas si les éléments utilisent l'ordre flex)

var wrappers = $('.flex[class*="flex-wrap"]'); //sélectionner les éléments flex wrap et wrap-reverse

    if (wrappers.length) { //ne pas ajouter d'écouteur s'il n'y a pas d'éléments flex
        $(window)
            .on('resize', function() {
                wrappers.each(function() {
                    var prnt = $(this),
                        chldrn = prnt.children(':not(:first-child)'), //sélectionner les éléments flex
                        frst = prnt.children().first();

                    chldrn.each(function(i, e) { $(e).toggleClass('flex-wrapped', $(e).offset().top != frst.offset().top); }); //l'élément s'est enveloppé
                    prnt.toggleClass('flex-wrapping', !!prnt.find('.flex-wrapped').length); //l'enveloppement a commencé
                    frst.toggleClass('flex-wrapped', !!!chldrn.filter(':not(.flex-wrapped)').length); //tous sont enveloppés
               });
            })
            .trigger('resize'); //manière fainéante d'appeler initialement ce qui précède
    }

.flex {
    display: flex;
}

.flex.flex-wrap {
    flex-wrap: wrap;
}

.flex.flex-wrap-reverse {
    flex-wrap: wrap-reverse;
}

.flex.flex-1 > * { /*rendre les éléments de largeur égale*/
    flex: 1;
}

.flex > * {
  flex-grow: 1;
}

.cc-min-width-200 > * { /*enfant combinateur*/
  min-width: 200px;
}

    Hello
    There
    World

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X