Can flexbox detect when a flex item wraps?
Asked Answered
D

6

42

I am hoping to get rid of media queries and just use flexbox to solve my problem, but I'm not sure if this is possible. As the window gets smaller, I want the columns to shrink until they hit their minimum width. Once they hit that, the middle column will jump down for the wrap.

I think my question boils down to this- can flexbox detect when it wraps? If so, can I prompt it to change the order of the items on wrap with strictly CSS?

Here is a codepen of what I"m trying to accomplish using media queries.

.wrapper {
  display: flex;
  justify-content: center;
  margin: 5px;
  flex-wrap: wrap;
}

.red {
  background-color: red;
  height: 240px;
  margin: 5px;
  width: 500px;
  order: 0;
}

.yellow {
  background-color: yellow;
  margin: 5px;
  height: 240px;
  min-width: 240px;
  max-width: 400px;
  flex: 1;
  order: 1;
}

.blue {
  background-color: blue;
  height: 240px;
  margin: 5px;
  min-width: 350px;
  max-width: 400px;
  flex: 1;
  order: 2;
}

@media (max-width:1130px) {
  .yellow {
    order: 4;
  }
}
<div class="wrapper">
  <div class="red"></div>
  <div class="yellow"></div>
  <div class="blue"></div>
</div>
Deejay answered 14/4, 2017 at 23:43 Comment(0)
E
16

Can flexbox detect when a flex item wraps?

No.

In CSS, once the browser renders the page on the initial cascade, it doesn't reflow the document when an element wraps. As a result, parent elements don't know when their children wrap.

That's why containers don't shrink-to-fit their content after wrapping.

That's why you need media queries or JavaScript.

Here are some more details:

Exact answered 14/4, 2017 at 23:48 Comment(0)
C
12

I solved this by checking to see if the element offset is greater than 0, since wrapped items get 'flexed' below. Here's my function:

isHidden(id) {
    let el = document.getElementById(id);
    return (el.offsetTop > 0)
}
Cookson answered 8/2, 2019 at 2:50 Comment(0)
T
1

Additional to Citizens anwser, you can use the offset and compare it to the other elements:

window.onresize = function (event) {
    let blu= document.getElementById("blue");
    let yel= document.getElementById("yellow");
    if (blu.offsetTop > yel.offsetTop) {
        // do something;
    } else {
        // do something;
    }
}
Toboggan answered 20/11, 2019 at 8:30 Comment(0)
M
1

I noticed elements will typically wrap in relation to the first element. Comparing offset top of each element to the first element is a simpler approach. This works for wrap and wrap-reverse. (Probably won't work if elements use flex order)

var wrappers = $('.flex[class*="flex-wrap"]'); //select flex wrap and wrap-reverse elements

if (wrappers.length) { //don't add listener if no flex elements
    $(window)
        .on('resize', function() {
            wrappers.each(function() {
                var prnt = $(this),
                    chldrn = prnt.children(':not(:first-child)'), //select flex items
                    frst = prnt.children().first();

                chldrn.each(function(i, e) { $(e).toggleClass('flex-wrapped', $(e).offset().top != frst.offset().top); }); //element has wrapped
                prnt.toggleClass('flex-wrapping', !!frst.find('.flex-wrapped').length); //wrapping has started
                frst.toggleClass('flex-wrapped', !!!chldrn.filter(':not(.flex-wrapped)').length); //all are wrapped
           });
        })
        .trigger('resize'); //lazy way to initially call the above
}
.flex {
    display: flex;
}

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

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

.flex.flex-1 > * { /*make items equal width*/
    flex: 1;
}

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

.cc-min-width-200 > * { /*child combinator*/
    min-width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="flex flex-1 flex-wrap-reverse cc-min-width-200">
    <div>Hello</div>
    <div>There</div>
    <div>World</div>
</div>
Medic answered 23/8, 2020 at 22:8 Comment(0)
I
0

When I needed to do this, I used the below JS to detect if the header had grown larger than expected and added a class to it.

<script>
    function handleflexwrap() {
        if ($("#header").height() > 120) {
            $("#header").addClass("wrapped");
        } else {
            $("#header").removeClass("wrapped");
        }
    }
    //Check on load
    handleflexwrap();    

    //Check again on resize
    $(window).on('resize', function() {
        handleflexwrap();
    });
</script>

Then my CSS could re-order the wrapping divs:

#header.wrapped #navigation{
    order: 4;
}

#navigation was originally "order: 2", in the middle of three elements. On wrapping, it gets set to 4, so moves to the end of the three.

Iolenta answered 26/9, 2021 at 17:18 Comment(0)
D
0

Combining a few answers together, comparing to parent worked for me in more scenarios:

setIsHidden(ref?.current?.offsetTop - ref?.current?.parentElement?.offsetTop > 0)

Dentist answered 19/4, 2023 at 8:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.