Усекать текст в середине ряда divs

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

Corvid/ Игры/ World of Warcraft/ Активы/ Модели символов/ Альянс/ Ночной эльф/ Малфурион

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

Corvid/ Игры/.../ Ночной эльф/ Малфурион

Я пробовал сделать структуру следующим образом:

<div className={styles.container}>
  <div className={styles.first}>
    {/** Contains first two items */}
  </div>
  <div className={styles.truncate}>
    {/** N arbitrary path items */}
  </div>
  <div className={styles.last}>
    {/** Last two items */}
  </div>
</div>

Можно ли это сделать с помощью CSS?

Ответы

Ответ 1

Интересная проблема - к сожалению, я не вижу надежного решения только для CSS. То есть, если структура HTML не может быть отредактирована, и даже тогда будет некоторая жесткая кодировка, я не верю, что есть надежное решение только для CSS.

Однако здесь есть 3 потенциальных решения:

  • Простая функция служебной программы JavaScript
  • Функциональный (безгосударственный) компонент реагирования
  • Компонент с учетом состояния

1. Функция JavaScript

В приведенном ниже примере я создал функцию truncateBreadcrumbs(), которая принимает 3 параметра:

  • selector - селектор CSS, соответствующий элементам, которые вы хотите усекать
  • separator - символ, используемый для разделения элементов
  • segments - количество сегментов, которые вы хотите усечь строку, чтобы

Его можно использовать как:

truncateBreadcrumbs(".js-truncate", "/", 4);

который найдет все элементы с классом .js-truncate и усечет содержимое до 4 элементов с разделителем ... в середине, например:

Corvid / Games / ... / Night Elf / Malfurion

Также могут использоваться нечетные числа сегментов, например 5 будет генерировать:

Corvid / Games / World of Warcraft / ... / Night Elf / Malfurion

Если аргумент segment равен или больше числа элементов, не происходит усечения.

И вот полный рабочий пример:

function truncateBreadcrumbs(selector, separator, segments) {
  const els = Array.from(document.querySelectorAll(selector));

  els.forEach(el => {
    const split = Math.ceil(segments / 2);
    const elContent = el.innerHTML.split(separator);

    if (elContent.length <= segments) {
      return;
    }

    el.innerHTML  = [].concat(
      elContent.slice(0, split),
      ["..."],
      elContent.slice(-(segments-split))
    ).join(` ${separator} `);
  });
}

truncateBreadcrumbs(".js-truncate--2", "/", 2);
truncateBreadcrumbs(".js-truncate--4", "/", 4);
truncateBreadcrumbs(".js-truncate--5", "/", 5);
<div class="js-truncate--2">Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion</div>

<div class="js-truncate--4">Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion</div>

<div class="js-truncate--5">Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion</div>

<div class="js-truncate--4">Corvid / Games / Night Elf / Malfurion</div>

Ответ 2

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

ul { list-style-type: none; }
ul li { display: none; }
ul li:nth-last-child(n+2):after { content: " / "; }
ul li:nth-child(2):after { content: " / ... /"; }
ul li:nth-child(-n+2), ul li:nth-last-of-type(-n+2) { display: inline-block; }
<ul>
  <li><a href="#">Corvid</a></li>
  <li><a href="#">Games</a></li>
  <li><a href="#">World of Warcraft</a></li>
  <li><a href="#">Assets</a></li>
  <li><a href="#">Character Models</a></li>
  <li><a href="#">Alliance</a></li>
  <li><a href="#">Night Elf</a></li>
  <li><a href="#">Malfurion</a></li>
</ul>

Ответ 3

.enumeration > div{display:inline-block;font-size:150%}
.enumeration > div:after{content: " / ";color:blue;font-weight:bold;}
.enumeration > div:nth-child(n+3) {display:none;}
.enumeration > div:nth-child(n+2):after {content: " / ... / ";color: red;}
.enumeration > div:nth-last-child(-n+2) {display:inline-block;}
.enumeration > div:nth-last-child(-n+3):after{content:" / ";color:green;}
.enumeration > div:nth-last-child(-n+2):after{content:" / ";color:orange;}
.enumeration > div:last-child:after{content:""}

Немного сложно, но это только css и работает с любым размером списка.:) (Цвета и жирный шрифт - это просто, чтобы упростить просмотр, где применяется каждый селектор.)

  • отображение "/" после каждого элемента
  • Не показывать элементы после второго элемента
  • отобразите "/.../" после элемента из второго
  • отображать последние два элемента (они скрыты в 2)
  • отображение "/" после третьего элемента с конца
  • отображение "/" после второго элемента с конца
  • не отображаются "/" после последнего элемента.

