현재 뷰포트에서 DOM 요소가 보이는지 어떻게 알 수 있습니까?


949

HTML 문서에서 DOM 요소가 현재 표시되는지 ( 뷰포트 에 표시) 알 수있는 효율적인 방법이 있습니까?

(질문은 Firefox에 관한 것입니다.)


보이는 의미에 따라 다릅니다. 스크롤 위치가 주어진 경우 현재 페이지에 표시되어 있음을 의미하는 경우 요소 y 오프셋 및 현재 스크롤 위치를 기준으로 계산할 수 있습니다.
roryf

18
설명 : 현재 표시된 사각형 내에서와 같이 표시됩니다. 숨겨 지거나 표시되지 않는 것처럼 보이기 : 없음? Z 순서에서 다른 것 뒤에 보이지 않는 것처럼 보이나요?
Adam Wright

6
현재 표시된 사각형 내에서와 같이. 감사. 환호했다.
benzaita

2
여기의 모든 답변은 부모 요소의 보이는 영역 밖에 있지만 (뷰 오버플로가 숨겨져 있음) 뷰포트 영역 안에있는 항목에 대해서는 오 탐지를 나타냅니다.
Andy E

1
백만 개의 답변이 있으며 대부분 엄청나게 길다. 2 라이너
leonheess 여기

답변:


346

업데이트 : 시간이 흐르고 브라우저도 있습니다. 이 기술은 더 이상 권장하지 않습니다 그리고 당신은 사용해야 단의 솔루션을 사용하면 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
  );
}

1
게시 된 원래 기능에 실수가있었습니다. 엘을 재 할당하기 전에 너비 / 높이를 저장해야합니다 ...
Prestaul

15
요소가 스크롤 가능한 div에 있고 뷰에서 스크롤되면 어떻게됩니까?
amartynov

2
아래 스크립트의 최신 버전을 검토하십시오
Dan

1
@amartynov의 질문에 대해서도 궁금합니다. 누구든지 조상 요소의 오버플로로 인해 요소가 숨겨져 있는지 단순히 말하는 방법을 알고 있습니까? 아이가 얼마나 깊게 중첩되어 있는지에 관계없이 이것이 감지 될 수 있다면 보너스.
Eric Nguyen

1
DOM을 통해 반복되는 @deadManN은 매우 느립니다. 그 이유는 충분하지만 브라우저 공급 업체는 getBoundingClientRect요소 좌표를 찾기 위해 특별히 만들었습니다 . 왜 우리는 이것을 사용하지 않습니까?
Prestaul

1402

이제 대부분의 브라우저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요소의 컨테이너에서 사용
  • 새로운 것을 시도하십시오- 교차 관찰자 API 설명

6
이 솔루션을 사용하고 있습니다 ( "하단"오타에주의하십시오). 우리가 고려하는 요소에 이미지가있을 때 알아야 할 것이 있습니다. Chrome은 (적어도) 이미지가로드 될 때까지 기다려야 boundingRectangle의 정확한 값을 갖습니다. Firefox에이 "문제"가없는 것 같습니다
Claudio

4
본문 내부의 컨테이너에서 스크롤을 사용하도록 설정하면 작동합니까? 예를 들어, 여기서 작동하지 않습니다 -agaase.github.io/webpages/demo/isonscreen2.html isElementInViewport (document.getElementById ( "innerele")). innerele은 스크롤이 가능한 컨테이너 안에 있습니다.
agaase

70
계산에서는 요소가 화면보다 작다고 가정합니다. 높거나 넓은 요소가있는 경우 사용하는 것이 더 정확할 수 있습니다.return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
Roonaan

