Marmelador
Marmelador

Reputation: 1017

Hide certain items when their flexbox container overflows

Consider this flexbox container with a few items. A few random ones tagged with .hide-on-overflow

<div id="container">
  <div class="item">Item 1 </div>
  <div class="item hide-on-overflow">Item 2 </div>
  <div class="item hide-on-overflow">Item 3 </div>
  <div class="item ">Item 4 </div>
  <div class="item hide-on-overflow">Item 5 </div>
</div>

CSS to set this up as a flexbox row

#container {
  display: flex; 
}

.item {
  flex-shrink: 0;
}

.hide-on-overflow { }

Now I would like the items tagged with .hide-on-overflow to disappear when #container begins to overflow. The question is not how to hide them (I would use display: none;), but how to detect the container overflowing. This is intended as a flexible solution instead of using a media query on the entire window width (thus estimating when the container would overflow)

Upvotes: 1

Views: 381

Answers (1)

touchmarine
touchmarine

Reputation: 2058

I do not think this can be done with CSS. But I would do it in JavaScript using the Intersection Observer API. Note that this an example of just one way to do this, there are possibly much better ways.

const container = document.querySelector("#container")
// elements to hide when overflowing
const elements = document.querySelectorAll(".hide-on-overflow")
// lastItem with which we detect if we are overflowing
const lastItem = container.lastElementChild

const observer = new IntersectionObserver(callback, {
    root:       null,  // intersect with viewport
    rootMargin: '0px', // no margin when computing intersections
    threshold:  1.0,   // execute callback when every pixel is visible/hidden
})
observer.observe(lastItem)

let breakpoint = 0 // breakpoint where we started to overflow
// Intersection Observer callback, which is called when no pixels of lastItem
// are visible anymore.
function callback(entries, observer) {
    for (const entry of entries) {
        if (!entry.isIntersecting) {
            // Save current viewport width when we hide the elements.
            breakpoint = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
            for (const element of elements) {
                element.style.display = "none"
            }
        }
    }
}

window.onresize = function() {
    // If we resize before the breakpoint is set, do nothing.
    if (breakpoint === 0) {
        return
    }

    // Viewport width
    vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
    if (vw > breakpoint) {
        for (const element of elements) {
            element.style.display = ""
        }
    }
}

Below is a simple summary of what this does. To best understand it, make sure to know the basics of Intersection Observer and read the code.

Using Intersection Observer we observe whether lastItem (last item in #container) is visible depending on the viewport. If it is not, it means we are overflowing. When this happens, we save the viewport width and hide the hide-on-overflow elements.

Then using a resize event listener, we display the hide-on-overflow elements when the current viewport width is larger than the viewport width of when we started overflowing.

We cannot use Intersection Observer for both ways as the lastItem is no longer visible when we hide it and the callback is not called back anymore. That is why we use the breakpoint variable and the resize event listener.

Viewport width is retrieved like it is described in javascript - How to get browser viewport dimensions?.

Upvotes: 1

Related Questions