之前有个类似关于文字溢出的需求,但是又略有不同。具体需求如下图所示
要求使用angular
当文字超过指定的长度时会出现省略号加上More
当点击More的时候就显示全部的内容,点击Show less的时候就返回到第一张图所示
由于项目使用的是angular框架,所以就把这个小功能写成了directive,代码有点多,但是我会加上相关的注释,这样会清晰一点
首先介绍一下在HTML中的用法
textCut是这个指令的名字,传入的数值是你希望要从第几个数值截取,比如这个是从第25个截取
<p class="mb-2" textCut="25">Job posting templates allow you to save content and xxxxxxxxxxx
</p>
这个是完整的指令的封装
import { Directive, ElementRef, HostListener, Input, AfterViewInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[textCut]'
})
export class TextCutDirective implements AfterViewInit {
@Input('textCut') textCutSize: number; //这个就是从HTML中传入的截取文字的个数
private allContent: string; //这个变量代表标签中原本的所有的文字,比如这里的P标签
private contentEle: HTMLSpanElement;
private linkEle: HTMLSpanElement;
private isExpanded: boolean; //是否展开已经缩起来的文字
constructor(private el: ElementRef, private renderer: Renderer2) {
}
ngAfterViewInit() {//这个生命周期是说页面元素可以获取到的时候
this.waitUntilAddedToDom().then(() => {
if (this.shouldCollapse()) { // 如果需要折叠起来 就走这个逻辑
this.isExpanded = false;
this.allContent = this.el.nativeElement.innerText; //因为折叠会改变p标签的dom结构【this.el.nativeElement代表使用这个指令的元素,比如HTML中的p标签】,所以暂时把之前的内容存起来以备后边使用
this.collapseContent(); //折叠的主要逻辑
}
})
}
private expandContent() { // 点击...more会触发的事件
this.contentEle = this.getContentEle(this.allContent);
this.linkEle = this.getLinkEle('Show Less');
this.refreshContent();
}
private collapseContent() {// 点击Show less会触发的事件
this.contentEle = this.getContentEle(this.getContentCollapsed(this.allContent, this.textCutSize));
this.linkEle = this.getLinkEle('More');
this.refreshContent();
}
//这个方法是用来重新渲染P标签的DOM结构,让两个span标签去显示p里边的内容
private refreshContent() {
this.el.nativeElement.innerText = '';
this.renderer.appendChild(this.el.nativeElement, this.contentEle);
this.renderer.appendChild(this.el.nativeElement, this.linkEle);
}
//这个方法是用来动态生成一个新的span标签,并将相关的字符内容放进去
private getContentEle(contentDisplayed: string) {
let content: HTMLSpanElement;
content = this.renderer.createElement('span')
content.innerText = contentDisplayed;
return content;
}
//这个也是动态生成一个span标签用来放到上一个span的后边 【就是用来放More或者Show less】
private getLinkEle(text: string) {
let link: HTMLSpanElement;
link = this.renderer.createElement('span')
link.innerText = ' ' + text;
this.setLinkEleStyle(link);
this.addClickListenerToLinkEle(link);
return link;
}
//这个方法用来控制点击Show less和More的逻辑
private addClickListenerToLinkEle(link: HTMLSpanElement) {
this.renderer.listen(link, 'click', () => {
this.isExpanded = !this.isExpanded;
if (this.isExpanded) {
this.expandContent();
} else {
this.collapseContent()
}
});
}
//用来设置More,Show less的样式
private setLinkEleStyle(link: HTMLSpanElement) {
this.renderer.setStyle(link, 'color', '#287AB9');
this.renderer.setStyle(link, 'cursor', 'pointer');
}
//这个是文字会溢出的条件,就是文字超过两行的时候才可以会出现我们规定的显示...More
private shouldCollapse() {
const MAX_HEIGHT = 38;
return this.el.nativeElement.offsetHeight > MAX_HEIGHT;
}
//这是后来发现的一个bug 由于angular莫名的加载顺序,导致我们的元素并没有加载到DOM树里去 所以就这个方法去解决这个问题
private waitUntilAddedToDom(timeout = 1000): Promise<void> {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const timer = setInterval(() => {
const elementAddedToDom = document.body.contains(this.el.nativeElement);
if (elementAddedToDom) {
clearInterval(timer);
return resolve();
}
if (Date.now() - startTime >= timeout) {
clearInterval(timer);
return reject('timeout');
}
}, 100);
});
}
//这个方法主要是截取相关的字符并加上...
private getContentCollapsed(content: string, truncatedSize: number): string {
return content.split(' ')
.slice(0, truncatedSize)
.join(' ')
.concat(' ...');
}
}
网友评论