11
팁 : jQuery를 사용하여이를 구현하려는 isElementInViewport(document.getElementById('elem'))경우 jQuery 객체 (예 :)가 아니라 HTML DOM 객체 (예 :)를 전달해야합니다 isElementInViewport($("#elem)). jQuery와 동등한 것은 다음 [0]과 같이 추가하는 것 isElementInViewport($("#elem)[0])입니다.
jiminy

11
el is not defined
M_Willett

175

최신 정보

최신 브라우저에서는 다음과 같은 이점을 제공하는 Intersection Observer API 를 확인할 수 있습니다 .

  • 스크롤 이벤트를 듣는 것보다 나은 성능
  • 교차 도메인 iframe에서 작동
  • 요소가 다른 요소를 방해하거나 교차하는지 알 수 있습니다.

Intersection Observer는 본격적인 표준으로 나아가고 있으며 Chrome 51 이상, Edge 15 이상 및 Firefox 55 이상에서 이미 지원되며 Safari 용으로 개발 중입니다. 도있다 polyfill 이 없습니다.


이전 답변

Dan제공 한 답변 에는 일부 상황에 적합하지 않은 문제 가있을 수 있습니다. 이러한 문제 중 일부는 하단 근처의 답변에서 지적되었으므로 그의 코드는 다음과 같은 요소에 대해 잘못된 긍정을 제공합니다.

  • 테스트중인 요소 앞에 다른 요소가 숨겨져 있음
  • 부모 또는 조상 요소의 가시 영역 외부
  • CSS clip속성 을 사용하여 숨겨진 요소 또는 하위 요소

이러한 제한 사항은 다음과 같은 간단한 테스트 결과에서 설명됩니다 .

isElementInViewport를 사용한 테스트 실패

해결책: 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/

그리고 결과 :

isElementVisible을 사용한 테스트 통과

추가 사항

그러나이 방법에는 고유 한 제한이 없습니다. 예를 들어, 동일한 위치에있는 다른 요소보다 낮은 z- 색인으로 테스트중인 요소는 앞에있는 요소가 실제로 일부를 숨기지 않더라도 숨겨진 것으로 식별됩니다. 여전히이 방법은 Dan의 솔루션에서 다루지 않는 경우에 사용됩니다.

모두 element.getBoundingClientRect()document.elementFromPoint()CSSOM 작업 초안 사양의 일부이며 이후 적어도 IE 6에서 지원하고, 가장 오랫동안 데스크탑 브라우저 (하지 완벽하게이기는하지만). 자세한 내용 은이 기능에 대한 Quirksmode 를 참조하십시오.

contains()에서 반환 한 document.elementFromPoint()요소가 가시성 테스트중인 요소의 하위 노드 인지 확인하는 데 사용됩니다 . 리턴 된 요소가 동일한 요소 인 경우에도 true를 리턴합니다. 이것은 단지 수표를 더욱 강력하게 만듭니다. 모든 주요 브라우저에서 지원되며 Firefox 9.0이 마지막으로 추가됩니다. 이전 Firefox 지원에 대해서는이 답변의 기록을 확인하십시오.

가시성을 위해 요소 주위에 더 많은 점을 테스트하려는 경우 (예 : 요소가 50 % 이상으로 덮여 있지 않은지 확인하려면) 응답의 마지막 부분을 조정하는 데 많은 시간이 걸리지 않습니다. 그러나 모든 픽셀을 100 % 볼 수 있는지 확인하면 속도가 매우 느려질 수 있습니다.


2
doc.documentElement.clientWidth를 사용 하시겠습니까? 대신 'document.documentElement'여야합니까? 다른 말로
Kevin Lamping

@ 클램핑 : 좋은 캐치, 감사합니다. 에 doc대한 별칭 으로 사용했던 코드에서 바로 복사했습니다 document. 예, 나는 이것이 엣지 케이스에 알맞은 솔루션이라고 생각합니다.
Andy E

2
나를 위해 그것은 작동하지 않습니다. 그러나 이전 답변의 inViewport ()는 FF에서 작동합니다.
Satya Prakash

9
경계 모서리가 예상 요소를 반환하지 않을 수 있으므로 둥근 모서리 또는 변형이 적용된 경우 요소의 중심이 보이는지 확인하는 것이 좋습니다.element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Jared

2
나를 위해 입력 작업을하지 못했습니다 (크롬 카나리아 50). 왜 모퉁이가 더 둥근 지 모릅니다. el.contains (efp (rect.left + 1, rect.top + 1))을 작동 시키려면 좌표를 약간 줄여야했습니다. || el.contains (efp (rect.right-1, rect.top + 1)) || el.contains (efp (rect.right-1, rect.bottom-1)) || el.contains (efp (rect.left + 1, rect.bottom-1))
Rayjax

65

나는 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() */;
}

33

공공 서비스 :
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>

2
isElementPartiallyInViewport매우 유용합니다. 좋은데
RJ Cuthbertson

isElementInViewport (e)의 경우 : 코드가 이미지를로드하지도 않습니다. 이것은 올바른 것입니다. function isElementInViewport (e) {var t = e.getBoundingClientRect (); return t.top> = 0 && t.left> = 0 && t.bottom <= (window.innerHeight || document.documentElement.clientHeight) && t.right <= (window.innerWidth || document.documentElement.clientWidth) }
Arun chauhan

1
@ Arun chauhan : 내 코드 중 어느 것도 이미지를로드하지 않으므로 그 이유는 무엇이며 수식이 정확합니다.
Stefan Steiger

1
@targumon : 이유는 이전 브라우저를 지원하기 때문입니다.
Stefan Steiger

1
MDN에 따르면 @StefanSteiger는 IE9부터 지원되므로 window.innerHeight를 직접 사용하는 것이 실제로 안전합니다 (적어도 필자의 경우). 감사!
targumon

28

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경우 어떤 요소의 일부가 뷰포트에 있습니다.


27

더 짧고 빠른 버전 :

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/


4
내 솔루션은 더 탐욕스럽고 빠릅니다. 요소에 viewport에 픽셀이 있으면 false를 반환합니다.
Eric Chen

1
나는 그것을 좋아한다. 간결한. 첫 번째 줄에서 함수 이름과 괄호 사이, 공백과 괄호 사이의 공백을 제거 할 수 있습니다. 그 공간을 좋아하지 않았습니다. 어쩌면 여전히 쉽게 읽을 수 있도록 모든 색상을 코딩하는 것은 텍스트 편집기 일뿐입니다. function aaa (arg) {statements} 더 빨리 실행하지 않고 대신 축소하는 것으로 알고 있습니다.
YK

21

사용 가능한 기능 의 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
    }
});

