今天在解决 ios 移动端滚动穿透的问题时遇到一个问题,就是判断元素可否滚动,把这个过程记录下来。如下以纵向滚动为例,横向滚动同理。
嫌麻烦的能够直接查看代码。javascript
Element.scrollHeightjava
scrollHeight 是一个元素内容高度的度量,包括因为溢出致使的视图中不可见的内容,scrollHeight 的值等于元素在不能出现滚动条的时候展现出视图中全部内容所须要的最小高度。
若是元素内容能在不出现滚动条的状况下所有展现,那么元素的 scrollHeight 和 clientHeight 相等。 MDN
Element.clientHeightios
clientHeight 是指元素内部高度,包括 padding,但不包括 margin、border 以及 横向滚动条。
clientHeight = CSS height + CSS padding - height of horizontal scrollbar (if present) MDN
根据上面两个定义,很容易就想到能够经过比较 scrollHeight 和 clientHeight 的大小来判断元素是否能够滚动。最开始我就是这么作的。git
function eleCanScroll(ele) { return ele.scrollHeight > ele.clientHeight; }
使用以后发现这个方法大部分时候判断是对的,可是在一些状况下就出现的误差。
误差1
查看demo
子元素超出父元素,并且父元素不设置 overflow: auto
误差2
查看demo
子元素相对父元素绝对定位github
上面的两个 demo 中,虽然 box 元素的 scrollHeight 大于 clientHeight,可是并不能滚动。code
其实缘由从 scrollHeight 的定义中就能找到,scrollHeight 是一个元素内容高度的度量,包括因为溢出致使的视图中不可见的内容,也就是说,只要元素的子元素没有彻底在父级内容框中显示出来,无论是由于定位仍是 translate 偏移致使,父级元素的 scrollHeight 就必定大于 clientHeight。而这里面不少状况下父级元素都是不能滚动的。ip
因此经过比较 scrollHeight 和 clientHeight 的大小来判断元素是否能够滚动的方法是错误的,我看网上有的解决办法是判断父级元素的 overflow 属性,但咱们知道这样也是不对的。文档
正确的解决办法就要从 scrollTop 上入手了。
Element.scrollTopget
一个元素的 scrollTop 值是这个元素的顶部到视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。 MDNscrollTop 能够被设置为任何整数值,同时注意:若是一个元素不能被滚动(例如,它没有溢出,或者这个元素有一个"non-scrollable"属性), scrollTop将被设置为0。it
根据上面 scrollTop 的语法咱们就能找到解决方案。咱们能够设置元素的 scrollTop = 1,再获取下 scrollTop,若是值变为 0,说明该元素不能够滚动。
代码地址
function eleCanScroll(ele) { if (!ele instanceof HTMLElement) { console.log("fuck off"); return; } if (ele.scrollTop > 0) { return true; } else { ele.scrollTop++; // 元素不能滚动的话,scrollTop 设置不会生效,还会置为 0 const top = ele.scrollTop; // 重置滚动位置 top && (ele.scrollTop = 0); return top > 0; } }
https://developer.mozilla.org...
https://developer.mozilla.org...
https://developer.mozilla.org...