判断text-overflow: ellipsis是否生效

3,341 阅读3分钟

js本身不直接提供检测 CSS 样式是否生效的功能,但可以通过 js来检查指定元素的样式来判断文本是否已溢出。

常用的方法

元素的scrollWidthclientWidth相比较,当scrollWidth大于clientWidth时,文本溢出。

.overflow{
    text-overflow: ellipsis;
    overflow:hidden;
    width:100px;
    font-size:16px;
}
<div class="overflow">Coding Startup</div>
const isOverflowed = ()=>{
    let el = document.querySelector<HtmlElement>('.overflow');
    if(!el) return
    if(el.clientWidth < el.scrollWidth) {
        console.log('文本溢出');
    } else {
        console.log('文本没有溢出');
    }
}
isOverflowed() // 文本溢出

痛点

这个方法有个瑕疵,clientWidthscrollWidth属性都是进行过四舍五入处理的,

当元素实际可视宽度为小数,元素实际内容宽度也为小数,并且元素实际可视宽度和元素实际内容宽度相等,那这个判断就会不准确了。例如:当元素实际可视宽度为112.66px,元素实际内容宽度为112.83px,这个时候元素肯定是溢出了的,但是这个时候元素的clientWidthscrollWidth的值相等,是113px。

.overflow{
    text-overflow: ellipsis;
    overflow:hidden;
    width:112.66px;
    font-size:16px;
}
<div class="overflow">Coding Startup</div>
const isOverflowed = ()=>{
    let el = document.querySelector<HtmlElement>('.overflow');
    if(!el) return
    if(el.clientWidth < el.scrollWidth) {
        console.log('文本溢出');
    } else {
        console.log('文本没有溢出');
    }
}
isOverflowed() // 文本没有溢出

新方案

解决这个痛点的思路是用其他方法得到高精度的clientWidthscrollWidth

高精度clientWidth

利用getBoundingClientRect api可以获取。

const clientWidth = el.getBoundingClientRect().width

高精度scrollWidth

mdn文档中也是建议利用getBoundingClientRect来获取scrollWidth,但是是获取不到的,只能另辟蹊径。利用range把元素的所有子节点划成一个范围,再获取这个范围的宽度。elementUI也是用了这个方法计算实际内容宽度。

    const range = document.createRange()
    range.setStart(el, 0)
    range.setEnd(el, el.childNodes.length)
    // 所有子节点的宽度总和
    const rangeWidth = range.getBoundingClientRect().width
    const getStyle = (el:HTMLElement,key:string)=>{ 
        if(!el || !key) return
        return getComputedStyle(el)?.[key] 
    } 
    // 还需要加上容器元素的左右padding
    // cellChild是例子中的变量,是元素dom节点,可以点击上方elementUI链接查看
    const padding = (parseInt(getStyle(cellChild, 'paddingLeft'), 10) || 0) + (parseInt(getStyle(cellChild, 'paddingRight'), 10) || 0);
    // 内容实际宽度
    const scrollWidth = rangeWidth + padding

再把高精度的clientWidth和高精度的scrollWidth进行比较就可以得出文本是否溢出了。

const isOverflowed = ()=>{
    let el = document.querySelector<HtmlElement>('.overflow');
    if(!el) return
    const range = document.createRange()
    range.setStart(el, 0)
    range.setEnd(el, el.childNodes.length)
    // 所有子节点的宽度总和
    const rangeWidth = range.getBoundingClientRect().width
    const getStyle = (el:HTMLElement,key:string)=>{ 
        if(!el || !key) return
        return getComputedStyle(el)?.[key] 
    } 
    // 还需要加上容器元素的左右padding 
    // const padding = (parseInt(getStyle(cellChild, 'paddingLeft'), 10) || 0) + (parseInt(getStyle(cellChild, 'paddingRight'), 10) || 0); 
    // 上面那行写错了 cellChild改为el
    const padding = (parseInt(getStyle(el, 'paddingLeft'), 10) || 0) + (parseInt(getStyle(el, 'paddingRight'), 10) || 0); 
    // 内容实际宽度
    const scrollWidth = rangeWidth + padding
    // 内容当前宽度
    const clientWidth = el.getBoundingClientRect().width
    if(clientWidth < scrollWidth) {
      console.log('文本溢出');
    } else {
      console.log('文本没有溢出');
    }
}

兼容性

兼容性挺不错的,除了ie(正经人谁兼容ie /doge)

Untitled.png

补充(20230529)

这个方案不仅仅可以判断单行的文本是否溢出,还可以判断多行文本是否溢出。

.overflow{
    text-overflow: ellipsis;
    overflow:hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 5;
}
    range.setStart(el, 0)
    range.setEnd(el, el.childNodes.length)
    // 所有子节点的高度总和
    const rangeHeight = range.getBoundingClientRect().height
    const getStyle = (el:HTMLElement,key:string)=>{ 
        if(!el || !key) return
        return getComputedStyle(el)?.[key] 
    } 
    // 还需要加上容器元素的上下padding 
    // const padding = (parseInt(getStyle(cellChild, 'paddingTop'), 10) || 0) + (parseInt(getStyle(cellChild, 'paddingBottom'), 10) || 0);
    // 上面那行写错了 cellChild改为el
    const padding = (parseInt(getStyle(el, 'paddingTop'), 10) || 0) + (parseInt(getStyle(el, 'paddingBottom'), 10) || 0);
    // 内容实际高度
    const scrollHeight = rangeHeight + padding
    // 内容当前高度
    const clientHeight = el.getBoundingClientRect().height
    if(clientHeight < scrollHeight) {
      console.log('文本溢出');
    } else {
      console.log('文本没有溢出');
    }