Можно ли сделать строковые элементы встроенными блоками переносом строк, как встроенный текст? - PullRequest
4 голосов
/ 17 июня 2020

CSS display: inline элементы с текстом в них «перетекают» в конец строки, а затем переходят к следующей строке, когда контейнер для них недостаточно велик.

inline text wrapping at container boundry

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

Если я используйте display: inline-block, это позволяет мне контролировать ширину блока, но затем предотвращает разрыв строки.

inline-block elements not wrapping at container boundry

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

Я рассмотрел два обходных пути, которые дают мне желаемый визуальный результат:

Correct layout via work arounds

  1. Установите моноширинный шрифт и заполните встроенные элементы рассчитанным правильным количеством текстовых символов, которые затем скрываются. У этого есть проблемы с тем фактом, что текст все еще существует и его можно выбрать на странице.
  2. Используйте javascript, когда элемент встроенного блока должен переноситься и вводить два блока меньшего расчетного размера, один для первого раздела и один на остаток. Это очень медленно при работе с большим количеством этих блоков.

Ни одно из этих решений не очень хорошее, и если это возможно чисто CSS, я очень хочу узнать, как это сделать.

Вот ссылка на примеры на скриншотах, отображаемых в интерактивном режиме:

https://jsfiddle.net/n3wzd29v/

let tags = document.getElementsByClassName("js");

let currentLeft = 0;
for (let i=0; i<tags.length; i++) {
    let tag = tags[i];
    
    // dont know how to get the "width this would be if it was not inline cleanly".
    tag.style.display="inline-block";
    let requestedWidth = tag.offsetWidth;
    tag.style.display="inline";

    let parentWidth = tag.parentNode.offsetWidth;
    
    console.log("requestedWidth:" + requestedWidth);
    console.log("parentWidth:" + parentWidth);
    console.log("currentLeft:" + currentLeft);

    
    //FIXME: handle spanning over arbitary number of lines.
    if ((currentLeft + requestedWidth) < parentWidth) {
    	// Got plenty of space for this element on this line so just make it a inline-block
      tag.style.display = "inline-block";
      currentLeft += requestedWidth;
      console.log("Just setting inline-block, its wide enough");
    } else {
    	// This element needs breaking up into two elements, one for the part that fits on this
      // line, and another for the remainder on the next line.
      let part1Width = parentWidth - currentLeft;
      let part1 = document.createElement("span");
      part1.style.display = "inline-block"
      part1.style.width = part1Width + "px";
			part1.style.verticalAlign = "top";
      part1.style.height = "16px";

      let part2Width = requestedWidth - part1Width;
      let part2 = document.createElement("span");
      part2.style.display = "inline-block"
      part2.style.width = part2Width + "px";
			part2.style.verticalAlign = "top";
      part2.style.height = "16px";
      
      currentLeft = 0 + part2Width;
      
      tag.appendChild(part1);
      tag.appendChild(part2);
      console.log("Adding two fixed width children: " + part1Width + " " + part2Width);
    }
}
.container {
  width: 200px;
  background-color: lightblue;
}

span {
  word-break: break-all;
  font-family: monospace;
}

.bad {
  display: inline-block;
  height: 16px;
}

.stuffed {
  line-height:22px;
}

.js {
  height: 15px;  
  vertical-align: top;
}

.color1 {
  background-color: hsl(0, 80%, 50%);
}
.color2 {
  background-color: hsl(20, 80%, 50%);
}
.color3 {
  background-color: hsl(40, 80%, 50%);
}
.color4 {
  background-color: hsl(60, 80%, 50%);
}

.color1.stuffed {
  color: hsl(0, 80%, 50%);
}
.color2.stuffed {
  color: hsl(20, 80%, 50%);  
}
.color3.stuffed {
  color: hsl(40, 80%, 50%);
}
.color4.stuffed {
  color: hsl(60, 80%, 50%);  
}
<html>
<body>

