截断一系列div中间的文本

时间:2017-08-08 13:14:50

标签: css web

我有一个组件,目前会呈现出如下所示的内容:

Corvid / 游戏 / 魔兽世界 / 资产 / 角色模型 / 联盟 / 暗夜精灵 / 玛法里奥

我总是希望前两个项目和最后两个项目可见,但是,如果可能的话,我希望中间的所有内容都截断为...。也就是说,如果上面的字符串要溢出包含的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实现吗?

6 个答案:

答案 0 :(得分:11)

有趣的问题 - 遗憾的是,我无法看到可靠的纯CSS解决方案。也就是说,除非可以编辑HTML结构,即使这样会有一些硬编码,我也不相信有一个可靠的CSS解决方案。

但是,这里有3个可能的解决方案:

  1. 一个简单的JavaScript实用程序函数
  2. 功能(无状态)React组件
  3. 有状态的React组件
  4. 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。功能(无状态)反应组件

    您正在使用React(基于className属性)。如果是这种情况,我们可以创建一个简单的功能组件来截断文本。我已经采用了上面的代码并将其转换为功能组件<Truncate />,它执行相同的操作:

    const Truncate = function(props) {
      const { segments, separator } = props;
      const split = Math.ceil(segments / 2);
      const elContent = props.children.split(separator);
    
      if (elContent.length <= segments) {
        return (<div>{props.children}</div>);
      }
    
      const newContent = [].concat(
        elContent.slice(0, split),
        ["..."],
        elContent.slice(-(segments-split))
      ).join(` ${separator} `);
    
      return (
        <div>{newContent}</div>
      )
    }
    

    可以用作:

    <Truncate segments="4" separator="/">
      Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
    </Truncate>
    

    这是完整的工作示例:

    const Truncate = function(props) {
      const { segments, separator } = props;
      const split = Math.ceil(segments / 2);
      const elContent = props.children.split(separator);
    
      if (elContent.length <= segments) {
        return (<div>{props.children}</div>);
      }
    
      const newContent = [].concat(
        elContent.slice(0, split),
        ["..."],
        elContent.slice(-(segments-split))
      ).join(` ${separator} `);
    
      return (
        <div>{newContent}</div>
      )
    }
    
    class App extends React.Component {
      render() {
        return (
          <div>
            <Truncate segments="2" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="4" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="5" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="4" separator="/">
              Corvid / Games / Night Elf / Malfurion
            </Truncate>
          </div>
        )
      }
    }
    
    ReactDOM.render(<App />, document.getElementById("app"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    <div id="app"></div>

    3。有状态反应组件

    我们还可以创建一个有状态的组件来响应屏幕/元素的宽度。这是一个非常粗略的想法 - 一个测试元素宽度并在必要时截断它的组件。理想情况下,组件只会根据需要截断,而不是截断到固定数量的段。

    用法与上述相同:

    <Truncate segments="4" separator="/">
      Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
    </Truncate>
    

    但区别在于组件测试容器宽度以查看段是否适合,如果不符合则文本被截断。单击“展开代码段”按钮可全屏查看演示,以便您可以调整窗口大小。

    class Truncate extends React.Component {
      constructor(props) {
        super(props);
        this.segments = props.segments;
        this.separator = props.separator;
        this.split = Math.ceil(this.segments / 2);
        this.state = {
          content: props.children
        } 
      }
      
      componentDidMount = () => {
        this.truncate();
        window.addEventListener("resize", this.truncate);
      }
      
      componentWillUnmount = () => {
        window.removeEventListener("resize", this.truncate);
      }
      
      truncate = () => {
        if (this.div.scrollWidth > this.div.offsetWidth) {
          const elContentArr = this.state.content.split(this.separator);
          this.setState({
            content: [].concat(
              elContentArr.slice(0, this.split),
              ["..."],
              elContentArr.slice(-(this.segments - this.split))
            ).join(` ${this.separator} `)
          })
        }
      }
    
      render() {
        return (
          <div className="truncate" ref={(el) => { this.div = el; }}>
            {this.state.content}
          </div>
        )
      }
    }
    
    class App extends React.Component {
      render() {
        return (
          <div>
            <Truncate segments="2" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="4" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="5" separator="/">
              Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
            </Truncate>
    
            <Truncate segments="4" separator="/">
              Corvid / Games / Night Elf / Malfurion
            </Truncate>
          </div>
        )
      }
    }
    
    ReactDOM.render(<App />, document.getElementById("app"));
    .truncate {
      display: block;
      overflow: visible;
      white-space: nowrap;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    <div id="app"></div>

答案 1 :(得分:6)

这是一种方法,只有在总共超过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>

解决方法可能是在选择器前面添加一个类,并且只有在超过4个项目时才会截断。 理想情况下,这将添加到渲染上,但也可以通过javascript添加它,如本例所示

document.querySelectorAll("ul").forEach( el => el.childElementCount > 4 && el.classList.add("truncate") );
ul { list-style-type: none; }
ul li { display: inline-block;}
ul li:nth-last-child(n+2):after { content: " / "; }
ul.truncate li { display: none; }
ul.truncate li:nth-child(2):after { content: " / ... /"; }
ul.truncate 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>

编辑:

不知道问题中是否有遗漏,但如果你不想改变你的结构,你也可以这样做:

.truncate div { display: inline-block; }
.truncate .mid { display: none; }
.truncate .first:after { content: "... /"; }
<div class="truncate">
  <div class="first">
    Corvid / Games / 
  </div>
  <div class="mid">
    World of Warcraft / Assets / Character Models / Alliance / 
  </div>
  <div class="last">
    Night Elf / Malfurion
  </div>
</div>

或者如果你想要一个简单的纯js函数

document.querySelectorAll(".turncate").forEach( el => {
  const parts = el.innerText.split(" / ");
  if(parts.length > 4){
    parts.splice(2, parts.length-4); //ensure we only have two first and last items
    parts.splice(2, 0, "..."); // add ... after 2 first items.
    el.innerText = parts.join(" / ");
  }
});
<div class="turncate">
Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
</div>

答案 2 :(得分: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并适用于任何列表大小。 :) (颜色和粗体只是为了更容易看到每个选择器的应用位置。)

  1. 在每个项目后显示“/”
  2. 不要在第二项
  3. 之后显示项目
  4. 在第二个
  5. 项目后面显示“/.../”
  6. 显示最后两项(隐藏在2中的项目)
  7. 在结束后的第三项之后显示“/”
  8. 在结束后的第二项之后显示“/”
  9. 在最后一项之后不显示“/”。
  10. .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>

答案 3 :(得分:2)

我已经使用伪元素和媒体查询提出了这个概念证明。请在整页上运行代码段并将宽度更改为小于500px。

我已经了解了媒体查询cannot apply to individual elements' widths,因此这可能不太有用,您必须根据浏览器的宽度将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>

答案 4 :(得分:2)

您可以使用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>

Flex将使中心元素宽度动态化,文本溢出将使其截断。 如果不是你想要的,请解释更多。

答案 5 :(得分:2)

这使用了我从Lea Verou获得的技巧 有关SCSS示例,请参阅https://codepen.io/HerrSerker/pen/JOaqjL

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>