ฉันจะรู้ได้อย่างไรว่าองค์ประกอบ DOM สามารถมองเห็นได้ในวิวพอร์ตปัจจุบันหรือไม่?


949

มีวิธีที่มีประสิทธิภาพในการบอกหรือไม่ว่าองค์ประกอบ DOM (ในเอกสาร HTML) สามารถมองเห็นได้ในปัจจุบัน (ปรากฏในวิวพอร์ต ) หรือไม่?

(คำถามหมายถึง Firefox)


ขึ้นอยู่กับสิ่งที่คุณหมายถึงโดยการมองเห็น หากคุณหมายถึงมันแสดงอยู่บนหน้านี้ในขณะที่กำหนดตำแหน่งการเลื่อนคุณสามารถคำนวณตามองค์ประกอบ y และออฟเซ็ตตำแหน่งการเลื่อนปัจจุบัน
roryf

18
การชี้แจง: มองเห็นได้ภายในกรอบสี่เหลี่ยมที่แสดงอยู่ในปัจจุบัน มองเห็นได้เช่นเดียวกับที่ไม่ถูกซ่อนหรือแสดง: ไม่มี มองเห็นได้เช่นเดียวกับที่ไม่ได้อยู่ในสิ่งอื่นตามลำดับ Z ใช่ไหม
Adam Wright

6
เช่นเดียวกับภายในสี่เหลี่ยมที่แสดงอยู่ในปัจจุบัน ขอบคุณ ซักค้าน
benzaita

2
โปรดทราบว่าคำตอบทั้งหมดที่นี่จะให้ผลบวกเป็นเท็จสำหรับรายการที่อยู่นอกพื้นที่ที่มองเห็นได้ขององค์ประกอบหลักของพวกเขา (eh ที่มีการซ่อนไว้ล้น) แต่ภายในพื้นที่วิวพอร์ต
Andy E

1
มีหนึ่งล้านคำตอบและส่วนใหญ่ยาวขัน ดูที่นี่สำหรับสองซับ
leonheess

คำตอบ:


346

อัปเดต:เวลาเดินผ่านไปเรื่อย ๆ มีเบราว์เซอร์ของเรา เทคนิคนี้ไม่แนะนำให้ใช้อีกต่อไปและคุณควรใช้โซลูชันของ Danหากคุณไม่จำเป็นต้องรองรับ Internet Explorer รุ่นก่อน 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
ฟังก์ชั่นดั้งเดิมที่โพสต์มีข้อผิดพลาด จำเป็นต้องบันทึกความกว้าง / ความสูงก่อนที่จะกำหนดใหม่ el ...
Prestaul

15
จะเป็นอย่างไรถ้าองค์ประกอบนั้นอยู่ใน div ที่เลื่อนได้และเลื่อนออกจากมุมมอง?
amartynov

2
โปรดตรวจสอบสคริปต์เวอร์ชันใหม่กว่าด้านล่าง
Dan

1
ยังอยากรู้เกี่ยวกับคำถามของ @ amartynov ใครรู้วิธีที่จะบอกว่าองค์ประกอบถูกซ่อนไว้เนื่องจากล้นขององค์ประกอบบรรพบุรุษ? โบนัสสามารถตรวจพบได้ไม่ว่าเด็กจะอยู่ในระดับใด
Eric Nguyen

1
@deadManN การเรียกซ้ำผ่าน DOM นั้นช้ามาก นั่นเป็นเหตุผลเพียงพอ แต่ผู้ค้าเบราว์เซอร์ได้สร้างขึ้น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 ที่สามารถเปลี่ยนการมองเห็นขององค์ประกอบ

ไม่เคยใช้มันใน jQuery $ (document) .ready ()เท่านั้นเนื่องจากไม่มีการรับประกัน CSS ถูกนำไปใช้ในขณะนี้ รหัสของคุณสามารถทำงานกับ CSS บนโลคัลฮาร์ดไดรฟ์ได้ แต่เมื่อวางบนเซิร์ฟเวอร์ระยะไกลมันจะล้มเหลว

หลังจากDOMContentLoadedยิงรูปแบบที่ถูกนำมาใช้แต่ภาพจะยังไม่ได้โหลด ดังนั้นเราควรเพิ่มwindow.onloadฟังเหตุการณ์

