|
/** |
|
* Is the element hidden from view? Here are the various ways this cold be. |
|
* * Via css with opacity, display, or visibility. |
|
* * Removed from the DOM, or not yet appended to the DOM |
|
* * Within the overflow of a ancestor. |
|
* * One of the methods above but applied to an ancestor element. |
|
* |
|
* @param {HTMLNode} el - element to examine |
|
* @return {Boolean} Could the element be visible |
|
*/ |
|
function isHidden(el) { |
|
var parent = el.parentNode; |
|
|
|
// if the element doesn't exist in the DOM |
|
if (!isAttached(el) || !isAttached(el.parentNode)) { |
|
return true; |
|
} |
|
|
|
// body is always visible so stop here |
|
if (isBody(el)) { |
|
return false; |
|
} |
|
|
|
// If any are true then we are hidden we can stop here |
|
if (checkHiddenStyle(el)) { |
|
return true; |
|
} |
|
|
|
// We are visible so check if our parent is body and if so stop here |
|
if (isBody(parent)) { |
|
return false; |
|
} |
|
|
|
// Is our parent hidden if so stop here |
|
if (checkHiddenStyle(parent)) { |
|
return true; |
|
} |
|
|
|
// check overflow then check offset if we are hidden in the offset stop here |
|
if (checkOverflow(parent) && isOffsetHidden(el)) { |
|
return true; |
|
} |
|
|
|
// Second verse same as the first |
|
return isHidden(parent, el.getBoundingClientRect()); |
|
} |
|
|
|
/** |
|
* Get the value of a calculated style |
|
* |
|
* @param {HTMLNode} el - The element to examine |
|
* @param {string} property - The style property to check |
|
* @return {string} - the matching property |
|
*/ |
|
function getStyle(el, property) { |
|
return document.defaultView.getComputedStyle(el, null)[property]; |
|
} |
|
|
|
/** |
|
* Test if the element has opacity display or visibility set |
|
* |
|
* @param {HTMLNode} el - The element to examine |
|
* @return {boolean} - Is the property set |
|
*/ |
|
function checkHiddenStyle(el) { |
|
return !!['opacity', 'display', 'visibility'].filter(function check(prop) { |
|
return /(0|none|hidden)/.test(getStyle(el, prop)); |
|
}).length; |
|
} |
|
|
|
/** |
|
* Test if the element has opacity overflow set |
|
* |
|
* @param {HTMLNode} el - The element to examine |
|
* @return {boolean} - Is the property set |
|
*/ |
|
function checkOverflow(el) { |
|
return /(hidden|scroll)/.test(window.getComputedStyle(el, null).getPropertyValue('overflow')); |
|
} |
|
|
|
/** |
|
* Is this the body element |
|
* |
|
* @param {HTMLNode} el - An element to examine |
|
* @return {Boolean} Is this the body element |
|
*/ |
|
function isBody(el) { |
|
return el.nodeName === 'BODY'; |
|
} |
|
|
|
/** |
|
*Is the element still attached to the DOM |
|
* |
|
* @param {HTMLNode} el - An element to examine |
|
* @return {Boolean} Is the element still attached to the DOM. |
|
*/ |
|
function isAttached(el) { |
|
return document.documentElement.contains(el); |
|
} |
|
|
|
/** |
|
* Determine if this element is hidden by its parent by offset or overflow |
|
* |
|
* @param {HTMLElement} el - The element under test |
|
* @return {Boolean} Is the element hidden by its parent |
|
*/ |
|
function isOffsetHidden(el) { |
|
var elBox = el.getBoundingClientRect(); |
|
var pBox = el.parentElement.getBoundingClientRect(); |
|
|
|
return elBox.top > pBox.bottom || |
|
elBox.bottom < pBox.top || |
|
elBox.left > pBox.right || |
|
elBox.right < pBox.left; |
|
} |
|
|
|
// export public API |
|
module.exports = { |
|
isHidden: isHidden |
|
}; |