HTML 문서에서 DOM 요소가 현재 표시되는지 ( 뷰포트 에 표시) 알 수있는 효율적인 방법이 있습니까?
(질문은 Firefox에 관한 것입니다.)
HTML 문서에서 DOM 요소가 현재 표시되는지 ( 뷰포트 에 표시) 알 수있는 효율적인 방법이 있습니까?
(질문은 Firefox에 관한 것입니다.)
답변:
업데이트 : 시간이 흐르고 브라우저도 있습니다. 이 기술은 더 이상 권장하지 않습니다 그리고 당신은 사용해야 단의 솔루션을 사용하면 7 전에 인터넷 익스플로러의 지원 버전이 필요하지 않은 경우.
독창적 인 솔루션 (현재 구식) :
현재 뷰포트에서 요소가 완전히 보이는지 확인합니다.
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)
);
}
뷰포트에 요소의 일부가 보이는지 확인하기 위해 간단히 수정할 수 있습니다.
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
);
}
getBoundingClientRect요소 좌표를 찾기 위해 특별히 만들었습니다 . 왜 우리는 이것을 사용하지 않습니까?
이제 대부분의 브라우저 는 getBoundingClientRect 메소드를 지원 하며, 이는 모범 사례가되었습니다. 오래된 대답을 사용하여입니다 매우 느리게 , 정확하지 및 여러 버그가 있습니다 .
올바른 것으로 선택한 솔루션은 거의 정확하지 않습니다 . 버그에 대한 자세한 내용을 읽을 수 있습니다 .
이 솔루션은 Internet Explorer 7 이상, iOS 5 이상, Safari, Android 2.0 (Eclair) 이상, BlackBerry, Opera Mobile 및 Internet Explorer Mobile 9 에서 테스트되었습니다 .
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() */
);
}
위에서 주어진 함수가 호출 될 때 정답을 반환하는지 확인할 수 있지만 이벤트로서 요소의 가시성을 추적하는 것은 어떻습니까?
<body>태그 하단에 다음 코드를 배치하십시오 .
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); // Internet Explorer 9+ :(
attachEvent('onload', handler);
attachEvent('onscroll', handler);
attachEvent('onresize', handler);
}
*/
DOM 수정을 수행하면 요소의 가시성을 변경할 수 있습니다.
지침 및 일반적인 함정 :
아마 당신은 페이지 확대 / 모바일 장치 핀치를 추적해야합니까? jQuery는 줌 / 핀치 크로스 브라우저를 처리해야 합니다. 그렇지 않으면 첫 번째 또는 두 번째 링크가 도움이됩니다.
DOM 을 수정 하면 요소의 가시성에 영향을 줄 수 있습니다. 이를 제어하고 handler()수동으로 호출 해야합니다. 불행히도, 우리는 크로스 브라우저 onrepaint이벤트 가 없습니다 . 반면에 요소의 가시성을 변경할 수있는 DOM 수정에 대해서만 최적화하고 재확인을 수행 할 수 있습니다.
이 시점에서 CSS가 적용되었다는 보장이 없으므로 jQuery $ (document) .ready () 에서만 사용 하지 마십시오 . 코드는 하드 드라이브에서 CSS와 로컬로 작동 할 수 있지만 일단 원격 서버에 넣으면 실패합니다.
후에 DOMContentLoaded는 스타일이 적용 되지만 이미지는 아직로드되지 않습니다 . 따라서 window.onload이벤트 리스너를 추가해야합니다 .
아직 줌 / 핀치 이벤트를 잡을 수 없습니다.
마지막 수단은 다음 코드 일 수 있습니다.
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
웹 페이지가있는 탭이 활성화되어 있고 가시적인지 신경 쓰면 HTML5 API 의 멋진 기능 pageVisibiliy 를 사용할 수 있습니다.
TODO :이 방법은 두 가지 상황을 처리하지 않습니다.
z-indexoverflow-scroll요소의 컨테이너에서 사용return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))경우 jQuery 객체 (예 :)가 아니라 HTML DOM 객체 (예 :)를 전달해야합니다 isElementInViewport($("#elem)). jQuery와 동등한 것은 다음 [0]과 같이 추가하는 것 isElementInViewport($("#elem)[0])입니다.
el is not defined
최신 브라우저에서는 다음과 같은 이점을 제공하는 Intersection Observer API 를 확인할 수 있습니다 .
Intersection Observer는 본격적인 표준으로 나아가고 있으며 Chrome 51 이상, Edge 15 이상 및 Firefox 55 이상에서 이미 지원되며 Safari 용으로 개발 중입니다. 도있다 polyfill 이 없습니다.
Dan 이 제공 한 답변 에는 일부 상황에 적합하지 않은 문제 가있을 수 있습니다. 이러한 문제 중 일부는 하단 근처의 답변에서 지적되었으므로 그의 코드는 다음과 같은 요소에 대해 잘못된 긍정을 제공합니다.
clip속성 을 사용하여 숨겨진 요소 또는 하위 요소이러한 제한 사항은 다음과 같은 간단한 테스트 결과에서 설명됩니다 .

isElementVisible()아래 테스트 결과와 코드의 일부에 대한 설명과 함께 이러한 문제에 대한 해결책이 있습니다.
function isElementVisible(el) {
var rect = el.getBoundingClientRect(),
vWidth = window.innerWidth || document.documentElement.clientWidth,
vHeight = window.innerHeight || document.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))
);
}
합격 시험 : http://jsfiddle.net/AndyE/cAY8c/
그리고 결과 :

