Подтвердить что ты не робот

Сделать последний элемент оставшейся шириной с упаковкой (и с поддержкой IE9)

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

Я уже встречал несколько вопросов по аналогичной теме, так как решения обычно используют элементы float: left для выравнивания их по строке, а overflow: hidden - для последнего элемента, что заставляет автоматически занимать оставшееся пространство. Эти решения работают отлично в одной строке, но они перестают работать, когда есть достаточно элементов до последнего элемента, которые они переносят на несколько строк. Затем "последний" элемент по-прежнему отображается в первой строке (если достаточно места), что делает его более не последним элементом.

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

Это довольно просто с упаковкой flexbox, так как вам просто нужно поместить flex: 0 auto на прежние элементы, чтобы они занимали все необходимое пространство (не принимая больше) и flex: 1 на последнем элементе чтобы он занял оставшуюся часть. Однако мне нужно поддерживать Internet Explorer 9, поэтому flexbox, к сожалению, не может быть и речи.

Вот как выглядит ситуация. При запуске фрагмента вы можете использовать кнопку "Toggle flex box" для переключения режима flexbox, который показывает, как он должен выглядеть.

* { box-sizing: border-box; }
.container {
  width: 300px;
  background: snow;
  padding: 5px;
  overflow: auto;
}
.element {
  float: left;
  margin: 2px 5px 2px 0;
  background: lavender;
}
.last {
  overflow: hidden;
}
.last > input {
  width: 100%;
}

/* Working solution with flex box */
.flex .container {
  display: flex;
  flex-wrap: wrap;
}
.flex .element {
  flex: 0 auto;
}
.flex .last {
  flex: 1;
}
<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>

  <div class="last"><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>

  <div class="last"><input type="text" /></div>
</div>

<button onclick="this.parentNode.classList.toggle('flex')">Toggle flex box</button>
4b9b3361

Ответ 1

Вот решение:

  • Добавить <div style="clear:left;margin-top:22px"></div> перед элементом .last *
  • Добавьте margin-top: -22px; в .last, где сумма примерно такая же, как высота строки
  • Если вы не хотите, чтобы он стал слишком маленьким, добавьте min-width в .last, и он будет работать так, как вы ожидали. Кроме того, min-width: 1px; требуется в классе, чтобы избежать получения 0 в особых случаях.

Протестировано и работает в IE8 +, Edge, Chrome, Opera, Firefox

* { box-sizing: border-box; }
.container {
  width: 300px;
  background: snow;
  padding: 5px;
  overflow: auto;
}
.element {
  float: left;
  margin: 2px 5px 2px 0;
  background: lavender;
}
.last {
  overflow: hidden;
  margin-top: -22px;
  min-width: 1px;
}
.last > input {
  width: 100%;
}
.container .unwrap {
  clear: left;
  margin-top: 22px
}
<div class="container">
  <div class="unwrap"></div>
  <div class="last"><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="unwrap"></div>
  <div class="last"><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz boo</div>
  <div class="unwrap"></div>
  <div class="last" style="min-width: 100px">
    <input type="text" placeholder="min-width 100px" />
  </div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="unwrap"></div>
  <div class="last"><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo baaaaaaaaaaaaaaaaaaaaaaar</div>
  <div class="unwrap"></div>
  <div class="last"><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz Foo bar baz</div>
  <div class="unwrap"></div>
  <div class="last"><input type="text" /></div>
</div>

Ответ 2

Следующее решение не является совершенным, оно использует хаки позиции. Если вы отключите overflow: hidden;, вы увидите, что каждый <input> имеет ту же ширину, что и контейнер. Но с некоторыми визуальными настройками CSS он выглядит хорошо и может работать нормально.

Проблема 1:, если вы наберете больше символов, чем может визуально соответствовать, весь вид сдвинется влево.

Обходной путь 1: установить значение maxlength для <input> может помочь.

jsfiddle

* {
  box-sizing: border-box;
}
.container {
  width: 300px;
  background: snow;
  padding: 5px;
  position: relative;
  overflow: hidden; /*hide the overflow*/
}
span:after {
  content: "\00A0" /*just for one no-break space*/
}
input {
  width: 100%;
  position: absolute; /*without any top/right/bottom/left value*/
  border: 2px solid grey;
  border-width: 0 0 2px 0;
}
input:focus {
  outline: 0;
}
<div class="container">
  <span>FooFoo barFoo bar baz</span>
  <input type="text" />
</div>

<div class="container">
  <span>FooFoo barFoo bar bazFooFoo barFoo bar baz bla</span>
  <input type="text" />
</div>

Ответ 3

И для всех остальных, кому нравится работать с script, вот для вас тоже.

Просто добавьте data-calcattr к элементам, которые вы хотите, чтобы его свойство width установлено в width: calc(100% - ??px);, где ??px - его текущая ширина.

Боковое примечание:

script, скорее всего, потребуются корректировки, чтобы компенсировать будущие границы, поля и т.д., так что это служит больше как один из способов, как это сделать.

function calcattr() {
  var els = document.querySelectorAll('[data-calcattr]');
  // TODO: add switch to enable calc. different properties like
  //       get the remaining height as well, by checking the 
  //       data-calcattr value, i.e.: data-calcattr='height'
  for (i = 0; i < els.length; i++) {
    els[i].setAttribute("style", "width: 0");    
    var lpos = parseInt(els[i].getBoundingClientRect().left);
    var minw = parseFloat(getComputedStyle(els[i]).getPropertyValue('min-width'));
    var calcwidth = "calc(100% - " + (lpos + 2) + "px)";
    els[i].setAttribute("style", "width: " + calcwidth);
    if ((els[i].getBoundingClientRect().right - lpos) < minw) {
      els[i].setAttribute("style", "width: 100%");
    }
  }
}

window.onload = calcattr;

(function() {
  window.addEventListener("resize", resizeThrottler, false);
  var resizeTimeout;
  function resizeThrottler() {
    if ( !resizeTimeout ) {
      resizeTimeout = setTimeout(function() {
        resizeTimeout = null;
        actualResizeHandler();
      }, 66);
    }
  }
  function actualResizeHandler() {
    calcattr();
  }
}());
* { box-sizing: border-box; }
.container {
  width: 300px; //70%;
  background: snow;
  padding: 5px;
  overflow: auto;
}
.fullwidth {
  width: 100%;
}
.element {
  float: left;
  margin: 2px 5px 2px 0;
  background: lavender;
}
.last > input {
  width: 100%;
}
.last {
  float: left;
  min-width: 20px;
}
<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>

  <div class="last" data-calcattr><input type="text" /></div>
</div>

<div class="container">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>

  <div class="last" data-calcattr><input type="text" /></div>
</div>

<div class="container fullwidth">
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>
  <div class="element">Foo</div>
  <div class="element">Foo bar</div>
  <div class="element">Foo bar baz</div>

  <div class="last" data-calcattr><input type="text" /></div>
</div>