js获取display的值_JS实现多行溢出省略号思路

0ae991c47d03a1949633ec3e0c201fe0.gif

说起多行溢出省略号,用CSS实现最简单

.one-line {
 display: -webkit-box !important;
 overflow: hidden;
 text-overflow: ellipsis;
 word-break: break-all;
 -webkit-box-orient: vertical;
 -webkit-line-clamp: 1;
 /*clip  修剪文本。*/
}

.more-line {
 display: -webkit-box !important;
 overflow: hidden;
 text-overflow: ellipsis;
 word-break: break-all;
 -webkit-box-orient: vertical;
 -webkit-line-clamp: 2;
}

下面就摸索下用JS如何实现:

  • Github DEMO

先看两个API:

getClientRects获取元素占据页面的所有矩形区域 :

  • getClientRects 返回一个TextRectangle集合,就是TextRectangleList对象。TextRectangle对象包含了, top left bottom right width height 六个属性TextRectangle对于文本对象,W3C提供了一个 TextRectangle 对象,这个对象是对文本区域的一个解释。这里的文本区域只针对inline 元素,比如:a, span, em这类标签元素。浏览器差异getClientRects() 最先由MS IE提出,后被W3C引入并制订了标准。目前主流浏览器都支持该标准,而IE只支持TextRectangle的top left bottom right四个属性。IE下可以通过right-left来计算width、bottom-top来计算height。ie 和非ie浏览器在使用getClientRects还是有些差别的,ie获取TextRectangleList的范围很大。而非ie获取的范围比较小, 只有display:inline的对象才能获取到TextRectangleList,例如em i span 等标签。应用场景getClientRects常用于获取鼠标的位置,如放大镜效果。微博的用户信息卡也是通过改方法获得的。

总结:只能用于行内元素,返回每一行的信息,返回信息和getBoundingClientRect返回类似。

DEMO:

058b6060c07fb2840cc2c954af102fd5.png
html>
<html>

 <head>
  <meta charset="UTF-8">
  <title>title>
  <style type="text/css">
   * {padding: 0px;margin: 0px;
   }div {width: 80%;height: 90px;border: 1px solid red;
   }#test{width:400px;height: 10px;background: red;
   }style>
 head>

 <body>
  <div>
   <span id="main">返回值是ClientRect对象集合,该对象是与该元素相关的CSS边框。每个ClientRect对象包含一组描述该边框的只读属性——left、top、right和bottom,单位为像素,这些属性值是相对于视口的top-left的。即使当表格的标题在表格的边框外面,该标题仍会被计算在内。span>
  div>
  <div id="test">div>
 body>
 <script type="text/javascript">console.log(document.getElementById("main").getClientRects());script>

html>

getBoundingClientRect获取元素位置 getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。getBoundingClientRect是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height这里的topleft和css中的理解很相似,width、height是元素自身的宽高,但是rightbottom和css中的理解有点不一样。right是指元素右边界距窗口最左边的距离,bottom是指元素下边界距窗口最上面的距离。

getBoundingClientRect()最先是IE的私有属性,现在已经是一个W3C标准。所以你不用当心浏览器兼容问题,不过还是有区别的:IE只返回top,lef,right,bottom四个值,

返回差异:

getClientRectsgetBoundingClientRect 的区别返回类型差异:getClientRects 返回一个TextRectangle集合,就是TextRectangleList对象。getBoundingClientRect返回 一个TextRectangle对象,即使DOM里没有文本也能返回TextRectangle对象.浏览器差异:除了safari,firefox2.0外所有浏览器都支持getClientRectsgetBoundingClientRect,firefox 3.1给TextRectangle增加了 width 和 height。ie 和非ie浏览器在使用getClientRects还是有些差别的,ie获取TextRectangleList的范围很大。而非ie获取的范围比较小, 只有display:inline的对象才能获取到TextRectangleList,例如em i span 等标签。通过测试,至少Chrome 2+\Safari 4\Firefox3.5\0pera 9.63+已经支持getBoundingClientRect方法。使用场景差异:出于浏览器兼容的考虑,现在用得最多的是getBoundingClientRect,经常用来获取一个element元素的viewport坐标。

