كيف يمكنك معرفة ما إذا كان عنصر DOM مرئيًا في إطار العرض الحالي؟

StackOverflow https://stackoverflow.com/questions/123999

سؤال

هل هناك طريقة فعالة لمعرفة ما إذا كان عنصر DOM (في مستند HTML) مرئيًا حاليًا (يظهر في ملف إطار العرض)?

(السؤال يتعلق بفايرفوكس)

هل كانت مفيدة؟

المحلول

تحديث: الوقت يمر وكذلك متصفحاتنا. لم يعد يوصى بهذه التقنية ويجب عليك استخدام حل @Dan أدناه (https://stackoverflow.com/a/7557433/5628) إذا كنت لا تحتاج إلى دعم IE<7.

الحل الأصلي (عفا عليه الزمن الآن):

سيؤدي هذا إلى التحقق مما إذا كان العنصر مرئيًا بالكامل في إطار العرض الحالي:

function elementInViewport(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top >= window.pageYOffset &&
    left >= window.pageXOffset &&
    (top + height) <= (window.pageYOffset + window.innerHeight) &&
    (left + width) <= (window.pageXOffset + window.innerWidth)
  );
}

يمكنك تعديل هذا ببساطة لتحديد ما إذا كان أي جزء من العنصر مرئيًا في إطار العرض:

function elementInViewport2(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}

نصائح أخرى

الآن معظم المتصفحات يدعم getBoundingClientRect الطريقة التي أصبحت أفضل الممارسات.باستخدام الجواب القديم هو بطيء جدا, غير دقيقة و لديه العديد من الأخطاء.

الحل المختار صحيح هو لا يكاد يكون دقيقًا أبدًا.أنت تستطيع اقرأ أكثر عن أخطاءه.


تم اختبار هذا الحل على IE7+، وiOS5+ Safari، وAndroid2+، وBlackberry، وOpera Mobile، وIE Mobile. 10.


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); // IE9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

إذا قمت بإجراء أي تعديلات على DOM، فيمكنها تغيير رؤية العنصر الخاص بك بالطبع.

المبادئ التوجيهية والمزالق الشائعة:

ربما تحتاج إلى تتبع تكبير/تصغير الصفحة/قرصة الجهاز المحمول؟ يجب أن يتعامل jQuery تكبير / قرصة عبر المتصفح، وإلا أولاً أو ثانية الرابط يجب أن يساعدك.

اذا أنت تعديل DOM, يمكن أن يؤثر على رؤية العنصر.يجب عليك السيطرة على ذلك والاتصال handler() يدويا.لسوء الحظ، ليس لدينا متصفح متقاطع onrepaint حدث.ومن ناحية أخرى، يسمح لنا ذلك بإجراء تحسينات وإجراء إعادة فحص فقط على تعديلات DOM التي يمكنها تغيير رؤية العنصر.

ابدا استخدمه داخل jQuery $(مستند).جاهز() فقط لأن لا يوجد أي ضمان تم تطبيق CSS في هذه اللحظة.يمكن أن تعمل التعليمات البرمجية الخاصة بك محليًا مع CSS الخاص بك على القرص الصلب، ولكن بمجرد وضعها على خادم بعيد، ستفشل.

بعد DOMContentLoaded مطرود من العمل، يتم تطبيق الأنماط, ، لكن لم يتم تحميل الصور بعد.لذلك، ينبغي لنا أن نضيف window.onload مستمع الحدث

لا يمكننا التقاط حدث التكبير/التصغير حتى الآن.

يمكن أن يكون الملاذ الأخير هو الكود التالي:

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

يمكنك استخدام الميزة الرائعة pageVisibiliy HTML5 API إذا كنت تهتم بما إذا كانت علامة التبويب الخاصة بصفحة الويب الخاصة بك نشطة ومرئية.

لكى يفعل:هذه الطريقة لا تعالج حالتين:

تحديث

في المتصفحات الحديثة، قد ترغب في التحقق من واجهة برمجة تطبيقات مراقب التقاطع والذي يوفر الفوائد التالية:

  • أداء أفضل من الاستماع لأحداث التمرير
  • يعمل في إطارات iframe عبر المجال
  • يمكن معرفة ما إذا كان أحد العناصر يعيق/يتقاطع مع عنصر آخر

Intersection Observer في طريقه ليصبح معيارًا متكاملاً وهو مدعوم بالفعل في Chrome 51+ وEdge 15+ وFirefox 55+ وهو قيد التطوير لـ Safari.هناك أيضا polyfill متاح.


الإجابة السابقة

