js本身不直接提供检测 CSS 样式是否生效的功能,但可以通过 js来检查指定元素的样式来判断文本是否已溢出。
常用的方法
元素的scrollWidth
与clientWidth
相比较,当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() // 文本溢出
痛点
这个方法有个瑕疵,clientWidth
和scrollWidth
属性都是进行过四舍五入处理的,
当元素实际可视宽度为小数,元素实际内容宽度也为小数,并且元素实际可视宽度和元素实际内容宽度相等,那这个判断就会不准确了。例如:当元素实际可视宽度为112.66px,元素实际内容宽度为112.83px,这个时候元素肯定是溢出了的,但是这个时候元素的clientWidth
和scrollWidth
的值相等,是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() // 文本没有溢出
新方案
解决这个痛点的思路是用其他方法得到高精度的clientWidth
和scrollWidth
。
高精度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)
补充(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('文本没有溢出');
}