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:
- 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.
- The observer has specified a root argument equal to null,
which means the element's intersection is being detected relative to a browser window.
- 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:
- jQuery 3.6.*
- Chrome 103.0.* / Firefox 108.0