هناك بعض المشاكل مع الإجابة المقدمة من دان مما قد يجعله أسلوبًا غير مناسب في بعض المواقف.تمت الإشارة إلى بعض هذه المشكلات في إجابته بالقرب من الأسفل، حيث أن الكود الخاص به سيعطي نتائج إيجابية خاطئة للعناصر التالية:

  • مخفي بواسطة عنصر آخر أمام العنصر الذي يتم اختباره
  • خارج المنطقة المرئية لعنصر الأصل أو السلف
  • عنصر أو عناصره الفرعية مخفية باستخدام CSS clip ملكية

وتظهر هذه القيود في النتائج التالية ل اختبار بسيط:

Failed test, using isElementInViewport

الحل: isElementVisible()

إليك حل لهذه المشكلات، مع نتيجة الاختبار أدناه وشرح لبعض أجزاء الكود.

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
        vHeight  = window.innerHeight || doc.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/

والنتيجة:

Passed test, using isElementVisible

ملاحظات إضافية

ومع ذلك، فإن هذه الطريقة لا تخلو من القيود الخاصة بها.على سبيل المثال، سيتم تحديد العنصر الذي يتم اختباره بمؤشر z أقل من عنصر آخر في نفس الموقع على أنه مخفي حتى إذا كان العنصر الموجود في المقدمة لا يخفي فعليًا أي جزء منه.ومع ذلك، فإن هذه الطريقة لها استخداماتها في بعض الحالات التي لا يغطيها حل دان.

كلاهما element.getBoundingClientRect() و document.elementFromPoint() هي جزء من مواصفات مسودة عمل CSSOM وهي مدعومة على الأقل في IE 6 والإصدارات الأحدث و معظم متصفحات سطح المكتب لفترة طويلة (وإن لم يكن ذلك بشكل مثالي).يرى وضع Quirksmode على هذه الوظائف للمزيد من المعلومات.

contains() يتم استخدامه لمعرفة ما إذا كان العنصر الذي تم إرجاعه بواسطة document.elementFromPoint() هي عقدة فرعية للعنصر الذي نختبره من أجل الرؤية.كما أنه يُرجع صحيحًا إذا كان العنصر الذي تم إرجاعه هو نفس العنصر.هذا فقط يجعل الشيك أكثر قوة.إنه مدعوم في جميع المتصفحات الرئيسية، وكان Firefox 9.0 هو آخر المتصفحات التي أضافته.للحصول على دعم Firefox الأقدم، تحقق من سجل هذه الإجابة.

إذا كنت تريد اختبار المزيد من النقاط حول العنصر من أجل الرؤية - أي للتأكد من عدم تغطية العنصر بأكثر من 50٪، على سبيل المثال - فلن يستغرق الأمر الكثير لضبط الجزء الأخير من الإجابة.ومع ذلك، انتبه إلى أنه من المحتمل أن يكون الأمر بطيئًا جدًا إذا قمت بفحص كل بكسل للتأكد من أنه مرئي بنسبة 100%.

حاولت إجابة دان لكن الجبر المستخدم لتحديد الحدود يعني أن العنصر يجب أن يكون ≥ حجم إطار العرض وداخل إطار العرض بالكامل للحصول عليه true, مما يؤدي بسهولة إلى نتائج سلبية كاذبة.إذا كنت تريد تحديد ما إذا كان العنصر موجودًا في إطار العرض على الإطلاق، أم لا، إجابة ريانفي قريب ولكن العنصر الذي يتم اختباره يجب أن يتداخل مع إطار العرض، لذا جرب ما يلي:

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

كخدمة عامة:
إجابة دان بالحسابات الصحيحة (يمكن أن يكون العنصر > window، خاصة على شاشات الهاتف المحمول)، واختبار 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>

هناك مكون إضافي يسمى jQuery في رؤيه هذا يقوم بهذه المهمة

انظر المصدر حافة, ، الذي يستخدم 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 لو أي جزء من العنصر موجود في إطار العرض.

نسختي الأقصر والأسرع.

function isElementOutViewport(el){
    var rect = el.getBoundingClientRect();
    return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}

أضف jsFiddle كما هو مطلوبhttps://jsfiddle.net/on1g619L/1/

لقد وجدت أنه من المثير للقلق أنه لم يكن هناك jQuery نسخة مركزية من الوظائف المتاحة.عندما صادفت حل دان لقد تجسست على الفرصة لتقديم شيء ما للأشخاص الذين يحبون البرمجة في 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
//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
    }

});

أجد أن الإجابة المقبولة هنا معقدة للغاية بالنسبة لمعظم حالات الاستخدام.يقوم هذا الكود بالمهمة بشكل جيد (باستخدام 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");
    }
});