<p>Inline elements with text in them "flow" over the end of a line 
and then carry on the next line when the container isn't large enough for them.
</p>

<div class="container">
<span class="good color1">These inline</span><span class="good color2">spans will wrap over</span><span class="good color3">the end of the container</span><span class="good color4">exactly how i would like them to</span>
</div>


<br/>

<p>
However i want to do the same layout but with block elements that i can set
the width of. Using inline-block allows me to control the block width however
it seems to prevent line-breaking from working.
</p>

<div class="container">
<span class="bad color1" style="width:96px"></span><span class="bad color2" style="width:160px"></span><span class="bad color3" style="width:192px"></span><span class="bad color4" style="width:256px"></span>
</div>


<p>
Obviously i can acheieve something of the same behaviour by stuffing my inline
elements with hidden text, but that seems sub-optimal
</p>
<div class="container">
<span class="stuffed color1">████████████</span><span class="stuffed color2">████████████████████</span><span class="stuffed color3">████████████████████████</span><span class="stuffed color4">████████████████████████████████</span>
</div>

<p>
Finally, it is ofc also possible to fix this problem in javascript, but i'm tenative
to do layout in javascript when the browser already clearly have a well optimised 
layout engine for doing this on text already.
</p>

<div class="container">

<span class="js color1" style="width:96px"></span><span class="js color2" style="width:160px"></span><span class="js color3" style="width:192px"></span><span class="js color4" style="width:256px"></span>

</div>

<br/>
<br/>
<br/>

</body>
</html>

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Если это только для визуальной цели, то у вас может быть способ использовать элемент span, в котором вы применяете несколько градиентов.

Вот пример, который не требует пояснений:

.container {
  width: 200px;
  padding:5px;
  background-color: lightblue;
  text-align:justify;
}

span {
  padding:0;
  background-image:
    linear-gradient(hsl(0, 80%, 50%),hsl(0, 80%, 50%)),
    linear-gradient(hsl(20, 80%, 50%),hsl(20, 80%, 50%)),
    linear-gradient(hsl(40, 80%, 50%),hsl(40, 80%, 50%)),
    linear-gradient(hsl(60, 80%, 50%),hsl(60, 80%, 50%));
  background-size:
    100px 100%, /* 1st color width = 100px */
    280px 100%, /* 2nd color width = 280px - 100px = 180px */
    450px 100%, /* 3rd color width = 450px - 280px = 170px */
    520px 100%; /* 4th color width = 520px - 450px = 70px */
  background-repeat:no-repeat;
}

/* we fill the span with content */
span::before,
span::after{
  content:"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █";
  color:transparent;
}
<div class="container">
<span ></span>
</div>

Вот еще одна очень безумная идея, в которой я буду полагаться на использование столбцов:

.container {
  height: 120px; /* this will define the visual width of container */
  column-width: 20px; /* this will define the visual height of rows*/
  display: inline-block;
  column-gap: 4px; /* gap between rows */
  /* this transformation will invert everything like we want */
  transform-origin: top left;
  transform: rotate(90deg) scaleY(-1);
  /**/
}

span {
  display: block;
}

span:hover {
  filter: invert(1);
}
 /* all the height below will define the visual width */
span:nth-child(1) {
  background: hsl(0, 80%, 50%);
  height: 80px;
}

span:nth-child(2) {
  background: hsl(20, 80%, 50%);
  height: 100px;
}

span:nth-child(3) {
  background: hsl(40, 80%, 50%);
  height: 80px;
}

span:nth-child(4) {
  background: hsl(60, 80%, 50%);
  height: 200px;
}
<div class="container">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
1 голос
/ 17 июня 2020

НЕТ.

Я не думаю, что решение, которого вы пытаетесь достичь, является логичным. Если я правильно понял ваш вопрос, вы пытаетесь установить для элемента WIDTH несколько строк.

Обратите внимание на первую опцию «ПЛОХО». Вы хотите установить ширину для элемента с желтым фоном, например, а затем продолжить эту ширину на нескольких строках?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...