Сделать адаптивную привязку контейнера к элементам при изменении размера

У меня есть контейнер div с динамической шириной, содержащий элементы постоянной ширины. Я хотел бы иметь возможность изменять размер контейнера, чтобы он отображал только целые предметы, никогда не разрезая предмет справа.

JSFiddle

Например, экран пользователя может отображать 5 элементов:

enter image description here

Если бы этот пользователь начал уменьшать ширину своего экрана, как только панель уже не будет достаточно широкой, чтобы удерживать 5 полных элементов, я бы хотел, чтобы она уменьшалась до 4 пунктов.

Плохо:

enter image description here

Хорошо:

enter image description here

Я знаю, что этого можно достичь с помощью мультимедийных запросов CSS3, но я бы хотел избежать написания другой точки останова для каждого отдельного количества элементов. Я также хотел бы избежать использования обработчика событий javascript resize, хотя я не уверен, что это возможно без него.

Ответ 1

Чистый CSS (некоторые ограничения)

Это решение основано на модификации другого решения для аналогичной проблемы, которую я дал в другом месте.

Вот скрипка.

Он включает сложную взаимосвязь перекрывающихся псевдоэлементов для создания границ, что может привести к тому, что решение будет иметь определенные ограничения на то, что может или не может быть выполнено внутри него (сложный фон будет проблемой, а также как необходимость для определенных аспектов позиционирования). Тем не менее он функционирует в данном случае.

Немного пояснения

По существу, каждый элемент .item создает собственный раздел верхних/нижних границ, используя как элементы :after, так и :before, первый привязан к .itemContainer, последний привязан к самому .item (для создания последнего бит границы в конце строки требуется :before). Кроме того, :before также создает "гибкую" позицию правой границы, чтобы дать ей необходимую реакцию, когда элемент смещается вне поля зрения. Вот почему :before должен быть связан с самим .item, а также почему каждый элемент фона :after должен использоваться для "скрытия" правой границы предыдущего элемента :before.

Поскольку мы не знаем через css "count" в любой заданной точке относительно того, какой элемент является "последним" на дисплее, все элементы :before должны отображаться, но нам не нужны правильные границы для них всех, поэтому почему :after должен их покрыть. Когда элемент переходит к следующей строке, его :after больше не покрывает правую границу того, что теперь стало последним отображаемым элементом, показывая, что граница используется как "правая" граница всей группы.

HTML (соответствие вашей оригинальной скрипке)

<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 основных элементов

.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 псевдоэлементов

.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;
}

Ответ 2

Удалить

white-space: nowrap;

из .itemBar

и добавьте

text-align:justify;

в .itemContainer

Таким образом, другие предметы, которые не подходят там, будут падать на следующую строку, но пространство будет равномерно распределено.

Демо: http://jsfiddle.net/rDxRt/4/

Ответ 3

У вас могут быть элементы в контейнере. Таким образом, они будут плавать на следующей строке, если контейнер попадет на небольшой.

Если вам повезло и знаю высоту элементов, вы можете установить контейнер на фиксированную высоту и overflow: hidden, чтобы элементы, которые текут на следующую строку, не отображались.

Пример jsfiddle

Ответ 4

Здесь другое решение, которое немного проще, чем ScottS. Он не требует никакого позиционирования или использования ::before или ::after, вместо этого полагаясь на то, что ::first-line не растягивается мимо последнего видимого элемента.

Разметка

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

Обратите внимание, что это решение чувствительно к межэлементному пробелу, поэтому вам нужно удалить новые строки/пробелы между последовательными .item s. Я обернул строку между именем тега и классом выше, чтобы сделать его более читаемым.

CSS

.itemContainer {
    overflow: hidden;
    height: 70px; /* .item 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 */
}

Результат

Снимок экрана из Safari, ограниченный только 8 из 10 элементов

Протестировано в Safari, Chrome, Firefox и IE 11.