Skip to content

Instantly share code, notes, and snippets.

@c-kick
Last active November 27, 2023 11:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save c-kick/59eb52a762b014539c00678d4bac6b09 to your computer and use it in GitHub Desktop.
Save c-kick/59eb52a762b014539c00678d4bac6b09 to your computer and use it in GitHub Desktop.
Check if an element is visible
/** isVisible
*
* Determines whether an element is visible within the (specified, or Window) viewport. Executes the callback based
* on that result. The callback is called with three parameters:
* - visible: a boolean that's true if ANY pixels of the element are visible
* - fullyVisible: a boolean that's true if the ENTIRE element fits the viewport, and thus is wholly visible
* - the element's bounding box, including 'pageY' and 'pageX' which contain the element's position as relative to
* the whole document. Useful for scrolling into view,
*
* The viewport can either be omitted, specified partially or completely.
*
* Usage: isVisible(myElement, callback, {viewport object});
*
* @param {Element} element - The element to check for visibility.
* @param {Function} callback - The callback function to execute when done checking.
* @param {Object} [vp] - Object (optional) containing top, bottom, left and right offsets of the viewport to check against - falls back to the Window if not provided.
*/
export function isVisible(element, callback, vp = {}) {
if (!(element instanceof Element)) {
throw new TypeError('Not a valid node');
}
if (typeof element.getBoundingClientRect === 'function') {
const rect = element.getBoundingClientRect();
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
const clientTop = document.documentElement.clientTop;
const clientLeft = document.documentElement.clientLeft;
rect.pageY = rect.top + scrollTop - clientTop;
rect.pageX = rect.left + scrollLeft - clientLeft;
const viewport = {
top: typeof vp.top !== 'undefined' ? vp.top : 0,
bottom: typeof vp.bottom !== 'undefined' ? vp.bottom : window.innerHeight || document.documentElement.clientHeight,
left: typeof vp.left !== 'undefined' ? vp.left : 0,
right: typeof vp.right !== 'undefined' ? vp.right : window.innerWidth || document.documentElement.clientWidth,
};
const fullyVisible = (
(rect.height > 0 || rect.width > 0) &&
rect.bottom < viewport.bottom &&
rect.right < viewport.right &&
rect.top > viewport.top &&
rect.left > viewport.left
);
const visible = (
(rect.height > 0 || rect.width > 0) &&
rect.bottom >= 0 &&
rect.top <= viewport.bottom &&
((rect.right > viewport.left && rect.right <= viewport.right) || (rect.left < viewport.right && rect.left >= viewport.left))
)
if (typeof callback === 'function') {
callback.call(this, visible, fullyVisible, rect);
}
} else {
console.error('Can\'t check visibility for', typeof this, this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment