admin管理员组

文章数量:1203409

I'm wondering if anyone has an easy solution for this. I'm trying to detect if any part of a HTML element finds itself outside of the viewport. I've tried utilizing the following code:

$.fn.isOnScreen = function(){

    var win = $(window);

    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();

    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();

Brought to you by Steven

I can only get this to work when the entire element is not viewable anymore, but I just need to know if part of the element is outside of the viewport.

When the element is outside of the viewport, then I'm putting a different class on it, so that it will shift to the left instead so that it is viewable again.

Something like:

if(elementIsPartiallyOutsideViewport) {
    ele.addClass('move-left');
}

Any ideas?

I'm wondering if anyone has an easy solution for this. I'm trying to detect if any part of a HTML element finds itself outside of the viewport. I've tried utilizing the following code:

$.fn.isOnScreen = function(){

    var win = $(window);

    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();

    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();

Brought to you by Steven

I can only get this to work when the entire element is not viewable anymore, but I just need to know if part of the element is outside of the viewport.

When the element is outside of the viewport, then I'm putting a different class on it, so that it will shift to the left instead so that it is viewable again.

Something like:

if(elementIsPartiallyOutsideViewport) {
    ele.addClass('move-left');
}

Any ideas?

Share Improve this question edited Feb 10, 2014 at 22:24 Romes asked Feb 10, 2014 at 21:45 RomesRomes 3,1185 gold badges39 silver badges52 bronze badges 2
  • how do you want the function to work? find the element outside the viewport and return it or you give the element as param and want the return a boolean value? – Zafar Commented Feb 10, 2014 at 22:18
  • Something to the effect of: if(elementPartiallyOusideViewport) { ele.addClass('move-left') } if that makes sense to you... – Romes Commented Feb 10, 2014 at 22:22
Add a comment  | 

2 Answers 2

Reset to default 22

Most of the browsers already support getBoundingClientRect() method. So you can try the following code.

function isElementInViewport (el) {
    var rect = el.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&     
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
}

You simply pass the element to the function and get false if element is not inside the viewport.

Usage.

if (!isElementInViewport(el)) {
    el.addClass('move-left');
}

Edit

Just an addition. You can get more info about getBoundingClientRect() function and the browser support in here

There is a solution with an Intersection Observer. An advantage of the solution is position-relative calculations are not required.

jQuery plugin code snippet: (plugin.js)

(function ($) {
    class IntersectionDetector {
        async isElementPartiallyOutOfViewport(element) {
            return new Promise((resolve) => {
                const callback = this.handleOutOfViewportObservation.bind(this, resolve);
                const options = {
                    root: null,
                    threshold: 0,
                }

                const observer = new IntersectionObserver(callback, options);
                observer.observe(element);
            })
        }

        handleOutOfViewportObservation(resolve, [entry], observer) {
            const element = entry.target;

            observer.unobserve(element);
            observer.disconnect();

            const ratios = new Map();
            ratios.set('inViewportCompletelyRatio', 1);
            ratios.set('outOfViewportCompletelyRatio', 0);
            ratios.set('actualRatio', entry.intersectionRatio);

            const precision = Math.pow(10, 2);
            for (const [name, prevRatio] of ratios) {
                const nextRatio = precision * prevRatio;

                ratios.set(name, nextRatio);
            }

            const actualRatio = ratios.get('actualRatio');
            const inViewportCompletelyRatio = ratios.get('inViewportCompletelyRatio');
            const outOfViewportCompletelyRatio = ratios.get('outOfViewportCompletelyRatio');

            const isOutOfViewportPartially =
                (actualRatio > outOfViewportCompletelyRatio) &&
                (actualRatio < inViewportCompletelyRatio);

            resolve(isOutOfViewportPartially);
        }
    }

    $.fn.isOnScreen = async function () {
        const elements = this;
        const promises = elements.map(async (index, element) => {
            const detector = new IntersectionDetector();

            return await detector.isElementPartiallyOutOfViewport(element);
        });

        const results = await Promise.all(promises);

        return results.every(result => result);
    };
}(jQuery));

Usage example: (main.js)

jQuery(async function ($) {
    const isOnScreen = await $('#element-to-check').isOnScreen();

    console.log(isOnScreen);
});

Explanation:

  1. jQuery operates nodes collection, thus a better option is to check every node in a collection. If every node in the collection is partially visible the function returns true.
  2. The observer has specified a root argument equal to null, which means the element's intersection is being detected relative to a browser window.
  3. The solution uses precision. Potentially, JavaScript can cause a mistake while processing floating point numbers. The idea is to compare integer parts of numbers instead of floats to avoid the incorrect result. For instance, there is a good answer to this issue: https://stackoverflow.com/a/50780164/11173494

The code tested for:

  1. jQuery 3.6.*
  2. Chrome 103.0.* / Firefox 108.0

本文标签: javascriptHow to detect if DOM element is partially out of the viewportStack Overflow