John Farkerson
John Farkerson

Reputation: 2632

React: Only display items which fully fit within flex box, and dynamically determine the number of items being displayed

The accepted answer here is very very close to being what I want. I have a vertical flexbox with items that have a fixed height. I would like as many items as possible to be displayed, but if an item overflows at all, it should be completely omitted. Try the code snippet in the linked answer to see what I mean. This effect can pretty easily be achieved by setting 'flex-wrap: wrap' and overflow hidden on the container.

However, I have one more issue: instead of just displaying these items, I also need to know HOW MANY are currently being displayed. I'm trying to show as many items as will fit, and then at the bottom a label will say "...and 5 more" if there are five items that don't fit, for example. What's the easiest way to go about this? I would love to avoid just dividing the height of the container by the heights of the individual items if possible. There must be a better way.

Upvotes: 6

Views: 1778

Answers (1)

John Farkerson
John Farkerson

Reputation: 2632

Did a lot of digging and finally came to a solution that I'm happy with. Use offsetLeft of the elements in order to determine which ones are wrapped. Do the same but with offsetTop for a horizontal flex box.

CSS:

.container {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
}

React:

const items = [...]; // Array of ReactElements

const [numberOfItemsDisplayed, setNumberOfItemsDisplayed] = useState(0);
const ref = useRef(null)

useLayoutEffect(() => {
    const cells = ref.current?.children;
    if (cells && cells.length > 0) {
        const startingOffset = cells[0].offsetLeft;
        let numItems = 0;
        for (let i = 0; i < cells.length; i++) {
            if (cells[i].offsetLeft > startingOffset) {
                break;
            }
            numItems++;
        }
        setNumberOfItemsDisplayed(numItems);
    }
}, []);

return <div class="container" ref={ref}>{items}</div>

Upvotes: 7

Related Questions