8dedf1e58be30db0be6166651bbbc291.png

Vue多行溢出省略号

监听DOM尺寸变化

import { addListener, removeListener } from 'resize-detector'

 if (this.autoresize) {
    let resizeCallback = () => {
      this.update()
    }
    addListener(this.$el, resizeCallback)   //监听
    this.unregisterResizeCallback = () => {   //移除
      removeListener(this.$el, resizeCallback)
    }
  }

判断是否溢出

  isOverflow () {
      if (!this.maxLines && !this.maxHeight) {
        return false
      }

      if (this.maxLines) {   
        //获取全部显示的行数
        let actualLines = this.$refs.content.getClientRects().length
        if (actualLines > this.maxLines) {
          return true
        }
      }

      if (this.maxHeight) {
        if (this.$el.scrollHeight > this.$el.offsetHeight) {
          return true
        }
      }
      return false
    },

二分查找多行截取字符临界值

moveEdge (steps) {
  this.clampAt(this.offset + steps)
},
clampAt (offset) {
  this.offset = offset
  this.applyChange()
},
applyChange () {
  this.$refs.text.textContent = this.realText
},
stepToFit () {
  this.fill()
  this.clamp()
},
fill () {
  while (!this.isOverflow() && this.offset this.text.length) {
    this.moveEdge(1)
  }
},
clamp () {
  while (this.isOverflow() && this.offset > 0) {
    this.moveEdge(-1)
  }
},
search (...range) {
  let [from = 0, to = this.offset] = range
  if (to - from <= 3) {
    this.stepToFit()
    return
  }
  let target = Math.floor((to + from) / 2)    //从中间找临界值
  this.clampAt(target)
  if (this.isOverflow()) {
    this.search(from, target)
  } else {
    this.search(target, to)
  }
}

完整代码:

import { addListener, removeListener } from 'resize-detector'
import Vue from 'vue'

const UPDATE_TRIGGERS = ['maxLines', 'maxHeight', 'ellipsis']
const INIT_TRIGGERS = ['tag', 'text', 'autoresize']

