有趣的问题-很遗憾我无法看到一个可靠的仅基于CSS的解决方案。也就是说,除非可以编辑HTML结构,即使这样也会有一些硬编码,我认为并没有可靠的CSS-only解决方案。
不过,以下是3个潜在的解决方案:
- 一个简单的JavaScript实用函数
- 一个功能性(无状态)React组件
- 一个带有状态的React组件
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
属性,您正在使用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>
)
}
它可以用作:
<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. 一个有状态的React组件
我们也可以创建一个有状态的组件来响应屏幕/元素的宽度。这是一个对该想法的简单实现 - 一个组件测试元素的宽度并在必要时进行截断。理想情况下,组件只截断必要的部分,而不是固定数量的段落。
使用方法与上面相同:
<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>