最近在项目中有这样一个需求,一段内容超出3行之后要在后面显示'...更多'按钮,点击更多按钮之后显示完整内容,内容后面变为'收起'按钮,先看看我实现的效果图,如下图所示:
下面就来讲讲我的做法,有什么优化之处或者不对之处还请多多指教哈~
1,首先就是先比较高度判断是否溢出
先通过offsetHeight获取'...更多'这个Dom节点的高度moreDom,以它为标准,然后通过offsetHeight获取该组件本身'this'的高度,比如限制了只能显示3行(maxRow=3),这个时候就可以比较高度来判断是否显示'...更多'这个按钮,通过this.$el.offsetHeight > maxRow*moreDom.offsetHeight判断是否溢出,溢出则进一步处理,否则不处理。
2,采用了二分法截取内容
循环判断获取最大的可显示的内容数量,采用了二分法截取内容的最多可显示的内容数量,这里需要加上一句if (contentBox.offsetHeight > maxHeight * (this.maxRow + 2))去截取到更多的可显示的内容数量。如果没有这一句就只能显示到最后一行的前半句,后半句就被隐藏掉了,通过Math.floor(text.length / 2)得到最大显示的内容的数量,通过contentDom.innerText = text.substring(0, Math.floor(text.length / 2))就可以给你内容的Dom节点赋值了。 上源代码,下面是父组件的代码:
// testText: 内容
// maxRow: 限制显示几行内容
// isLimitHeight: 是否限制内容显示
// isOver: 是否显示超出的提示语
// moreClick: 点击可以展开,点击可以收起的事件
<test-more
:testText="testText"
:maxRow="1"
:isLimitHeight="isLimitHeight"
:isOver.sync="isOver"
@moreClick="moreClick"
>
<span slot="before">内容:</span>
<span slot="after" v-if="isOver" {{moreText}}</span>
</test-more>
// js
moreClick() {
this.isLimitHeight = !this.isLimitHeight;
this.moreText = this.isLimitHeight ? '...更多' : '收起';
},
下面是该组件的代码:
<template>
<div class="overBox">
<slot name="before"></slot>
<span class="contentText">{{testText}}</span>
<!--<slot name="after" class="moreText" @click="handleMore"></slot>-->
// 用span包着获取高度
<span v-show="isShowMore" class="moreText" @click="handleMore">
<slot name="after"></slot>
</span>
</div>
</template>
<script>
export default {
name: 'testMore',
props: {
testText: {
type: String,
default: '',
},
isLimitHeight: {
type: Boolean,
default: false,
},
maxRow: {
type: Number,
default: 1,
},
},
data() {
return {
isHide: false, // 以后做是否显示提示语
isShowMore: false, // 默认不显示'更多'
};
},
mounted() {
this.init();
},
methods: {
handleMore() {
this.$emit('moreClick');
},
init() {
// 判断是否需要限制行数(高度)
if (this.isLimitHeight) {
this.handleHeight();
} else {
const contentDom = this.$el.querySelector('.contentText');
contentDom.innerText = this.testText;
}
},
handleHeight() {
this.$nextTick(() => {
const contentBox = this.$el;
const contentDom = this.$el.querySelector('.contentText');
const moreDom = this.$el.querySelector('.moreText');
// 设置成inline-block显示出来,获取它本身的高度
moreDom.style.display = 'inline-block';
// 获取限制maxRow最大行数的高度,以更多按钮的本身高度为参考物
const maxHeight = this.maxRow * moreDom.offsetHeight;
// 采用二分法进行判断需要显示多少内容
let n = 999;
if (contentBox.offsetHeight > maxHeight) {
let text = this.testText;
while (contentBox.offsetHeight > maxHeight && n > 0) {
if (contentBox.offsetHeight > maxHeight * (this.maxRow + 2)) {
contentDom.innerText = text = text.substring(0, Math.floor(text.length / 2));
} else {
contentDom.innerText = text = text.substring(0, text.length - 1);
}
n--;
}
this.$emit('update:isOver', true);
} else {
this.$emit('update:isOver', false);
}
})
},
},
watch: {
testText() {
this.init();
},
maxHeight() {
this.init();
},
isLimitHeight() {
this.init();
},
maxRow() {
this.init();
},
},
};
</script>
<style scoped>
.overBox {
margin: 200px 400px;
}
.moreText {
margin-left: 8px;
cursor: pointer;
color: #3199F5;
}
</style>