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-index
overflow-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
표류해야합니다 ...