当前位置: 首页 > Web前端 > HTML5

如何判断一个元素是否在ViewPort可视区域

时间:2023-04-05 19:24:54 HTML5

个性签名:生如夏花,死如冬雪;前言:经常需要计算元素的大小或者页面的位置,offsetWidth、clientWidth、scrollWidth、scrollTop这些关键字是家常便饭,每次遇到都需要提前试验一下。以提高下一步开发的效率。这里一次性总结一下判断元素是否在可见区域,用原生js简单实现懒加载。文末有一个简单的懒加载实现demo,有需要的可以看看。目录工作者要想做好本职工作,必先利其器。在判断一个元素是否在可见区域实现了简单的原生懒加载之前,我们先简单回顾一下以下几个关键概念。ps:如果你已经熟悉这些概念,可以跳到第五点查看关键代码示例。1.OffsetOffset(偏移尺寸),一个元素的可见尺寸是由它的高和宽决定的,包括所有的内边距、滚动条和边框尺寸(注意不包括外边距)。可以通过以下4个属性获取元素的偏移量。偏移概念公式offsetHeight元素在垂直方向上所占的空间,以像素为单位。这包括元素的高度、(可见的)水平滚动条的高度、顶部边框的高度和底部边框的高度。offsetHeght=content+padding+border+scrollXoffsetWidth元素在水平方向上所占的空间,以像素为单位。这些包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度。offsetWidth=content+padding+border+scrollYoffsetLeft元素的左外边框与包含元素的左内边框之间的像素距离。offsetTop元素的顶部外边框与包含元素的顶部内边框之间的距离(以像素为单位)。其中,offsetLeft和offsetTop属性与包含元素相关,包含元素的引用保存在offsetParent属性中。**offsetParent属性不一定等于parentNode的值。注意:所有这些偏移量属性都是只读的,每次访问它们都需要重新计算。因此,你应该尽量避免重复访问这些属性;如果需要重用其中某些属性的值,可以将它们保存在局部变量中以提高性能。这也是上一篇文本跑马灯工程中添加padding后需要重新获取textWidth的原因(点此跳转)。SummaryOffset:只读属性;包括滚动条和边框,不包括边距。2.客户区的大小客户区的大小是只读的,每次访问都必须重新计算。客户区大小概念公式clientWidthclientWidth属性为元素内容区的宽度加上左右padding的宽度;确定浏览器视口大小时(在IE7之前的版本中)。如下例所示:functiongetViewport(){//检查document.compatMode属性以确定浏览器是否运行在混杂模式。//Safari3.1之前的版本不支持该属性,所以会自动执行else语句;}else{return{width:document.documentElement.clientWidth,height:document.documentElement.clientHeight};}}客户区的SummarySize:只读属性;不包括滚动条和边框,不包括边距。3.滚动尺寸概念scrollHeight是没有滚动条的元素内容的总高度。scrollWidth是没有滚动条的元素内容的总宽度。scrollLeft要隐藏的内容区域左侧的像素数。通过设置此属性,您可以更改元素的滚动位置。scrollTop的像素数隐藏在内容区域上方。通过设置此属性,您可以更改元素的滚动位置。scrollWidth和scrollHeight主要用于确定元素内容的实际大小。scrollLeft和scrollTop属性不仅可以确定元素当前的滚动状态,还可以设置元素的滚动位置。当元素未滚动时,这两个属性的值都等于0。如果元素垂直滚动,scrollTop的值将大于0,表示元素上方不可见内容的像素高度。如果元素水平滚动,scrollLeft的值会大于0,表示元素左侧不可见内容的像素宽度。这两个属性都是可设置的,因此将元素的scrollLeft和scrollTop设置为0会重置元素的滚动位置。例如:上一篇(点此跳转)文本跑马灯项目中scrollLeft的使用总结了只读属性,不包括滚动条和边框。4、判断元素大小的兼容性一般来说,right和left的差值等于offsetWidth的值,bottom和top的差值等于offsetHeight。综上所述,就可以创建下面这个跨浏览器的函数:functiongetElementLeft(element){varactualLeft=element.offsetLeft;varcurrent=element.offsetParent;while(current!==null){actualLeft+=current.offsetLeft;current=current.offsetParent;}returnactualLeft;}functiongetElementTop(element){varactualTop=element.offsetTop;varcurrent=element.offsetParent;while(current!==null){actualTop+=current.偏移顶部;current=current.offsetParent;}returnactualTop;}functiongetBoundingClientRect(element){varscrollTop=document.documentElement.scrollTop;varscrollLeft=document.documentElement.scrollLeft;如果(element.getBoundingClientRect){如果(typeofarguments.callee.offset!="number"){vartemp=document.createElement("div");temp.style.cssText="position:absolute;left:0;top:0;";document.body.appendChild(temp);arguments.callee.offset=-temp.getBoundingClientRect().top-scrollTop;document.body.removeChild(temp);温度=空;}varrect=element.getBoundingClientRect();varoffset=arguments.callee.offset;return{left:rect.left+offset,right:rect.right+offset,top:rect.top+offset,bottom:rect.bottom+offset};}else{varactualLeft=getElementLeft(元素);varactualTop=getElementTop(元素);return{left:actualLeft-scrollLeft,right:actualLeft+element.offsetWidth-scrollLeft,top:actualTop-scrollTop,bottom:actualTop+element.offsetHeight-scrollTop}}}5.判断元素是否在可见区域知道尺寸和元素的位置在区域外我们可以做什么?我们可以利用上面学到的知识点来检测元素是否在可见区域,比方说大了,这也是图片懒加载的实现原理5.1第一种方法公式:el.offsetTop-document.documentElement.scrollTop<=viewPortHeightfunctionisInViewPortOfOne(el){//viewPortHeight兼容所有浏览器constviewPortHeight=window.innerHeight||文档.documentElement.clientHeight||document.body.clientHeightconstoffsetTop=el.offsetTopconstscrollTop=document.documentElement.scrollTopconsttop=offsetTop-scrollTopconsole.log('top',top)//这里有+100用于提前加载+100returntop<=viewPortHeight+100}5.2第二种方法公式:el.getBoundingClientReact().top<=viewPortHeight其实el.offsetTop-document.documentElement.scrollTop=el.getBoundingClientRect().top,利用这个,我们可以使用下面的代码代替方法一functionisInViewPortOfTwo(el){constviewPortHeight=window.innerHeight||文档.documentElement.clientHeight||document.body.clientHeightconsttop=el.getBoundingClientRect()&&el.getBoundingClientRect().topconsole.log('top',top)返回顶部<=viewPortHeight+100}5.3第三种方法公式:intersectionRatio>0&&intersectionRatio<=1//定义一个路口观察器constio=newIntersectionObserver(ioes=>{ioes.forEach(ioe=>{constel=ioe.targetconstintersectionRatio=ioe.intersectionRatioif(intersectionRatio>0&&intersectionRatio<=1){loadImg(el)io.unobserve(el)}el.onload=el.onerror=()=>io.unobserve(el)})})//复制代码执行交叉观察者函数isInViewPortOfThree(el){io.observe(el)}5.4兼容性比较在兼容性方面,我们知道比较原始的方法兼容性最好,那么第二种方法和第三种方法是否可以替换第三种方法?看一下caniuse的数据,getBoundingClientReact的适配很乐观。所以,如果移动端和桌面端都需要兼容适配,方法二完全可以替代方法一进行适配。如果只是桌面适配(比如操作后台),可以试试新的IntersectionObserver方式。毕竟IntersectionObserver中还有更丰富的功能等着我们去体验。5.5例子有时候,我们希望一些静态资源(比如图片)只在用户向下滚动进入视口时加载,这样可以节省带宽,提高网页性能。这就是所谓的“延迟加载”,也称为惰性加载。懒加载预览DEMO(放入你本地的图片,通过改变不同的方法实现懒加载)-------------------------------Gorgeous分割线----------------------------ps:有的公众号大佬们想减少每天写推文的时间消耗,采集技术原创文章稿件由本人展示推送公众号,合作可联系我。(可以在公众号找到我的联系方式)关于我我的github主页(点击进入)我的掘金主页(点击进入)我的简书主页(点击进入)我的公众号(点击进入或扫描下方二维码)参考原生JS实现最简单的图片懒加载JavaScript高级编程IntersectionObserverAPI教程