Last active
February 23, 2021 22:15
-
-
Save jasonfarrell/3659166 to your computer and use it in GitHub Desktop.
Checks if a DOM element is visible. Takes into consideration its parents and overflow.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var my_element = document.getElementById('my-element'); | |
//-- Returns true/false | |
my_element.isVisible(my_element); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Author: Jason Farrell | |
* Author URI: http://useallfive.com/ | |
* | |
* Description: Checks if a DOM element is truly visible. | |
* Package URL: https://github.com/UseAllFive/true-visibility | |
*/ | |
Element.prototype.isVisible = function() { | |
'use strict'; | |
/** | |
* Checks if a DOM element is visible. Takes into | |
* consideration its parents and overflow. | |
* | |
* @param (el) the DOM element to check if is visible | |
* | |
* These params are optional that are sent in recursively, | |
* you typically won't use these: | |
* | |
* @param (t) Top corner position number | |
* @param (r) Right corner position number | |
* @param (b) Bottom corner position number | |
* @param (l) Left corner position number | |
* @param (w) Element width number | |
* @param (h) Element height number | |
*/ | |
function _isVisible(el, t, r, b, l, w, h) { | |
var p = el.parentNode, | |
VISIBLE_PADDING = 2; | |
if ( !_elementInDocument(el) ) { | |
return false; | |
} | |
//-- Return true for document node | |
if ( 9 === p.nodeType ) { | |
return true; | |
} | |
//-- Return false if our element is invisible | |
if ( | |
'0' === _getStyle(el, 'opacity') || | |
'none' === _getStyle(el, 'display') || | |
'hidden' === _getStyle(el, 'visibility') | |
) { | |
return false; | |
} | |
if ( | |
'undefined' === typeof(t) || | |
'undefined' === typeof(r) || | |
'undefined' === typeof(b) || | |
'undefined' === typeof(l) || | |
'undefined' === typeof(w) || | |
'undefined' === typeof(h) | |
) { | |
t = el.offsetTop; | |
l = el.offsetLeft; | |
b = t + el.offsetHeight; | |
r = l + el.offsetWidth; | |
w = el.offsetWidth; | |
h = el.offsetHeight; | |
} | |
//-- If we have a parent, let's continue: | |
if ( p ) { | |
//-- Check if the parent can hide its children. | |
if ( ('hidden' === _getStyle(p, 'overflow') || 'scroll' === _getStyle(p, 'overflow')) ) { | |
//-- Only check if the offset is different for the parent | |
if ( | |
//-- If the target element is to the right of the parent elm | |
l + VISIBLE_PADDING > p.offsetWidth + p.scrollLeft || | |
//-- If the target element is to the left of the parent elm | |
l + w - VISIBLE_PADDING < p.scrollLeft || | |
//-- If the target element is under the parent elm | |
t + VISIBLE_PADDING > p.offsetHeight + p.scrollTop || | |
//-- If the target element is above the parent elm | |
t + h - VISIBLE_PADDING < p.scrollTop | |
) { | |
//-- Our target element is out of bounds: | |
return false; | |
} | |
} | |
//-- Add the offset parent's left/top coords to our element's offset: | |
if ( el.offsetParent === p ) { | |
l += p.offsetLeft; | |
t += p.offsetTop; | |
} | |
//-- Let's recursively check upwards: | |
return _isVisible(p, t, r, b, l, w, h); | |
} | |
return true; | |
} | |
//-- Cross browser method to get style properties: | |
function _getStyle(el, property) { | |
if ( window.getComputedStyle ) { | |
return document.defaultView.getComputedStyle(el,null)[property]; | |
} | |
if ( el.currentStyle ) { | |
return el.currentStyle[property]; | |
} | |
} | |
function _elementInDocument(element) { | |
while (element = element.parentNode) { | |
if (element == document) { | |
return true; | |
} | |
} | |
return false; | |
} | |
return _isVisible(this); | |
}; |
I'm having an issue when the el.offsetParent
is different from p
on line 72, where it returns true because l
is a large number. It breaks for me in that case.
As a temporary fix I added el.offsetParent === p &&
to line 68 to skip that check if the parents are different. Kinda just a workaround but it seems to work for now.
Is it possible to add to this a check if the parent is off screen? like position: absolute; left: -1000;
or off to the right, top, bottom?
Maybe, it's better to change :
'0' === _getStyle(el, 'opacity') ||
to
'1' !== _getStyle(el, 'opacity') ||
because of everyOne can change opacity
ex: opacity: 0.1
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
the first parameter is useless isn't it?