현재 뷰포트에서 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.