最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Make responsive container snap to elements as it resizes - Stack Overflow

programmeradmin9浏览0评论

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.

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.

Share Improve this question asked Sep 20, 2013 at 19:38 jbabeyjbabey 46.7k12 gold badges72 silver badges94 bronze badges 2
  • I think you're going to need something more plex, perhaps needing JS. Maybe there is a library out this that supports this. – TravisO Commented Sep 24, 2013 at 19:17
  • @Enzino same issue as Stefan Henze's answer. this causes a big empty space on the right side of the container. i want the container to shrink when it can't fit another element. – jbabey Commented Sep 24, 2013 at 20:00
Add a ment  | 

4 Answers 4

Reset to default 14 +250

Pure CSS (some limitations)

This solution is based off a modification to another solution for a similar problem I gave elsewhere.

Here is the fiddle.

It involves a plex 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 (plex 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 bee 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 pensate 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/rDxRt/4/

You could have the items in the container float. This way, they will float onto the next line if the container gets to small.

If you are lucky enough and know the height of the items, you can set the container to a fixed height and overflow: hidden to make the items that flow to the next line not show up.

jsfiddle example

Here's another solution that's little simpler than ScottS's. It doesn't require any positioning or use of ::before or ::after, instead relying on the fact that ::first-line doesn't stretch past the last visible item.

The markup

<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>

Note that this solution is sensitive to inter-element whitespace, so you need to remove newlines/spaces between consecutive .items. I've line-wrapped between the tag name and class above to make it bit more readable.

The CSS

.itemContainer {
    overflow: hidden;
    height: 70px; /* .item's offset height + margin */
    /* you can constrain the width of this any way you like */
}
.itemContainer::first-line {
    font-size: 70px; /* needs to be at least as big as .wrap height */
    line-height: 0px; /* fixes oddities in Firefox */
    background: black; /* "border" color */
}
.item {
    display: inline-block;
    vertical-align: text-top; /* this minimizes the font-size required  above */
    margin: 10px; /* include desired "border" size of .itemContainer */
    box-shadow: 0 0 0 9px white; /* spread = desired margin */
    background: white; /* needs an opaque background */
    /* reset font-size and line-height */
    font-size: 1rem;
    line-height: 1.5;
    /* set your item size and borders however you like */       
    height: 50px;
    width: 50px;
    border: 1px solid red;
    box-sizing: border-box;
}
.item::first-line {
    font-size: 1rem; /* fix IE precedence */
}
.item + .item {
    margin-left: 0; /* inline-block margins don't collapse */
}

Result

Tested in Safari, Chrome, Firefox, and IE 11.

发布评论

评论列表(0)

  1. 暂无评论