diff --git a/lib/commons/dom/visibility-methods.js b/lib/commons/dom/visibility-methods.js index cf11a64779..de57430898 100644 --- a/lib/commons/dom/visibility-methods.js +++ b/lib/commons/dom/visibility-methods.js @@ -117,16 +117,33 @@ export function overflowHidden(vNode, { isAncestor } = {}) { return false; } - const rect = vNode.boundingClientRect; - const nodes = getOverflowHiddenAncestors(vNode); + // a node with position fixed cannot be hidden by an overflow + // ancestor, even when that ancestor uses a non-static position + const position = vNode.getComputedStylePropertyValue('position'); + if (position === 'fixed') { + return false; + } + const nodes = getOverflowHiddenAncestors(vNode); if (!nodes.length) { return false; } + const rect = vNode.boundingClientRect; return nodes.some(node => { - const nodeRect = node.boundingClientRect; + // a node with position absolute will not be hidden by an + // overflow ancestor unless the ancestor uses a non-static + // position or a node in-between uses a position of relative + // or sticky + if ( + position === 'absolute' && + !hasPositionedAncestorBetween(vNode, node) && + node.getComputedStylePropertyValue('position') === 'static' + ) { + return false; + } + const nodeRect = node.boundingClientRect; if (nodeRect.width < 2 || nodeRect.height < 2) { return true; } @@ -238,3 +255,20 @@ export function detailsHidden(vNode) { return !vNode.parent.hasAttr('open'); } + +function hasPositionedAncestorBetween(child, ancestor) { + let node = child.parent; + while (node && node !== ancestor) { + if ( + ['relative', 'sticky'].includes( + node.getComputedStylePropertyValue('position') + ) + ) { + return true; + } + + node = node.parent; + } + + return false; +} diff --git a/test/commons/dom/visibility-methods.js b/test/commons/dom/visibility-methods.js index 867b50f691..f62b723c72 100644 --- a/test/commons/dom/visibility-methods.js +++ b/test/commons/dom/visibility-methods.js @@ -302,6 +302,97 @@ describe('dom.visibility-methods', () => { ); assert.isFalse(overflowHidden(vNode)); }); + + it('should return false for ancestor with "overflow:hidden" and element with "position:fixed"', () => { + var vNode = queryFixture( + '
' + + '
Hello world
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); + + it('should return false for ancestor with "overflow:hidden; position:relative" and element with "position:fixed"', () => { + var vNode = queryFixture( + '
' + + '
Hello world
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); + + it('should return false for ancestor with "overflow:hidden" and element with "position:absolute"', () => { + var vNode = queryFixture( + '
' + + '
Hello world
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); + + it('should return true for ancestor with "overflow:hidden; position:relative" and element with "position:absolute"', () => { + var vNode = queryFixture( + '
' + + '
Hello world
' + + '
' + ); + assert.isTrue(overflowHidden(vNode)); + }); + + it('should return true for ancestor with "overflow:hidden" and element with "position:absolute" if ancestor in-between uses "position:relative"', () => { + var vNode = queryFixture( + '
' + + '
' + + '
Hello world
' + + '
' + + '
' + ); + assert.isTrue(overflowHidden(vNode)); + }); + + it('should return true for ancestor with "overflow:hidden" and element with "position:absolute" if ancestor in-between uses "position:sticky"', () => { + var vNode = queryFixture( + '
' + + '
' + + '
Hello world
' + + '
' + + '
' + ); + assert.isTrue(overflowHidden(vNode)); + }); + + it('should return false for ancestor with "overflow:hidden" and element with "position:absolute" if ancestor in-between uses "position:absolute"', () => { + var vNode = queryFixture( + '
' + + '
' + + '
Hello world
' + + '
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); + + it('should return false for ancestor with "overflow:hidden" and element with "position:absolute" if ancestor in-between uses "position:fixed"', () => { + var vNode = queryFixture( + '
' + + '
' + + '
Hello world
' + + '
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); + + it('should return false for ancestor with "overflow:hidden" and element with "position:absolute" if ancestor of overflow node uses position other than static', () => { + var vNode = queryFixture( + '
' + + '
' + + '
Hello world
' + + '
' + + '
' + ); + assert.isFalse(overflowHidden(vNode)); + }); }); describe('clipHidden', () => {