export default {
  name: 'vue-clamper',
  props: {
    tag: {
      type: String,
      default: 'div'
    },
    autoresize: {
      type: Boolean,
      default: false
    },
    maxLines: Number,
    maxHeight: [String, Number],
    ellipsis: {
      type: String,
      default: '…'
    },
    expanded: Boolean
  },
  data () {
    return {
      offset: null,
      text: this.getText(),
      localExpanded: !!this.expanded
    }
  },
  computed: {
    clampedText () {
      return this.text.slice(0, this.offset) + this.ellipsis
    },
    isClamped () {
      if (!this.text) {
        return false
      }
      return this.offset !== this.text.length
    },
    realText () {
      return this.isClamped ? this.clampedText : this.text
    },
    realMaxHeight () {
      if (this.localExpanded) {
        return null
      }
      let { maxHeight } = this
      if (!maxHeight) {
        return null
      }
      return typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight
    }
  },
  watch: {
    expanded (val) {
      this.localExpanded = val
    },
    localExpanded (val) {
      if (val) {
        this.clampAt(this.text.length)
      } else {
        this.update()
      }
      if (this.expanded !== val) {
        this.$emit('update:expanded', val)
      }
    }
  },
  mounted () {
    this.init()

    INIT_TRIGGERS.forEach(prop => {
      this.$watch(prop, this.init)
    })

    UPDATE_TRIGGERS.forEach(prop => {
      this.$watch(prop, this.update)
    })
  },
  updated () {
    this.text = this.getText()
    this.applyChange()
  },
  beforeDestroy () {
    this.cleanUp()
  },
  methods: {
    init () {
      let contents = this.$slots.default
      if (!contents) {
        return
      }
      if (Array.isArray(contents) && contents.length > 1) {
        Vue.util.warn(
          'VueClamper only supports clamping plain text content.',
          this
        )
        return
      }
      let [content] = contents
      if (content && content.tag) {
        Vue.util.warn(
          'VueClamper only supports clamping plain text content.',
          this
        )
        return
      }

      this.offset = this.text.length

      this.cleanUp()

      if (this.autoresize) {
        let resizeCallback = () => {
          this.update()
        }
        addListener(this.$el, resizeCallback)
        this.unregisterResizeCallback = () => {
          removeListener(this.$el, resizeCallback)
        }
      }
      this.update()
    },
    update () {
      if (this.localExpanded) {
        return
      }
      this.applyChange()
      if (this.isOverflow() || this.isClamped) {
        this.search()
      }
    },
    expand () {
      this.localExpanded = true
    },
    collapse () {
      this.localExpanded = false
    },
    toggle () {
      this.localExpanded = !this.localExpanded
    },
    isOverflow () {
      if (!this.maxLines && !this.maxHeight) {
        return false
      }

      if (this.maxLines) {
        let actualLines = this.$refs.content.getClientRects().length
        if (actualLines > this.maxLines) {
          return true
        }
      }

      if (this.maxHeight) {
        if (this.$el.scrollHeight > this.$el.offsetHeight) {
          return true
        }
      }
      return false
    },
    getText () {
      let [content] = this.$slots.default || []
      return content ? content.text : ''
    },
    moveEdge (steps) {
      this.clampAt(this.offset + steps)
    },
    clampAt (offset) {
      this.offset = offset
      this.applyChange()
    },
    applyChange () {
      this.$refs.text.textContent = this.realText
    },
    stepToFit () {
      this.fill()
      this.clamp()
    },
    fill () {
      while (!this.isOverflow() && this.offset this.text.length) {
        this.moveEdge(1)
      }
    },
    clamp () {
      while (this.isOverflow() && this.offset > 0) {
        this.moveEdge(-1)
      }
    },
    search (...range) {
      let [from = 0, to = this.offset] = range
      if (to - from <= 3) {
        this.stepToFit()
        return
      }
      let target = Math.floor((to + from) / 2)
      this.clampAt(target)
      if (this.isOverflow()) {
        this.search(from, target)
      } else {
        this.search(target, to)
      }
    },
    cleanUp () {
      if (this.unregisterResizeCallback) {
        this.unregisterResizeCallback()
      }
    }
  },
  render (h) {
    let contents = [
      h(
        'span',
        {
          ref: 'text',
          attrs: {
            'aria-label': this.text.trim()
          }
        },
        this.realText
      )
    ]

    let { expand, collapse, toggle } = this
    let scope = { expand, collapse, toggle }
    let before = this.$scopedSlots.before
      ? this.$scopedSlots.before(scope)
      : this.$slots.before
    if (before) {
      contents.unshift(...(Array.isArray(before) ? before : [before]))
    }
    let after = this.$scopedSlots.after
      ? this.$scopedSlots.after(scope)
      : this.$slots.after
    if (after) {
      contents.push(...(Array.isArray(after) ? after : [after]))
    }
    let lines = [
      h(
        'span',
        {
          style: {
            boxShadow: 'transparent 0 0'
          },
          ref: 'content'
        },
        contents
      )
    ]
    return h(
      this.tag,
      {
        style: {
          maxHeight: this.realMaxHeight,
          overflow: 'hidden'
        }
      },
      lines
    )
  }
}

使用:

import VClamp from './components/Clamp.js'
<v-clamp:class="{
    demo: true,
    hyphens: hyphens1
  }":max-lines="lines"autoresize:style="{
    width: `${width1}px`
  }"
>
  {{ zh ? textZh : text }}
  <buttonslot="after"slot-scope="{ toggle }"class="toggle btn btn-sm"
    @click="toggle"
  >
    {{ zh ? '切换' : 'Toggle' }}
  button>
v-clamp>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值