I have a dynamic width container div that contains constant width items. I'd like to be able to resize the container so that it only ever shows whole items, never cutting the item on the right in pieces.
JSFiddle
For example, a user's screen may render showing 5 items:
If that user were to start shrinking the width of their screen, as soon as the bar is no longer wide enough to hold 5 full items I would like it to shrink down to only showing 4 items.
Bad:
Good:
I know this is possible to achieve by using CSS3 media queries, but I'd like to avoid writing a different breakpoint for every single different number of elements. I'd also like to avoid using a javascript resize
event handler, though I am not sure if this is possible without it.
This solution is based off a modification to another solution for a similar problem I gave elsewhere.
Here is the fiddle.
It involves a complex relationship of overlapping pseudo-elements to create the borders, which can cause the solution to have certain limitations on what may or may not be able to be done within it (complex backgrounds would be an issue, as well as a necessity for certain positioning aspects). Nevertheless, it functions in the given case.
A Bit of Explanation
Essentially, each .item
element is building its own section of top/bottom borders using both the :after
and :before
elements, the former tied to the .itemContainer
, the latter tied to the .item
itself (the :before
is needed to create the last bit of border at the end of the row). Additionally, the :before
is also creating the "flexible" position of the right border to give it the responsiveness needed when an element shifts out of view. This is why the :before
must be related to the .item
itself, and also why each :after
element's background must be used to "hide" the right border of the preceding :before
element.
Since we don't know via css the "count" at any given point as to which element is the "last" in the display, all the :before
elements must be displayed, but we don't want right borders for them all, hence why the :after
needs to cover them. As an element shifts down to the next line, its :after
no longer covers the right border of what has now become the last displayed element, revealing that border to be used as the "right" border of the whole group.
HTML (Matching your original fiddle)
<div class="itemBar">
<div class="itemContainer">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
CSS of main items
.itemBar {
display: inline-block;
width: 50%; /* some width can be set, does not need to be this */
}
.itemContainer {
position: relative; /* :after pseudo-elements are positioned off this */
z-index: 1; /* needed for pseudo-element interaction */
overflow: hidden;
display: inline-block;
max-height: 68px;
width: 100%;
border-left: 1px solid black; /* left border is supplied by this */
}
.item {
width: 60px;
height: 62px;
display: inline-block;
margin: 2px;
border: 1px solid black;
/* NOTE: CANNOT be given positioning */
}
CSS of Pseudo Elements
.item::after {
content: '';
position: absolute; /* will position off itemContainer */
z-index: -1; /* push it to the background */
top: 0; /* set it to top of itemContainer */
bottom: 0; /* set it to bottom of itemContainer */
margin-left: -100%; /* shove it past the far left edge of itemContainer */
/* next, use padding to bring it back to its position at the end
of the text string of .item */
padding-left: 100%;
/* next, add enough padding on the right to compensate for the right
padding, right margin, and right border of .item */
padding-right: 3px;
/* next, create the top and bottom border of "container",
in conjunction with the :before; so this is a pseudo-border for
.itemContainer being created by the .item elements */
border-top: 1px solid black;
border-bottom: 1px solid black;
background: #fff; /* hide other :before borders */
}
.item:before { /* make right border */
content: '';
padding-top: 66px; /* give it .itemContainer height minus border heights */
width: 100%;
margin-top: -3px; /* .item top margin + border width */
margin-left: -100%; /* pull the text in .item back into position */
margin-right: 0;
/* next, push this behind the background with an even lower z-index
to hide it if it is not the right most element beign used to
form the right border */
z-index: -2;
float: right; /* get the before element to the right */
position: relative; /* needs to be adjusted in position */
right: -4px; /* move it same as padding-right of the after element */
display: block; /* give it a display */
/* next, use it to build the fake right border and also the fake
final top/bottom borders of the of itemContainer */
border-right: 1px solid black;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
Remove:
white-space: nowrap;
from .itemBar
and add
text-align:justify;
to .itemContainer
This way, other items which does not fit there will fall to next line yet the space will be distributed equally.
Demo: http://jsfiddle.net/rDxRt/4/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With