1
중괄호가 if 문을 닫기에 충분합니까?
calasyr

1
동일한 클래스의 여러 요소로 작동시키지 못했습니다.
오즈의 위즈

1
@TheWhizofOz 나는 당신이 제기 한 다른 가능한 사용 사례의 예를 제공하기 위해 내 대답을 업데이트했습니다. 행운을 빌어 요.
r3wt

10

새로운 교차 관찰자 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>


3
좋은 구현 및에있는 링크에 따라 이 대답해야 추가하여 사파리에서 작동 <!DOCTYPE html>HTML로
아론 에이 탄을

참고 IntersectionObserver실험 기능 (향후 변경 될 수 있음)이다.
Karthik Chintala

2
@KarthikChintala-IE를 제외한 모든 브라우저에서 지원되며 사용 가능한 polyfill도 있습니다.
랜디 Casburn

단지 감지 이후 영업 이익의 문제를 해결하지 않습니다 변경 : IntersectionObserver유일한 루트 대상의 이동 후 콜백을 발생합니다.
Brandon Hill

호출 observe이벤트가 발생 하면 즉시 추적 된 요소의 현재 교차 상태를 알려줍니다. 그래서 어떤 식 으로든-해결합니다.
Mesqalito

8

내가 본 모든 대답은 요소가 현재 뷰포트 안에 있는지 확인합니다 . 그러나 그것은 그것을 볼 것을 의미하지 않는다 .
주어진 요소가 넘친 콘텐츠가있는 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에서이 솔루션을 테스트했습니다.


8

여기에 허용되는 답변이 대부분의 사용 사례에서 지나치게 복잡하다는 것을 알았습니다. 이 코드는 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)스크롤 핸들러 외부에 캐시해야 합니다.
sam

