How to tell if a DOM element is visible in the current viewport?
有没有一种有效的方法来判断一个DOM元素(在HTML文档中)当前是否可见(显示在视区中)?
(问题是指火狐)
现在大多数浏览器都支持getboundingclientrect方法,这已经成为最佳实践。使用一个旧的答案是非常缓慢的,不准确的,有几个错误。
选择正确的解决方案几乎从不精确。您可以阅读更多关于它的bug的信息。
此解决方案在IE7+、IOS5+Safari、Android2+、BlackBerry、Opera Mobile和IE Mobile 10上进行了测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function isElementInViewport (el) { //special bonus for those using jQuery if (typeof jQuery ==="function" && el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */ rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */ ); } |
如何使用:
您可以确保上面给出的函数在调用时返回正确的答案,但是跟踪元素作为事件的可见性如何?
将以下代码放在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | function onVisibilityChange(el, callback) { var old_visible; return function () { var visible = isElementInViewport(el); if (visible != old_visible) { old_visible = visible; if (typeof callback == 'function') { callback(); } } } } var handler = onVisibilityChange(el, function() { /* your code go here */ }); //jQuery $(window).on('DOMContentLoaded load resize scroll', handler); /* //non-jQuery if (window.addEventListener) { addEventListener('DOMContentLoaded', handler, false); addEventListener('load', handler, false); addEventListener('scroll', handler, false); addEventListener('resize', handler, false); } else if (window.attachEvent) { attachEvent('onDOMContentLoaded', handler); // IE9+ :( attachEvent('onload', handler); attachEvent('onscroll', handler); attachEvent('onresize', handler); } */ |
。
如果您做了任何DOM修改,它们当然可以更改元素的可见性。
指导方针和常见缺陷:
也许你需要跟踪页面缩放/移动设备夹点?jquery应该处理缩放/收缩交叉浏览器,否则第一个或第二个链接应该对您有所帮助。
如果修改dom,它会影响元素的可见性。您应该控制它并手动呼叫
永远不要在jquery$(document.ready()中使用它,因为此时没有应用任何CSS担保。您的代码可以在硬盘上本地使用您的CSS,但一旦放到远程服务器上,它就会失败。
启动
我们还无法捕获缩放/收缩事件。
最后一个办法可能是以下代码:
1 2 | /* TODO: this looks like a very bad code */ setInterval(handler, 600); |
如果您关心网页上的选项卡是否处于活动状态和可见状态,则可以使用Awesome功能页Visibility HTML5 API。
TODO:此方法不处理两种情况:
- 使用
z-index 重叠 在元素容器中使用EDOCX1[6]
尝试一些新的东西https://pawelgrzybek.com/the-intersection-observer-api-explained/
更新:时间在前进,我们的浏览器也在前进。不再推荐此技术,如果不需要支持ie<7,则应使用下面的@dan's解决方案(https://stackoverflow.com/a/7557433/5628)。
原始解决方案(现已过时):
这将检查元素是否在当前视区中完全可见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function elementInViewport(el) { var top = el.offsetTop; var left = el.offsetLeft; var width = el.offsetWidth; var height = el.offsetHeight; while(el.offsetParent) { el = el.offsetParent; top += el.offsetTop; left += el.offsetLeft; } return ( top >= window.pageYOffset && left >= window.pageXOffset && (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth) ); } |
您可以简单地修改它以确定元素的任何部分是否在视区中可见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function elementInViewport2(el) { var top = el.offsetTop; var left = el.offsetLeft; var width = el.offsetWidth; var height = el.offsetHeight; while(el.offsetParent) { el = el.offsetParent; top += el.offsetTop; left += el.offsetLeft; } return ( top < (window.pageYOffset + window.innerHeight) && left < (window.pageXOffset + window.innerWidth) && (top + height) > window.pageYOffset && (left + width) > window.pageXOffset ); } |
号
更新
在现代浏览器中,您可能希望查看交叉口观察器API,它提供了以下好处:
- 比监听滚动事件性能更好
- 在跨域iframes中工作
- 可以判断一个元素是否阻碍/交叉另一个元素
交集观察员正在成为一个成熟的标准,已经在Chrome51+、Edge 15+和Firefox 55+中得到支持,并且正在为Safari开发中。还提供了一种PolyFill。
上一个答案丹提供的答案存在一些问题,这可能使它在某些情况下成为不合适的方法。这些问题中有一些是在他接近底部的答案中指出的,他的代码将为以下元素提供误报:
- 被另一个元素隐藏在被测元素前面
- 在父元素或祖先元素的可见区域之外
- 使用css
clip 属性隐藏的元素或其子元素
这些局限性在以下简单测试结果中得到了证明:
。
解决办法:下面是这些问题的解决方案,测试结果如下,并解释了代码的某些部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function isElementVisible(el) { var rect = el.getBoundingClientRect(), vWidth = window.innerWidth || doc.documentElement.clientWidth, vHeight = window.innerHeight || doc.documentElement.clientHeight, efp = function (x, y) { return document.elementFromPoint(x, y) }; // Return false if it's not in the viewport if (rect.right < 0 || rect.bottom < 0 || rect.left > vWidth || rect.top > vHeight) return false; // Return true if any of its four corners are visible return ( el.contains(efp(rect.left, rect.top)) || el.contains(efp(rect.right, rect.top)) || el.contains(efp(rect.right, rect.bottom)) || el.contains(efp(rect.left, rect.bottom)) ); } |
Passing test: http://jsfiddle.net/AndyE/cAY8c/
号
结果是:
氧化镁
附加说明然而,这种方法并非没有其自身的局限性。例如,与同一位置的另一个元素相比,使用较低的z索引测试的元素将被标识为隐藏,即使前面的元素实际上没有隐藏它的任何部分。不过,这种方法在某些情况下也有它的用途,但丹的解决方案并没有涵盖。
如果你想在元素周围测试更多的点以获得可见性——也就是说,要确保元素不被超过50%的覆盖——那么调整答案的最后一部分就不需要太多。但是,请注意,如果您检查每个像素以确保它100%可见,可能会非常慢。
我试过丹的答案,但是用于确定边界的代数意味着元素必须同时小于或等于视区大小,并且完全在视区内才能得到
1 2 3 4 5 6 7 8 | function isElementInViewport(el) { var rect = el.getBoundingClientRect(); return rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ && rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */; } |
号
作为公共服务:
丹的答案是正确的计算(元素可以是>窗口,特别是在手机屏幕上),正确的jquery测试,以及添加iselementPartiallyInViewport:
顺便说一下,window.innerwidth和document.documentelement.clientwidth的区别在于clientwidth/clientheight不包括滚动条,window.innerwidth/height包含滚动条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | function isElementPartiallyInViewport(el) { //special bonus for those using jQuery if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0]; var rect = el.getBoundingClientRect(); // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 } var windowHeight = (window.innerHeight || document.documentElement.clientHeight); var windowWidth = (window.innerWidth || document.documentElement.clientWidth); // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0); var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0); return (vertInView && horInView); } // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport function isElementInViewport (el) { //special bonus for those using jQuery if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0]; var rect = el.getBoundingClientRect(); var windowHeight = (window.innerHeight || document.documentElement.clientHeight); var windowWidth = (window.innerWidth || document.documentElement.clientWidth); return ( (rect.left >= 0) && (rect.top >= 0) && ((rect.left + rect.width) <= windowWidth) && ((rect.top + rect.height) <= windowHeight) ); } function fnIsVis(ele) { var inVpFull = isElementInViewport(ele); var inVpPartial = isElementPartiallyInViewport(ele); console.clear(); console.log("Fully in viewport:" + inVpFull); console.log("Partially in viewport:" + inVpPartial); } |
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> Test <!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"> <script src="scrollMonitor.js"> --> <script type="text/javascript"> function isElementPartiallyInViewport(el) { //special bonus for those using jQuery if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0]; var rect = el.getBoundingClientRect(); // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 } var windowHeight = (window.innerHeight || document.documentElement.clientHeight); var windowWidth = (window.innerWidth || document.documentElement.clientWidth); // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0); var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0); return (vertInView && horInView); } // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport function isElementInViewport (el) { //special bonus for those using jQuery if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0]; var rect = el.getBoundingClientRect(); var windowHeight = (window.innerHeight || document.documentElement.clientHeight); var windowWidth = (window.innerWidth || document.documentElement.clientWidth); return ( (rect.left >= 0) && (rect.top >= 0) && ((rect.left + rect.width) <= windowWidth) && ((rect.top + rect.height) <= windowHeight) ); } function fnIsVis(ele) { var inVpFull = isElementInViewport(ele); var inVpPartial = isElementPartiallyInViewport(ele); console.clear(); console.log("Fully in viewport:" + inVpFull); console.log("Partially in viewport:" + inVpPartial); } // var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft, // var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; </head> <body> <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" /> <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> t <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /> <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" /> <!-- <script type="text/javascript"> var element = document.getElementById("myele"); var watcher = scrollMonitor.create( element ); watcher.lock(); watcher.stateChange(function() { console.log("state changed"); // $(element).toggleClass('fixed', this.isAboveViewport) }); --> </body> </html> |
。
有一个名为inview的jquery插件可以完成该任务
请参阅使用getboundingclientrect的verge源。就像是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function inViewport (el) { var r, html; if ( !el || 1 !== el.nodeType ) { return false; } html = document.documentElement; r = el.getBoundingClientRect(); return ( !!r && r.bottom >= 0 && r.right >= 0 && r.top <= html.clientHeight && r.left <= html.clientWidth ); } |
。
如果元素的任何部分在视区中,则返回
我的更短更快的版本。
1 2 3 4 | function isElementOutViewport(el){ var rect = el.getBoundingClientRect(); return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight; } |
。
根据需要添加jsiddlehttps://jsfiddle.net/on1g619l/1/
我发现不存在以
八大冰八大潮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | $.fn.inView = function(){ if(!this.length) return false; var rect = this.get(0).getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }; //additional examples for other use cases //true false whether an array of elements are all in view $.fn.allInView = function(){ var all = []; this.forEach(function(){ all.push( $(this).inView() ); }); return all.indexOf(false) === -1; }; //only the class elements in view $('.some-class').filter(function(){ return $(this).inView(); }); //only the class elements not in view $('.some-class').filter(function(){ return !$(this).inView(); }); |
使用
1 2 3 4 5 6 7 | $(window).on('scroll',function(){ if( $('footer').inView() ) { // do cool stuff } }); |
。
我发现这里接受的答案对于大多数用例来说过于复杂。此代码(使用jquery)可以很好地完成工作,并区分完全可见和部分可见元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var element = $("#element"); var topOfElement = element.offset().top; var bottomOfElement = element.offset().top + element.outerHeight(true); var $window = $(window); $window.bind('scroll', function() { var scrollTopPosition = $window.scrollTop()+$window.height(); var windowScrollTop = $window.scrollTop() if( windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) { // Element is partially visible (above viewable area) console.log("Element is partially visible (above viewable area)"); }else if( windowScrollTop > bottomOfElement && windowScrollTop > topOfElement ) { // Element is hidden (above viewable area) console.log("Element is hidden (above viewable area)"); }else if( scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement ) { // Element is hidden (below viewable area) console.log("Element is hidden (below viewable area)"); }else if( scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement ) { // Element is partially visible (below viewable area) console.log("Element is partially visible (below viewable area)"); }else{ // Element is completely visible console.log("Element is completely visible"); } }); |
我在这里遇到的所有答案都只检查元素是否位于当前视区内。但这并不意味着它是可见的。如果给定的元素在一个包含溢出内容的DIV中,并且滚动到视图之外,该怎么办?
要解决这个问题,您必须检查元素是否由所有父元素包含。我的解决方案就是这样:
它还允许您指定元素的可见程度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Element.prototype.isVisible = function(percentX, percentY){ var tolerance = 0.01; //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals if(percentX == null){ percentX = 100; } if(percentY == null){ percentY = 100; } var elementRect = this.getBoundingClientRect(); var parentRects = []; var element = this; while(element.parentElement != null){ parentRects.push(element.parentElement.getBoundingClientRect()); element = element.parentElement; } var visibleInAllParents = parentRects.every(function(parentRect){ var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); var visiblePercentageX = visiblePixelX / elementRect.width * 100; var visiblePercentageY = visiblePixelY / elementRect.height * 100; return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY; }); return visibleInAllParents; }; |
。
该解决方案忽略了这样一个事实,即元素可能由于其他事实而不可见,如
我在Chrome和Internet Explorer 11中测试过这个解决方案。
新的交叉口观察器API非常直接地解决了这个问题。
这个解决方案需要一个多填充,因为Safari,Opera和IE还不支持这个。(解决方案中包含polyfill)。
在这个解决方案中,有一个不可见的框是目标(观察到的)。当它进入视图时,标题顶部的按钮被隐藏。一旦盒子离开视图,它就会显示出来。
1 2 3 4 5 6 7 8 9 10 11 | const buttonToHide = document.querySelector('button'); const hideWhenBoxInView = new IntersectionObserver((entries) => { if (entries[0].intersectionRatio <= 0) { // If not in view buttonToHide.style.display ="inherit"; } else { buttonToHide.style.display ="none"; } }); hideWhenBoxInView.observe(document.getElementById('box')); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | header { position: fixed; top: 0; width: 100vw; height: 30px; background-color: lightgreen; } .wrapper { position: relative; margin-top: 600px; } #box { position: relative; left: 175px; width: 150px; height: 135px; background-color: lightblue; border: 2px solid; } |
号
1 2 3 4 | <script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"> <header> <button>NAVIGATION BUTTON TO HIDE</button> </header> |
我认为这是一种更实用的方法。丹的答案在递归上下文中不起作用。
这个函数通过递归地测试HTML标记上的任何级别来解决当元素位于其他可滚动div中时的问题,并在第一个false中停止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * fullVisible=true only returns true if the all object rect is visible */ function isReallyVisible(el, fullVisible) { if ( el.tagName =="HTML" ) return true; var parentRect=el.parentNode.getBoundingClientRect(); var rect = arguments[2] || el.getBoundingClientRect(); return ( ( fullVisible ? rect.top >= parentRect.top : rect.bottom > parentRect.top ) && ( fullVisible ? rect.left >= parentRect.left : rect.right > parentRect.left ) && ( fullVisible ? rect.bottom <= parentRect.bottom : rect.top < parentRect.bottom ) && ( fullVisible ? rect.right <= parentRect.right : rect.left < parentRect.right ) && isReallyVisible(el.parentNode, fullVisible, rect) ); }; |
。
基于上面的@dan's解决方案(https://stackoverflow.com/a/7557433/5628),我尝试了清理实现,以便在同一页上多次使用它更容易:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | $(function() { $(window).on('load resize scroll', function() { addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon'); addClassToElementInViewport($('.another-thing'), 'animate-thing'); // ?? repeat as needed ... }); function addClassToElementInViewport(element, newClass) { if (inViewport(element)) { element.addClass(newClass); } } function inViewport(element) { if (typeof jQuery ==="function" && element instanceof jQuery) { element = element[0]; } var elementBounds = element.getBoundingClientRect(); return ( elementBounds.top >= 0 && elementBounds.left >= 0 && elementBounds.bottom <= $(window).height() && elementBounds.right <= $(window).width() ); } }); |
我使用它的方式是,当元素滚动到视图中时,我添加一个类来触发一个CSS关键帧动画。它非常简单,当你在一个页面上有10个以上的内容需要有条件地进行动画制作时尤其有效。
希望有帮助!
尽可能简单地实现IMO:
1 2 3 4 | function isVisible(elem) { var coords = elem.getBoundingClientRect(); return Math.abs(coords.top) <= coords.height; } |
。
这是我的解决方案,如果一个元素隐藏在一个可滚动的容器中,它就会工作。
这是一个演示(请尝试重新调整窗口大小)
1 2 3 4 5 6 7 8 9 10 11 | var visibleY = function(el){ var top = el.getBoundingClientRect().top, rect, el = el.parentNode; do { rect = el.getBoundingClientRect(); if (top <= rect.bottom === false) return false; el = el.parentNode; } while (el != document.body); // Check its within the document viewport return top <= document.documentElement.clientHeight; }; |
我只需要检查它在Y轴上是否可见(对于滚动Ajax加载更多记录功能)。
对我有用的简单而小的解决方案。
示例:要查看元素是否在具有溢出滚动的父元素中可见。
1 2 3 4 5 6 7 8 9 10 11 12 | $(window).on('scroll', function () { var container = $('#sidebar'); var containerHeight = container.height(); var scrollPosition = $('#row1').offset().top - container.offset().top; if (containerHeight < scrollPosition) { console.log('not visible'); } else { console.log('visible'); } }) |
。
我有同样的问题,并通过使用getboundingclientrect()解决了它。这段代码是完全"通用"的,只需要编写一次代码就可以工作(您不必为要知道的每个元素都在视区中编写它)。此代码只检查它是否在视区中垂直,而不是水平。在这种情况下,变量(数组)"elements"保存要在视区中垂直检查的所有元素,因此可以在任何位置抓取任何需要的元素并将其存储在该位置。"for循环"循环遍历每个元素,并检查它是否在视区中垂直。每次用户滚动时都会执行此代码!如果getboudingclientrect().top小于视区的3/4(视区中的元素是四分之一),则它注册为"在视区中"。因为代码是通用的,所以您需要知道"哪个"元素在视区中。要了解这一点,您可以通过自定义属性、节点名、ID、类名等来确定它。这是我的代码(告诉我,如果它不起作用,它已经在IE 11、Firefox 40.0.3、Chrome 45.0.2454.85 m、Opera 31.0.1889.174和Edge with Windows 10中测试过,【还没有Safari】)。
1 2 3 4 5 6 7 8 9 10 | //scrolling handlers... window.onscroll = function(){ var elements = document.getElementById('whatever').getElementsByClassName('whatever'); for(var i = 0; i != elements.length; i++) { if(elements[i].getBoundingClientRect().top <= window.innerHeight*0.75 && elements[i].getBoundingClientRect().top > 0) { console.log(elements[i].nodeName + ' ' + elements[i].className + ' ' + elements[i].id + ' is in the viewport; proceed with whatever code you want to do here.'); } }; |
。
希望这能帮助别人:-)
更好的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function getViewportSize(w) { var w = w || window; if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight}; var d = w.document; if (document.compatMode =="CSS1Compat") { return { w: d.documentElement.clientWidth, h: d.documentElement.clientHeight }; } return { w: d.body.clientWidth, h: d.body.clientWidth }; } function isViewportVisible(e) { var box = e.getBoundingClientRect(); var height = box.height || (box.bottom - box.top); var width = box.width || (box.right - box.left); var viewport = getViewportSize(); if(!height || !width) return false; if(box.top > viewport.h || box.bottom < 0) return false; if(box.right < 0 || box.left > viewport.w) return false; return true; } |
。
下面是一个函数,用于指示某个元素是否在父元素的当前视区中可见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function inParentViewport(el, pa) { if (typeof jQuery ==="function"){ if (el instanceof jQuery) el = el[0]; if (pa instanceof jQuery) pa = pa[0]; } var e = el.getBoundingClientRect(); var p = pa.getBoundingClientRect(); return ( e.bottom >= p.top && e.right >= p.left && e.top <= p.bottom && e.left <= p.right ); } |
号
检查元素是否至少部分位于视图中(垂直尺寸):
1 2 3 4 5 6 7 8 9 10 11 12 13 | function inView(element) { var box = element.getBoundingClientRect(); return inViewBox(box); } function inViewBox(box) { return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true; } function getWindowSize() { return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight} } |
号
我使用这个功能(它只检查Y是否在屏幕上,因为大多数时候不需要X)
1 2 3 4 5 6 7 8 9 10 11 12 13 | function elementInViewport(el) { var elinfo = { "top":el.offsetTop, "height":el.offsetHeight, }; if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) { return false; } else { return true; } } |
对于类似的挑战,我真的很喜欢这个要点,它为ScrollIntoviewIfneeded()公开了一个polyfill。
所有需要回答的武功都在这个街区内:
1 2 3 4 5 6 7 8 9 | var parent = this.parentNode, parentComputedStyle = window.getComputedStyle(parent, null), parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')), parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')), overTop = this.offsetTop - parent.offsetTop < parent.scrollTop, overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight), overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft, overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth), alignWithTop = overTop && !overBottom; |