그러나이 방법에는 고유 한 제한이 없습니다. 예를 들어, 동일한 위치에있는 다른 요소보다 낮은 z- 색인으로 테스트중인 요소는 앞에있는 요소가 실제로 일부를 숨기지 않더라도 숨겨진 것으로 식별됩니다. 여전히이 방법은 Dan의 솔루션에서 다루지 않는 경우에 사용됩니다.
모두 element.getBoundingClientRect()와 document.elementFromPoint()CSSOM 작업 초안 사양의 일부이며 이후 적어도 IE 6에서 지원하고, 가장 오랫동안 데스크탑 브라우저 (하지 완벽하게이기는하지만). 자세한 내용 은이 기능에 대한 Quirksmode 를 참조하십시오.
contains()에서 반환 한 document.elementFromPoint()요소가 가시성 테스트중인 요소의 하위 노드 인지 확인하는 데 사용됩니다 . 리턴 된 요소가 동일한 요소 인 경우에도 true를 리턴합니다. 이것은 단지 수표를 더욱 강력하게 만듭니다. 모든 주요 브라우저에서 지원되며 Firefox 9.0이 마지막으로 추가됩니다. 이전 Firefox 지원에 대해서는이 답변의 기록을 확인하십시오.
가시성을 위해 요소 주위에 더 많은 점을 테스트하려는 경우 (예 : 요소가 50 % 이상으로 덮여 있지 않은지 확인하려면) 응답의 마지막 부분을 조정하는 데 많은 시간이 걸리지 않습니다. 그러나 모든 픽셀을 100 % 볼 수 있는지 확인하면 속도가 매우 느려질 수 있습니다.
doc대한 별칭 으로 사용했던 코드에서 바로 복사했습니다 document. 예, 나는 이것이 엣지 케이스에 알맞은 솔루션이라고 생각합니다.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
나는 Dan의 대답을 시도했다 . 그러나 경계를 결정하는 데 사용되는 대수는 요소가 뷰포트 크기보다 작고 뷰포트 내부에 완전히 있어야해서 true쉽게 잘못된 결과 를 가져옵니다 . 요소가 뷰포트에 있는지 여부를 결정하려면 ryanve의 답변 이 가깝지만 테스트중인 요소가 뷰포트와 겹치므로 다음을 시도하십시오.
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() */;
}
공공 서비스 :
Dan은 올바른 계산 (특히 휴대 전화 화면에서> 창일 수 있음), 올바른 jQuery 테스트 및 isElementPartiallyInViewport 추가와 함께 대답합니다.
그런데 window.innerWidth와 document.documentElement.clientWidth 의 차이점 은 clientWidth / clientHeight는 스크롤 막대를 포함하지 않지만 window.innerWidth / Height는 스크롤 막대를 포함하지 않는다는 것입니다.
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);
}
<!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="">
<title>Test</title>
<!--
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="scrollMonitor.js"></script>
-->
<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;
</script>
</head>
<body>
<div style="display: block; width: 2000px; height: 10000px; background-color: green;">
<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 />
<div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
<div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
t
</div>
<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" />
</div>
<!--
<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)
});
</script>
-->
</body>
</html>
isElementPartiallyInViewport매우 유용합니다. 좋은데
getBoundingClientRect 를 사용하는 verge 소스를 참조하십시오 . 그것은 다음과 같습니다
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
);
}
그것은 반환하는 true경우 어떤 요소의 일부가 뷰포트에 있습니다.
더 짧고 빠른 버전 :
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
그리고 필요에 따라 jsFiddle : https://jsfiddle.net/on1g619L/1/
사용 가능한 기능 의 jQuery 중심 버전 이 없다는 것이 문제를 발견했습니다 . I가 건너 왔을 때 단의 솔루션 내가 jQuery를 OO 스타일의 프로그램을 좋아하는 사람들을 위해 뭔가를 제공 할 수있는 기회를 감시했다. 멋지고 빠르며 매력처럼 작동합니다.
바다 빙 바다 붐
$.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
// Is 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();
});
용법
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
새로운 교차 관찰자 API는 이 질문을 매우 직접적으로 해결합니다.
Safari, Opera 및 Internet Explorer에서 아직이 기능을 지원하지 않기 때문에이 솔루션에는 폴리 필이 필요합니다 (폴리 필은 솔루션에 포함되어 있음).
이 솔루션에는 대상 (관찰되는) 상자가 보이지 않습니다. 보이면 머리글 상단의 단추가 숨겨집니다. 상자가보기를 벗어나면 표시됩니다.
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'));
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;
}
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<header>
<button>NAVIGATION BUTTON TO HIDE</button>
</header>
<div class="wrapper">
<div id="box">
</div>
</div>
IntersectionObserver실험 기능 (향후 변경 될 수 있음)이다.
IntersectionObserver유일한 루트 대상의 이동 후 콜백을 발생합니다.
observe이벤트가 발생 하면 즉시 추적 된 요소의 현재 교차 상태를 알려줍니다. 그래서 어떤 식 으로든-해결합니다.
내가 본 모든 대답은 요소가 현재 뷰포트 안에 있는지 확인합니다 . 그러나 그것은 그것을 볼 것을 의미하지 않는다 .
주어진 요소가 넘친 콘텐츠가있는 div 안에 있고 스크롤되지 않은 경우 어떻게됩니까?
이를 해결하려면 모든 부모가 요소를 포함하고 있는지 확인해야합니다.
내 솔루션은 정확히 다음을 수행합니다.
또한 요소의 표시량을 지정할 수 있습니다.
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;
};
이 솔루션은와 같은 다른 사실로 인해 요소가 표시되지 않을 수 있다는 사실을 무시했습니다 opacity: 0.
Chrome 및 Internet Explorer 11에서이 솔루션을 테스트했습니다.
여기에 허용되는 답변이 대부분의 사용 사례에서 지나치게 복잡하다는 것을 알았습니다. 이 코드는 jQuery를 사용하여 잘 작동하며 완전히 보이는 요소와 부분적으로 보이는 요소를 구분합니다.
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");
}
});
$window = $(window)스크롤 핸들러 외부에 캐시해야 합니다.
가장 간단한 솔루션 의 지원과 같은 Element.getBoundingClientRect은 () 한 완벽한 될 :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
나는 이것이 더 기능적인 방법이라고 생각합니다. Dan의 대답 은 재귀 적 컨텍스트에서 작동하지 않습니다.
이 함수는 요소가 HTML 태그까지 재귀 적으로 레벨을 테스트하여 스크롤 가능한 div 안에있을 때 문제를 해결하고 첫 번째 거짓에서 멈 춥니 다.
/**
* 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)
);
};
Android에서 Chrome을 확대 / 축소 할 때 가장 많이 사용되는 답변이 작동하지 않습니다. Dan의 답변 과 함께 Android의 Chrome을 설명 하려면 visualViewport를 사용해야합니다. 다음 예제는 세로 검사 만 고려하고 창 높이에 jQuery를 사용합니다.
var Rect = YOUR_ELEMENT.getBoundingClientRect();
var ElTop = Rect.top, ElBottom = Rect.bottom;
var WindowHeight = $(window).height();
if(window.visualViewport) {
ElTop -= window.visualViewport.offsetTop;
ElBottom -= window.visualViewport.offsetTop;
WindowHeight = window.visualViewport.height;
}
var WithinScreen = (ElTop >= 0 && ElBottom <= WindowHeight);
여기 내 해결책이 있습니다. 스크롤 가능한 컨테이너 안에 요소가 숨겨져 있으면 작동합니다.
여기 데모가 있습니다 (창 크기를 조정하십시오)
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 it's within the document viewport
return top <= document.documentElement.clientHeight;
};
Y 축에 보이는지 확인하기 만하면됩니다 (스크롤 Ajax로드 더 많은 레코드 기능).
dan의 솔루션을 기반으로 동일한 페이지에서 여러 번 사용하는 것이 더 쉽도록 구현을 정리했습니다.
$(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 가지 이상의 것들이있을 때 특히 효과적입니다.
$window = $(window)스크롤 핸들러 외부에 캐시해야 합니다
이전 답변에서 대부분의 사용법은 다음 시점에서 실패합니다.
-요소의 픽셀이 보이지만 " 코너 "가 아닌 경우 ,
- 언제 요소가 뷰포트보다 큰과를 중심으로 ,
-그들 중 대부분은 문서 또는 창 내부의 단일 요소 만 검사합니다 .
글쎄,이 모든 문제에 대해 해결책을 찾았으며 장점은 다음과 같습니다.
-
visible어느면에서든 픽셀 만 나타나고 모서리가 아닌 경우 돌아올 수 있습니다 .-
visible요소가 뷰포트보다 큰 동안에도 계속 돌아올 수 있습니다 .-당신은 당신을 선택
parent element하거나 자동으로 선택하도록 할 수 있습니다,-Works에 동적으로 추가 요소 도.
아래의 스 니펫을 확인하면 overflow-scroll요소의 컨테이너 에서 사용하는 차이가 문제를 일으키지 않으며 픽셀이 어느 쪽에서 나 또는 요소가 뷰포트보다 크고 내부 가 보이는 경우에도 다른 답변과 달리 이를 볼 수 있습니다 여전히 작동 하는 요소의 픽셀 .
사용법은 간단합니다.
// For checking element visibility from any sides
isVisible(element)
// For checking elements visibility in a parent you would like to check
var parent = document; // Assuming you check if 'element' inside 'document'
isVisible(element, parent)
// For checking elements visibility even if it's bigger than viewport
isVisible(element, null, true) // Without parent choice
isVisible(element, parent, true) // With parent choice
crossSearchAlgorithm뷰포트 검사 element3 내부 픽셀보다 큰 요소에 유용한 데모 는 다음과 같습니다.
당신은 당신이 때, 참조 element3 내부에 그것을 표시의 여부를 경우 요소에서 볼 수있는 경우에만 확인하기 때문에, 말 못하는 측면 이나 모서리 .
그리고 여기에는 요소가 뷰포트보다 클 때 crossSearchAlgorithm여전히 반환 visible할 수있는 기능 이 포함 되어 있습니다 .
JSFiddle과 함께 사용 : http://jsfiddle.net/BerkerYuceer/grk5az2c/
이 코드는 요소의 일부가보기에 표시되는지 여부에 대한보다 정확한 정보를 제공하기 위해 작성되었습니다. 성능 옵션 또는 세로 슬라이드에만 사용하십시오! 이 코드는 그리기 사례에서 더 효과적입니다.
더 나은 해결책 :
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;
}
다음은 부모 요소 의 현재 뷰포트에 요소가 보이는지 알려주는 함수입니다 .
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
);
}
이것은 요소가 적어도 부분적으로 보이는지 확인합니다 (수직 치수).
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}
}
나는 같은 질문을했고 getBoundingClientRect ()를 사용하여 알아 냈습니다.
이 코드는 완전히 '일반적'이며 작동하기 위해 한 번만 작성하면됩니다 (알고 싶은 각 요소에 대해 작성할 필요는 없습니다).
이 코드는 수평이 아닌 뷰포트에 수직인지 확인합니다 . 이 경우 변수 (배열) 'elements'는 뷰포트에서 세로로 확인하려는 모든 요소를 보유하므로 원하는 모든 요소를 가져 와서 거기에 저장하십시오.
'for loop'는 각 요소를 반복하고 뷰포트에 수직인지 확인합니다. 이 코드 는 사용자가 스크롤 할 때마다 실행 됩니다 ! getBoudingClientRect (). top이 뷰포트의 3/4보다 작 으면 (요소가 뷰포트의 1/4 임) '뷰포트에'로 등록됩니다.
코드는 일반적이므로 'port'요소가 뷰포트에 있는지 알고 싶을 것입니다. 이를 알아보기 위해 사용자 정의 속성, 노드 이름, ID, 클래스 이름 등으로 확인할 수 있습니다.
내 코드는 다음과 같습니다 (작동하지 않는 경우 알려주십시오. Internet Explorer 11, Firefox 40.0.3, Chrome 버전 45.0.2454.85 m, Opera 31.0.1889.174 및 Windows 10의 Edge에서 아직 테스트되지 않았습니다. ]) ...
// 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.');
}
};
이것은 나를 위해 일한 쉽고 작은 솔루션입니다.
예 : 오버 플로우 스크롤이있는 상위 요소에 요소가 표시되는지 확인하려고합니다.
$(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');
}
})
여기에있는 모든 대답은 요소가 뷰포트에 완전히 포함되어 있는지 여부를 결정합니다. 예를 들어,보기의 맨 아래에 이미지의 절반 만 보이는 경우 "외부"를 고려하면 여기의 해결 방법이 실패합니다.
를 통해 지연 로딩을 수행하는 유스 케이스가 IntersectionObserver있었지만 팝업 중에 발생하는 애니메이션으로 인해 페이지로드에서 이미 교차 된 이미지를 관찰하고 싶지 않았습니다 . 이를 위해 다음 코드를 사용했습니다.
const bounding = el.getBoundingClientRect();
const isVisible = (0 < bounding.top && bounding.top < (window.innerHeight || document.documentElement.clientHeight)) ||
(0 < bounding.bottom && bounding.bottom < (window.innerHeight || document.documentElement.clientHeight));
이것은 기본적으로 상단 또는 하단 경계가 독립적으로 뷰포트에 있는지 확인합니다. 반대쪽 끝은 외부에있을 수 있지만 한쪽 끝이있는 한 적어도 부분적으로 "보이는"것입니다.
이 기능을 사용합니다 (x는 필요하지 않기 때문에 y가 화면에 있는지 확인합니다)
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을 노출시키는 이 요점 을 정말로 즐겼습니다 .
대답하는 데 필요한 모든 쿵푸는이 블록 내에 있습니다 :
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;
this예를 들어 알고 싶은 요소를 참조 overTop하거나 overBottom표류해야합니다 ...