جميع الإجابات التي واجهتها هنا تتحقق فقط مما إذا كان العنصر المتمركزة داخل إطار العرض الحالي.لكن ذلك لا يعني أنه مرئي.
ماذا لو كان العنصر المحدد موجودًا داخل 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.

الجديد واجهة برمجة تطبيقات مراقب التقاطع يعالج هذا السؤال بشكل مباشر جدا.

سيحتاج هذا الحل إلى polyfill لأن Safari وOpera وIE لا يدعمون هذا حتى الآن.(يتم تضمين 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>

أعتقد أن هذه طريقة أكثر وظيفية للقيام بذلك.إجابة دان لا تعمل في سياق عودي.

تحل هذه الوظيفة المشكلة عندما يكون العنصر الخاص بك داخل أقسام أخرى قابلة للتمرير عن طريق اختبار أي مستويات أعلى بشكل متكرر من علامة HTML، وتتوقف عند الخطأ الأول.

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

بناءً على حل @dan أعلاه (https://stackoverflow.com/a/7557433/5628)، لقد قمت بمحاولة تنظيف التنفيذ بحيث يكون استخدامه عدة مرات على نفس الصفحة أسهل:

$(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 أشياء لتحريكها بشكل مشروط على الصفحة.

نأمل أن يساعد!

هذا هو الحل الخاص بي، وسيعمل إذا تم إخفاء عنصر داخل حاوية قابلة للتمرير.

هنا عرض توضيحي (حاول تغيير حجم النافذة إلى)

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 its within the document viewport
    return top <= document.documentElement.clientHeight;
};

كنت بحاجة فقط للتحقق مما إذا كان مرئيًا في المحور Y (للحصول على ميزة تحميل المزيد من السجلات من خلال ajax).

الحل السهل والصغير الذي نجح بالنسبة لي.

مثال تريد معرفة ما إذا كان العنصر مرئيًا في العنصر الأصلي الذي يحتوي على تجاوز السعة.

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

حل أفضل:

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width) return false;
    if(box.top > viewport.h || box.bottom < 0) return false;
    if(box.right < 0 || box.left > viewport.w) return false;
    return true;    
}

يتحقق مما إذا كان العنصر معروضًا جزئيًا على الأقل (البعد الرأسي):

function inView(element) {
                var box = element.getBoundingClientRect();
                return inViewBox(box);
}

function inViewBox(box) {
                return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}


function getWindowSize() { 
        return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight} 
}

كان لدي نفس السؤال واكتشفته باستخدام getBoundingClientRect().هذا الرمز "عام" تمامًا ويجب كتابته مرة واحدة فقط حتى يعمل (ليس عليك كتابته لكل عنصر تريد معرفته في إطار العرض).يتحقق هذا الرمز فقط لمعرفة ما إذا كان عموديًا في إطار العرض ليس أفقيا.في هذه الحالة، يحتفظ المتغير (المصفوفة) "العناصر" بجميع العناصر التي تتحقق من وجودها رأسيًا في إطار العرض، لذا احصل على أي عناصر تريدها في أي مكان وقم بتخزينها هناك.تدور "حلقة for" عبر كل عنصر وتتحقق لمعرفة ما إذا كان موجودًا رأسيًا في إطار العرض.يتم تنفيذ هذا الرمز كل مرة يقوم المستخدم بالتمرير!إذا كان getBoudingClientRect().top أقل من 3/4 إطار العرض (العنصر هو ربع في إطار العرض)، فسيتم تسجيله كـ "في إطار العرض".نظرًا لأن الكود عام، فسوف ترغب في معرفة العنصر الموجود في إطار العرض.لمعرفة ذلك، يمكنك تحديده حسب السمة المخصصة واسم العقدة والمعرف واسم الفئة والمزيد.هذا هو الكود الخاص بي (أخبرني إذا لم يعمل، فقد تم اختباره في IE 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.');
   }
};

أمل أن هذا يساعد شخصاما :-)

إليك وظيفة تخبرك ما إذا كان العنصر مرئيًا في إطار العرض الحالي لـ a الأبوين عنصر:

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

بأبسط ما يمكن الحصول على IMO:

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

أستخدم هذه الوظيفة (تتحقق فقط مما إذا كان الحرف y موجودًا على الشاشة لأنه في معظم الأوقات لا تكون هناك حاجة إلى 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;
    }

}

لتحدي مماثل لقد استمتعت حقا هذا الجوهر الذي يعرض polyfill ل التمرير إلىViewIfNeeded ().

كل ما يلزم من الكونغ فو للإجابة موجود ضمن هذه المجموعة:

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 - فقط يجب أن تحصل على الانجراف ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top