截断一系列div中间的文本

cor*_*vid 14 css web

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

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

我总是希望前两个项目和最后两个项目可见,但是,...如果可能的话,我希望中间的所有内容都截断.也就是说,如果上面的字符串要溢出包含div,它应该具有以下结果

Corvid/Games/.../暗夜精灵/玛法里奥

我尝试过如下结构:

<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>
Run Code Online (Sandbox Code Playgroud)

这可以用CSS实现吗?

Bre*_*ody 11

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

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

  1. 一个简单的JavaScript实用工具
  2. 功能(无状态)React组件
  3. 有状态的React组件

1. JavaScript函数

在下面的例子中,我创建了一个truncateBreadcrumbs()接受3个参数的函数:

  • selector - 与要截断的元素匹配的CSS选择器
  • separator - 用于分隔元素的字符
  • segments - 要将字符串截断为的段数

它可以像:

truncateBreadcrumbs(".js-truncate", "/", 4);
Run Code Online (Sandbox Code Playgroud)

会找到所有带有类的元素.js-truncate并将内容截断为4个元素,...中间的分隔符,如:

Corvid / Games / ... / Night Elf / Malfurion
Run Code Online (Sandbox Code Playgroud)

也可以使用奇数段,例如5会产生:

Corvid / Games / World of Warcraft / ... / Night Elf / Malfurion
Run Code Online (Sandbox Code Playgroud)

如果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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

2.功能(无状态)反应组件

它(根据className属性)显示您正在使用React.如果是这种情况,我们可以创建一个简单的功能组件来截断文本.我已经把上面的代码变成了一个功能组件<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>
  )
}
Run Code Online (Sandbox Code Playgroud)

它可以用作:

<Truncate segments="4" separator="/">
  Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
</Truncate>
Run Code Online (Sandbox Code Playgroud)

这是完整的工作示例:

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"));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

3.有状态反应组件

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

用法与上述相同:

<Truncate segments="4" separator="/">
  Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
</Truncate>
Run Code Online (Sandbox Code Playgroud)

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

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"));
Run Code Online (Sandbox Code Playgroud)
.truncate {
  display: block;
  overflow: visible;
  white-space: nowrap;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


kej*_*eja 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; }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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

document.querySelectorAll("ul").forEach( el => el.childElementCount > 4 && el.classList.add("truncate") );
Run Code Online (Sandbox Code Playgroud)
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; }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

编辑:

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

.truncate div { display: inline-block; }
.truncate .mid { display: none; }
.truncate .first:after { content: "... /"; }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

或者如果你想要一个简单的纯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(" / ");
  }
});
Run Code Online (Sandbox Code Playgroud)
<div class="turncate">
Corvid / Games / World of Warcraft / Assets / Character Models / Alliance / Night Elf / Malfurion
</div>
Run Code Online (Sandbox Code Playgroud)