手写一个超出显示更多,点击可以收起的限制行数组件

2,419 阅读1分钟

最近在项目中有这样一个需求,一段内容超出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>