4

가장 간단한 솔루션 의 지원과 같은 Element.getBoundingClientRect은 ()완벽한 될 :

function isInView(el) {
    let box = el.getBoundingClientRect();
    return box.top < window.innerHeight && box.bottom >= 0;
}

모바일 브라우저에서 어떻게 작동합니까? 대부분은 뷰포트와 관련하여 버그가 있으며, 스크롤 할 때 헤더가 올라가거나 내려 가고, 키보드가 Android 또는 iOS인지 여부에 따라 키보드가 표시 될 때 동작이 다릅니다.
Kev

@Kev이 메소드를 호출 할 때에 따라 제대로 작동합니다. 호출 한 다음 창의 크기를 조정하면 결과가 더 이상 정확하지 않을 수 있습니다. 원하는 기능 유형에 따라 모든 크기 조정 이벤트에서 호출 할 수 있습니다. 특정 사용 사례에 대해 별도의 질문을하고 여기에 핑 (Ping)하십시오.
leonheess

3

나는 이것이 더 기능적인 방법이라고 생각합니다. 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)
    );
};

3

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);

2

여기 내 해결책이 있습니다. 스크롤 가능한 컨테이너 안에 요소가 숨겨져 있으면 작동합니다.

여기 데모가 있습니다 (창 크기를 조정하십시오)

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로드 더 많은 레코드 기능).


2

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)스크롤 핸들러 외부에 캐시해야 합니다
Adam Rehal

2

이전 답변에서 대부분의 사용법은 다음 시점에서 실패합니다.

-요소의 픽셀이 보이지만 " 코너 "가 아닌 경우 ,

- 언제 요소가 뷰포트보다 큰과를 중심으로 ,

-그들 중 대부분은 문서 또는 창 내부의 단일 요소 만 검사합니다 .

글쎄,이 모든 문제에 대해 해결책을 찾았으며 장점은 다음과 같습니다.

- 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/

이 코드는 요소의 일부가보기에 표시되는지 여부에 대한보다 정확한 정보를 제공하기 위해 작성되었습니다. 성능 옵션 또는 세로 슬라이드에만 사용하십시오! 이 코드는 그리기 사례에서 더 효과적입니다.


1

더 나은 해결책 :

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;
}

12
버전이 더 좋은 이유를 시도하고 설명해야합니다. 그대로, 다른 솔루션과 거의 비슷하게 보입니다.
Andy E

1
훌륭한 해결책은 BOX / ScrollContainer를 가지고 있으며 WINDOW를 사용하지 않습니다 (지정되지 않은 경우에만). 보다 보편적 인 솔루션 인 속도보다 코드를 살펴보십시오 (많이 검색
했음

1

최대한 간단하게 IMO :

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}

0

다음은 부모 요소 의 현재 뷰포트에 요소가 보이는지 알려주는 함수입니다 .

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
    );
}

0

이것은 요소가 적어도 부분적으로 보이는지 확인합니다 (수직 치수).

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}
}

0

나는 같은 질문을했고 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.');
   }
};

0

이것은 나를 위해 일한 쉽고 작은 솔루션입니다.

: 오버 플로우 스크롤이있는 상위 요소에 요소가 표시되는지 확인하려고합니다.

$(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');
     }
})

0

여기에있는 모든 대답은 요소가 뷰포트에 완전히 포함되어 있는지 여부를 결정합니다. 예를 들어,보기의 맨 아래에 이미지의 절반 만 보이는 경우 "외부"를 고려하면 여기의 해결 방법이 실패합니다.

를 통해 지연 로딩을 수행하는 유스 케이스가 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));

이것은 기본적으로 상단 또는 하단 경계가 독립적으로 뷰포트에 있는지 확인합니다. 반대쪽 끝은 외부에있을 수 있지만 한쪽 끝이있는 한 적어도 부분적으로 "보이는"것입니다.


-1

이 기능을 사용합니다 (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;
    }

}

-3

비슷한 도전을 위해 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표류해야합니다 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.