.enumeration > div{display:inline-block;font-size:150%}
.enumeration > div:after{content: " / ";color:blue;font-weight:bold;} /*1*/
.enumeration > div:nth-child(n+3) {display:none;}/*2*/
.enumeration > div:nth-child(n+2):after {content: " / ... / ";color: red;}/*3*/
.enumeration > div:nth-last-child(-n+2) {display:inline-block;}/*4*/
.enumeration > div:nth-last-child(-n+3):after{content:" / ";color:green;}/*5*/
.enumeration > div:nth-last-child(-n+2):after{content:" / ";color:orange;}/*6*/
.enumeration > div:last-child:after{content:""}/*7*/
<h2>
More than 4 items list
</h2>
<div class="enumeration">
<div>item 1</div>
<div>item 2</div>
<div>item 3</div>
<div>item 4</div>
<div>item 5</div>
<div>item 6</div>
<div>item 7</div>
<div>item 8</div>
<div>item 9</div>
</div>

<h2>
4 items list
</h2>
<div class="enumeration">
<div>item 1</div>
<div>item 2</div>
<div>item 3</div>
<div>item 4</div>
</div>


<h2>
3 items list
</h2>
<div class="enumeration">
<div>item 1</div>
<div>item 2</div>
<div>item 3</div>
</div>


<h2>
2 items list
</h2>
<div class="enumeration">
<div>item 1</div>
<div>item 2</div>
</div>


<h2>
1 item list
</h2>
<div class="enumeration">
<div>item 1</div>
</div>

Ответ 4

Я выставил это доказательство концепции, используя псевдоэлементы и медиа-запросы. Запустите фрагмент на полной странице и измените ширину на менее 500 пикселей.

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

DIV.truncate::before {
  content: 'World of Warcraft / Assets / Character Models / Alliance';
}

@media (max-width: 500px) {
  DIV.truncate::before {
    content: '...';
  }
}

DIV.container {
  font-weight: bold;
  font-family: Arial, sans-serif;
  font-size: 14px;
  white-space: nowrap;
  overflow-x: auto;
}

DIV.container>DIV {
  display: inline-block;
}
<div class="container">
  <div class="first">Corvid / Games /</div>
  <div class="truncate"></div>
  <div class="last">/ Night Elf / Malfurion</div>
</div>

Ответ 5

Вы можете использовать комбинацию flex и truncate text:

.grid-container {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  width: 320px;
  margin-top: 30vh;
  margin-right: auto;
  margin-left: auto;
}

.grid-item {
  overflow: hidden;
  height: 1.5em;
  line-height: 1.5em;
}

.grid-item.expand {
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
  white-space: nowrap;
  text-overflow: ellipsis;
}
<div class="grid-container">
    <div class="grid-item" style="background: red">Corvid / Games </div>
    <div class="expand grid-item">/ World of Warcraft / Assets / Character Models / Alliance </div>
    <div class="grid-item" style="background: blue">/ Night Elf / Malfurion</div>
</div>

Ответ 6

Это использует методы, которые я получил от Lea Verou
См. https://codepen.io/HerrSerker/pen/JOaqjL для примера SCSS

ul.breadcrumb, ul.breadcrumb > li {
  list-style: none;
  margin: 0;
  padding: 0;
}
ul.breadcrumb > li {
  display: inline-block;
}

/* this adds the slash between the `<li>` */
ul.breadcrumb > li:not(:last-child):after {
  content: ' /';
}

/* this is the second `<li>`, but only if there are 5 or more `<li>` */
/* 5 = 2 + 4 - 1 */
ul.breadcrumb > li:nth-child(2):nth-last-child(n+4):after {
  content: ' / … /';
}
/* these are `<li>` No. 3 up to 3rd to last `<li>`, but only if there are 5 or more `<li>` */
/* 5 = 3 + 3 - 1 */

ul.breadcrumb > li:nth-child(n+3):nth-last-child(n+3) {
  display: none;
}
<ul class="breadcrumb">
  <li>Corvid</li>
</ul>

<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
</ul>

<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
</ul>

<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
  <li>Assets</li>
</ul>


<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
  <li>Assets</li>
  <li>Character Models</li>
</ul>

<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
  <li>Assets</li>
  <li>Character Models</li>
  <li>Alliance</li>
</ul>
<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
  <li>Assets</li>
  <li>Character Models</li>
  <li>Alliance</li>
  <li>Night Elf</li>
</ul>
<ul class="breadcrumb">
  <li>Corvid</li>
  <li>Games</li>
  <li>World of Warcraft</li>
  <li>Assets</li>
  <li>Character Models</li>
  <li>Alliance</li>
  <li>Night Elf</li>
  <li>Malfurion</li>
</ul>