เรายังไม่สามารถจับเหตุการณ์ซูม / บีบได้

วิธีสุดท้ายอาจเป็นรหัสต่อไปนี้:

/* TODO: this looks like a very bad code */
setInterval(handler, 600);

คุณสามารถใช้หน้าคุณสมบัติที่ยอดเยี่ยมเยี่ยมชม HTML5 API หากคุณสนใจว่าแท็บที่มีหน้าเว็บของคุณทำงานอยู่และสามารถมองเห็นได้

สิ่งที่ต้องทำ: วิธีนี้ไม่ได้จัดการกับสองสถานการณ์:


6
ฉันใช้วิธีนี้ (ระวังคำพิพากษา "botom") นอกจากนี้ยังมีสิ่งที่ต้องระวังเมื่อองค์ประกอบที่เรากำลังพิจารณาจะมีรูปภาพอยู่ 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 เพียงแค่การเตือนความจำที่เป็นมิตรที่จะผ่านในวัตถุ HTML DOM (เช่นisElementInViewport(document.getElementById('elem'))) และไม่ใช่วัตถุ jQuery (เช่นisElementInViewport($("#elem))) jQuery ที่เทียบเท่าคือการเพิ่ม[0]ดังนี้: isElementInViewport($("#elem)[0]).
jiminy

11
el is not defined
M_Willett

176

ปรับปรุง

ในเบราว์เซอร์สมัยใหม่คุณอาจต้องการตรวจสอบIntersection Observer APIซึ่งให้ประโยชน์ดังต่อไปนี้:

  • ประสิทธิภาพที่ดีกว่าฟังเหตุการณ์การเลื่อน
  • ทำงานใน iframes ข้ามโดเมน
  • สามารถบอกได้ว่าองค์ประกอบนั้นขัดขวาง / ตัดกันอีกองค์ประกอบหรือไม่

Intersection Observer กำลังจะเป็นมาตรฐานที่สมบูรณ์และได้รับการสนับสนุนใน Chrome 51+, Edge 15+ และ Firefox 55+ และอยู่ระหว่างการพัฒนาสำหรับ Safari นอกจากนี้ยังมีpolyfillใช้ได้


คำตอบก่อนหน้า

มีปัญหาบางอย่างเกี่ยวกับคำตอบที่ Dan เตรียมไว้ให้ซึ่งอาจเป็นวิธีที่ไม่เหมาะสมในบางสถานการณ์ บางประเด็นเหล่านี้ชี้ให้เห็นในคำตอบของเขาใกล้ด้านล่างว่ารหัสของเขาจะให้ผลบวกเท็จสำหรับองค์ประกอบที่:

  • ถูกซ่อนโดยองค์ประกอบอื่นที่อยู่ด้านหน้าของสิ่งที่ถูกทดสอบ
  • นอกพื้นที่ที่มองเห็นได้ขององค์ประกอบหลักหรือองค์ประกอบบรรพบุรุษ
  • องค์ประกอบหรือลูก ๆ ของมันถูกซ่อนไว้โดยใช้clipคุณสมบัติCSS

ข้อ จำกัด เหล่านี้แสดงให้เห็นในผลลัพธ์ต่อไปนี้ของการทดสอบอย่างง่าย :

การทดสอบล้มเหลวโดยใช้ 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

หมายเหตุเพิ่มเติม

อย่างไรก็ตามวิธีนี้ไม่ได้ไม่มีข้อ จำกัด ตัวอย่างเช่นองค์ประกอบที่ถูกทดสอบด้วยดัชนีซีต่ำกว่าองค์ประกอบอื่นที่ตำแหน่งเดียวกันจะถูกระบุว่าซ่อนแม้ว่าองค์ประกอบข้างหน้าจะไม่ได้ซ่อนส่วนใดส่วนหนึ่งของมัน ถึงกระนั้นวิธีนี้มีประโยชน์ในบางกรณีที่โซลูชันของ Dan ไม่ครอบคลุม

ทั้งสองelement.getBoundingClientRect()และdocument.elementFromPoint()เป็นส่วนหนึ่งของข้อมูลจำเพาะ CSSOM Working Draft และได้รับการสนับสนุนอย่างน้อย IE 6 และใหม่กว่าและเบราว์เซอร์เดสก์ท็อปส่วนใหญ่เป็นเวลานาน (แม้ว่าจะไม่สมบูรณ์) ดูQuirksmode บนฟังก์ชันเหล่านี้สำหรับข้อมูลเพิ่มเติม

contains()ใช้เพื่อดูว่าองค์ประกอบที่ส่งคืนโดยdocument.elementFromPoint()เป็นโหนดย่อยขององค์ประกอบที่เรากำลังทดสอบการมองเห็นหรือไม่ มันจะส่งกลับค่าจริงถ้าองค์ประกอบที่ส่งคืนเป็นองค์ประกอบเดียวกัน นี่ทำให้การตรวจสอบมีประสิทธิภาพยิ่งขึ้น ได้รับการสนับสนุนในเบราว์เซอร์ที่สำคัญทั้งหมด Firefox 9.0 เป็นคนสุดท้ายในการเพิ่ม สำหรับการสนับสนุน Firefox รุ่นเก่าให้ตรวจสอบประวัติคำตอบนี้

หากคุณต้องการทดสอบคะแนนเพิ่มเติมรอบองค์ประกอบเพื่อให้มองเห็นได้เช่นเพื่อให้แน่ใจว่าองค์ประกอบนั้นไม่ได้ครอบคลุมมากกว่านั้นให้บอกว่า 50% จะไม่ใช้เวลามากในการปรับส่วนสุดท้ายของคำตอบ อย่างไรก็ตามโปรดทราบว่าอาจช้ามากหากคุณตรวจสอบทุกพิกเซลเพื่อให้แน่ใจว่าสามารถมองเห็นได้ 100%


2
คุณหมายถึงใช้ doc.documentElement.clientWidth หรือไม่ ควรเป็น 'document.documentElement' แทนหรือไม่ ในบันทึกอื่น ๆ นี่เป็นวิธีการเดียวที่ใช้สำหรับกรณีการใช้งานเช่นการซ่อนเนื้อหาขององค์ประกอบสำหรับการเข้าถึงโดยใช้คุณสมบัติ 'คลิป' CSS: snook.ca/archives/html_and_css/hiding-content-for-accessibility
Kevin Lamping

@ klamping: จับได้ดีขอบคุณ ฉันต้องการคัดลอกขวาออกจากรหัสของฉันที่ฉันถูกใช้เป็นชื่อแทนสำหรับ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
ไม่ได้ทำงานกับอินพุตสำหรับฉัน (chrome canary 50) ไม่แน่ใจว่าทำไมบางทีอาจเป็นมุมกลมพื้นเมือง? ฉันต้องลด coords เล็กน้อยเพื่อให้มันทำงานได้ el.contain (efp (rect.left + 1, rect.top + 1)) || el.contain (efp (rect.right-1, rect.top + 1)) || el.contain (efp (rect.right-1, rect.bottom-1)) || el.contain (efp (rect.left + 1, rect.bottom-1))
Rayjax

65

ฉันพยายามคำตอบของแดน อย่างไรก็ตามพีชคณิตที่ใช้ในการกำหนดขอบเขตหมายความว่าองค์ประกอบจะต้องมีทั้งขนาดวิวพอร์ตและขนาดภายในวิวพอร์ตที่จะได้รับ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
@Aau chauhan: ไม่มีรหัสของฉันโหลดภาพดังนั้นทำไมมันควรและสูตรถูกต้อง
Stefan Steiger

1
@targumon: เหตุผลคือการสนับสนุนเบราว์เซอร์เก่า
Stefan Steiger

1
@StefanSteiger ตาม MDN มันรองรับตั้งแต่ IE9 ดังนั้นมันจึงปลอดภัย (อย่างน้อยในกรณีของฉัน) ที่จะใช้ window.innerHeight โดยตรง ขอบคุณ!
Targumon

28

ดูแหล่งที่มาของหมิ่นซึ่งใช้getBoundingClientRect มันเหมือนกับ:

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
โซลูชันของฉันโลภมากขึ้นและเร็วขึ้นเมื่อองค์ประกอบมีพิกเซลใด ๆ ในวิวพอร์ตมันจะส่งคืนค่าเท็จ
Eric Chen

1
ฉันชอบมัน. กระชับ. คุณสามารถลบช่องว่างระหว่างชื่อฟังก์ชั่นและวงเล็บและระหว่างวงเล็บและรั้งในบรรทัดแรก ไม่ชอบช่องว่างเหล่านั้น อาจเป็นเพียงเครื่องมือแก้ไขข้อความของฉันที่ใช้รหัสสีเพื่อให้อ่านง่าย ฟังก์ชั่น aaa (หาเรื่อง) {statement} ฉันรู้ว่าไม่ได้ทำให้มันทำงานได้เร็วขึ้น
YK

21

ฉันพบว่ามันหนักใจว่าไม่มีฟังก์ชั่นการใช้งานjQuery- centric เมื่อฉันเจอโซลูชันของ Danฉันได้เห็นโอกาสที่จะมอบบางสิ่งให้กับคนที่ชอบเขียนโปรแกรมในสไตล์ jQuery OO เป็นคนดีและคล่องแคล่วและทำงานได้อย่างมีเสน่ห์สำหรับฉัน

Bada bing Bada บูม

$.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
ฉันไม่สามารถทำให้มันทำงานกับองค์ประกอบหลายอย่างของคลาสที่เหมือนกัน
เสียงหวือของ Oz

1
@TheWhizofOz ฉันได้อัปเดตคำตอบของฉันเพื่อให้ตัวอย่างของกรณีการใช้ที่เป็นไปได้อื่น ๆ ที่คุณนำมาใช้ ขอให้โชคดี
r3wt

10

API ผู้สังเกตการณ์แยกใหม่ตอบคำถามนี้โดยตรงมาก

โซลูชันนี้จะต้องใช้ polyfill เนื่องจาก Safari, Opera และ Internet Explorer ยังไม่รองรับ (polyfill รวมอยู่ในโซลูชัน)

ในการแก้ปัญหานี้มีมุมมองที่เป็นเป้าหมาย (สังเกตได้) เมื่อเข้าสู่มุมมองปุ่มที่ด้านบนของส่วนหัวจะถูกซ่อน มันจะปรากฏขึ้นเมื่อกล่องออกจากมุมมอง

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
Alon ตัน

โปรดทราบว่าIntersectionObserverเป็นคุณลักษณะทดลอง (ซึ่งอาจเปลี่ยนแปลงได้ในอนาคต)
Karthik Chintala

2
@KarthikChintala - รองรับทุกเบราว์เซอร์ยกเว้น IE - และยังมี polyfill
แรนดี้แคสเบิร์น

ไม่ได้อยู่ที่คำถาม OP ตั้งแต่เพียง แต่ตรวจพบการเปลี่ยนแปลง : 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 น่าจะทำงานได้ดีขึ้นอยู่กับว่าคุณใช้วิธีนี้เมื่อไร หากคุณโทรแล้วปรับขนาดหน้าต่างผลลัพธ์อาจไม่ถูกต้องอีกต่อไป คุณสามารถเรียกมันได้ในทุก ๆ การปรับขนาดเหตุการณ์ขึ้นอยู่กับประเภทของฟังก์ชั่นที่คุณต้องการ อย่าลังเลที่จะถามคำถามแยกต่างหากเกี่ยวกับกรณีการใช้งานเฉพาะของคุณและส่งมาที่นี่
เกียจคร้าน

3

ฉันคิดว่านี่เป็นวิธีที่ใช้งานได้มากกว่าที่จะทำ คำตอบของแดนไม่ได้ทำงานในบริบทแบบวนซ้ำ

ฟังก์ชั่นนี้แก้ปัญหาเมื่อองค์ประกอบของคุณอยู่ใน divs ที่เลื่อนได้อื่น ๆ โดยการทดสอบระดับใด ๆ ซ้ำจนถึงแท็ก HTML และหยุดที่ false แรก

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

คำตอบที่ได้รับการยอมรับมากที่สุดจะไม่ทำงานเมื่อซูมเข้า Google Chrome บน Android เมื่อใช้ร่วมกับคำตอบของ Danสำหรับบัญชี Chrome บน Android ต้องใช้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 load-more-records)


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

ประเพณีส่วนใหญ่ในคำตอบก่อนหน้านี้ล้มเหลวในจุดเหล่านี้:

- เมื่อมองเห็นพิกเซลขององค์ประกอบ แต่ไม่เห็น " มุม "

เมื่อมีองค์ประกอบเป็นใหญ่กว่าวิวพอร์ตและศูนย์กลาง ,

-Most ของพวกเขาจะตรวจสอบเฉพาะสำหรับองค์ประกอบเอกพจน์ภายในเอกสารหรือหน้าต่าง

สำหรับปัญหาเหล่านี้ฉันมีทางออกและด้านบวกคือ:

- คุณสามารถกลับมาได้visibleเมื่อมีพิกเซลจากด้านใดด้านหนึ่งปรากฏขึ้นและไม่ใช่มุม

- คุณยังสามารถกลับมาได้visibleในขณะที่องค์ประกอบที่ใหญ่กว่าวิวพอร์ต

- คุณสามารถเลือกparent elementหรือให้มันเลือกโดยอัตโนมัติ

- ทำงานกับองค์ประกอบที่เพิ่มขึ้นแบบไดนามิกเช่นกัน

หากคุณตรวจสอบตัวอย่างด้านล่างนี้คุณจะเห็นความแตกต่างในการใช้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ประโยชน์สำหรับองค์ประกอบที่ใหญ่กว่าวิวพิกเซลตรวจสอบองค์ประกอบภายใน 3 เพื่อดู:

คุณจะเห็นเมื่อคุณอยู่ภายใน 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 (เฉพาะในกรณีที่ไม่ได้ระบุ) ลองดูที่รหัสกว่าอัตรามันเป็นทางออกที่สากลมากขึ้น (ถูกค้นหามันมาก) ที่
Teter

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

รหัสนี้เป็น 'สามัญ' อย่างสมบูรณ์และจะต้องเขียนเพียงครั้งเดียวเพื่อให้มันทำงานได้ (คุณไม่จำเป็นต้องเขียนมันออกมาสำหรับแต่ละองค์ประกอบที่คุณต้องการรู้ว่าอยู่ในวิวพอร์ต)

รหัสนี้การตรวจสอบเท่านั้นที่จะเห็นว่ามันเป็นแนวตั้งในวิวพอร์ตไม่ได้ในแนวนอน ในกรณีนี้ 'องค์ประกอบ' ตัวแปร (อาร์เรย์) เก็บองค์ประกอบทั้งหมดที่คุณกำลังตรวจสอบให้อยู่ในแนวตั้งในวิวพอร์ตดังนั้นคว้าองค์ประกอบใด ๆ ที่คุณต้องการได้ทุกที่และเก็บไว้ที่นั่น

เครื่องหมาย 'for loop' วนรอบแต่ละองค์ประกอบและตรวจสอบเพื่อดูว่าเป็นแนวตั้งในวิวพอร์ตหรือไม่ รหัสนี้รันทุกครั้งที่ผู้ใช้เลื่อน! ถ้า getBoudingClientRect (). น้อยกว่า 3/4 วิวพอร์ต (องค์ประกอบคือหนึ่งในสี่ของวิวพอร์ต) มันจะลงทะเบียนเป็น 'ในวิวพอร์ต'

เนื่องจากรหัสเป็นแบบทั่วไปคุณจะต้องรู้ว่าองค์ประกอบใดอยู่ในวิวพอร์ต ในการค้นหานั้นคุณสามารถกำหนดได้โดยแอตทริบิวต์ที่กำหนดเองชื่อโหนด id ชื่อคลาสและอื่น ๆ

นี่คือรหัสของฉัน (บอกฉันว่ามันใช้งานไม่ได้มันถูกทดสอบใน Internet Explorer 11, Firefox 40.0.3, Chrome เวอร์ชัน 45.0.2454.85 m, Opera 31.0.1889.174 และ Edge กับ Windows 10, ไม่ใช่ Safari ]) ...

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

ฉันใช้ฟังก์ชั่นนี้ (มันจะตรวจสอบเฉพาะว่า y เป็นแบบ inscreen เพราะส่วนใหญ่เวลาที่ x ไม่จำเป็นต้องใช้)

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

สำหรับความท้าทายที่คล้ายกันฉันสนุกกับแก่นสารนี้ซึ่งเผยให้เห็น polyfill สำหรับscrollIntoViewIfNeeded ()()

กังฟูที่จำเป็นทั้งหมดที่จำเป็นต้องมีเพื่อตอบอยู่ในบล็อกนี้:

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.