From 05de4eb44e720bac32973ea510052b417e97518d Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 6 Mar 2023 12:12:21 +0100 Subject: [PATCH 1/2] chore: Refactor createStackingOrder --- lib/commons/dom/create-grid.js | 108 +++++++++++++++++---------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/lib/commons/dom/create-grid.js b/lib/commons/dom/create-grid.js index 5485e69f81..fc54eaa122 100644 --- a/lib/commons/dom/create-grid.js +++ b/lib/commons/dom/create-grid.js @@ -7,6 +7,9 @@ import constants from '../../core/constants'; import cache from '../../core/base/cache'; import assert from '../../core/utils/assert'; +const FLOAT_ORDER = 0.25; +const STATIC_POSITION_ORDER = 0.5; + /** * Setup the 2d grid and add every element to it, even elements not * included in the flat tree @@ -57,11 +60,11 @@ export default function createGrid( if (vNode && vNode.parent) { parentVNode = vNode.parent; } - // elements with an assigned slot need to be a child of the slot element + // Elements with an assigned slot need to be a child of the slot element else if (node.assignedSlot) { parentVNode = getNodeFromTree(node.assignedSlot); } - // an svg in IE11 does not have a parentElement but instead has a + // An SVG in IE11 does not have a parentElement but instead has a // parentNode. but parentNode could be a shadow root so we need to // verify it's in the tree first else if (node.parentElement) { @@ -74,7 +77,7 @@ export default function createGrid( vNode = new axe.VirtualNode(node, parentVNode); } - vNode._stackingOrder = getStackingOrder(vNode, parentVNode); + vNode._stackingOrder = createStackingOrder(vNode, parentVNode); const scrollRegionParent = findScrollRegionParent(vNode, parentVNode); const grid = scrollRegionParent ? scrollRegionParent._subGrid : rootGrid; @@ -248,65 +251,68 @@ function isFlexOrGridContainer(vNode) { * @param {VirtualNode} * @return {Number[]} */ -function getStackingOrder(vNode, parentVNode) { +function createStackingOrder(vNode, parentVNode) { const stackingOrder = parentVNode._stackingOrder.slice(); - const zIndex = vNode.getComputedStylePropertyValue('z-index'); - const positioned = - vNode.getComputedStylePropertyValue('position') !== 'static'; - const floated = vNode.getComputedStylePropertyValue('float') !== 'none'; - - if (isStackingContext(vNode, parentVNode)) { - // if an element creates a stacking context, find the first - // true stack (not a "fake" stack created from positioned or - // floated elements without a z-index) and create a new stack at - // that point (step #5 and step #8) - // @see https://www.w3.org/Style/css2-updates/css2/zindex.html - const index = stackingOrder.findIndex( - value => (value < 1 && value > 0.2) || value === 0 - ); - if (index !== -1) { - stackingOrder.splice(index, stackingOrder.length - index); - } - // flex and grid items can use z-index even if position: static - // @see https://www.w3.org/TR/css-flexbox-1/#painting - // @see https://www.w3.org/TR/css-grid-1/#z-order - if ( - !['auto', '0'].includes(zIndex) && - (positioned || isFlexOrGridContainer(parentVNode)) - ) { - stackingOrder.push(parseInt(zIndex)); - } - // since many things can create a new stacking context without position or - // z-index, we need to know the order in the dom to sort them by. Use the - // nodeIndex property to create a number less than the "fake" stacks from - // positioned or floated elements but still larger than 0 - // 10 pad gives us the ability to sort up to 1B nodes (padStart does not - // exist in ie11) - else { - let float = vNode.nodeIndex.toString(); - while (float.length < 10) { - float = '0' + float; - } - stackingOrder.push(parseFloat('0.' + float)); - } - } // if a positioned element has z-index: auto or 0 (step #8), or if // a non-positioned floating element (step #5), treat it as its // own stacking context // @see https://www.w3.org/Style/css2-updates/css2/zindex.html - else if (positioned) { - // Put positioned elements above floated elements - stackingOrder.push(0.5); - } else if (floated) { - // Put floated elements above z-index: 0 - // (step #5 floating get sorted below step #8 positioned) - stackingOrder.push(0.25); + if (!isStackingContext(vNode, parentVNode)) { + if (vNode.getComputedStylePropertyValue('position') !== 'static') { + // Put positioned elements above floated elements + stackingOrder.push(STATIC_POSITION_ORDER); + } else if (vNode.getComputedStylePropertyValue('float') !== 'none') { + // Put floated elements above z-index: 0 + // (step #5 floating get sorted below step #8 positioned) + stackingOrder.push(FLOAT_ORDER); + } + return stackingOrder; + } + + // if an element creates a stacking context, find the first + // true stack (not a "fake" stack created from positioned or + // floated elements without a z-index) and create a new stack at + // that point (step #5 and step #8) + // @see https://www.w3.org/Style/css2-updates/css2/zindex.html + const index = stackingOrder.findIndex(value => + [0, FLOAT_ORDER, STATIC_POSITION_ORDER].includes(value) + ); + if (index !== -1) { + stackingOrder.splice(index, stackingOrder.length - index); + } + + const zIndex = getRealZIndex(vNode, parentVNode); + if (!['auto', '0'].includes(zIndex)) { + stackingOrder.push(parseInt(zIndex)); + return stackingOrder; + } + // since many things can create a new stacking context without position or + // z-index, we need to know the order in the dom to sort them by. Use the + // nodeIndex property to create a number less than the "fake" stacks from + // positioned or floated elements but still larger than 0 + // 10 pad gives us the ability to sort up to 1B nodes (padStart does not + // exist in ie11) + let float = vNode.nodeIndex.toString(); + while (float.length < 10) { + float = '0' + float; } + stackingOrder.push(parseFloat('0.' + float)); return stackingOrder; } +function getRealZIndex(vNode, parentVNode) { + const position = vNode.getComputedStylePropertyValue('position'); + if (position === 'static' && !isFlexOrGridContainer(parentVNode)) { + // z-index is ignored on position:static, except if on a flex or grid + // @see https://www.w3.org/TR/css-flexbox-1/#painting + // @see https://www.w3.org/TR/css-grid-1/#z-order + return 'auto'; + } + return vNode.getComputedStylePropertyValue('z-index'); +} + /** * Return the parent node that is a scroll region. * @param {VirtualNode} From 8a4678d400933472e997c4db198cfbf559c93bfa Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 6 Mar 2023 13:31:21 +0100 Subject: [PATCH 2/2] Remove magic numbers --- lib/commons/dom/create-grid.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/commons/dom/create-grid.js b/lib/commons/dom/create-grid.js index fc54eaa122..050adb80db 100644 --- a/lib/commons/dom/create-grid.js +++ b/lib/commons/dom/create-grid.js @@ -7,8 +7,10 @@ import constants from '../../core/constants'; import cache from '../../core/base/cache'; import assert from '../../core/utils/assert'; -const FLOAT_ORDER = 0.25; -const STATIC_POSITION_ORDER = 0.5; +const ROOT_ORDER = 0; +const DEFAULT_ORDER = 0.1; +const FLOAT_ORDER = 0.2; +const POSITION_STATIC_ORDER = 0.3; /** * Setup the 2d grid and add every element to it, even elements not @@ -35,7 +37,7 @@ export default function createGrid( vNode = new VirtualNode(document.documentElement); } - vNode._stackingOrder = [0]; + vNode._stackingOrder = [ROOT_ORDER]; rootGrid ??= new Grid(); addNodeToGrid(rootGrid, vNode); @@ -261,7 +263,7 @@ function createStackingOrder(vNode, parentVNode) { if (!isStackingContext(vNode, parentVNode)) { if (vNode.getComputedStylePropertyValue('position') !== 'static') { // Put positioned elements above floated elements - stackingOrder.push(STATIC_POSITION_ORDER); + stackingOrder.push(POSITION_STATIC_ORDER); } else if (vNode.getComputedStylePropertyValue('float') !== 'none') { // Put floated elements above z-index: 0 // (step #5 floating get sorted below step #8 positioned) @@ -276,7 +278,7 @@ function createStackingOrder(vNode, parentVNode) { // that point (step #5 and step #8) // @see https://www.w3.org/Style/css2-updates/css2/zindex.html const index = stackingOrder.findIndex(value => - [0, FLOAT_ORDER, STATIC_POSITION_ORDER].includes(value) + [ROOT_ORDER, FLOAT_ORDER, POSITION_STATIC_ORDER].includes(value) ); if (index !== -1) { stackingOrder.splice(index, stackingOrder.length - index); @@ -297,7 +299,7 @@ function createStackingOrder(vNode, parentVNode) { while (float.length < 10) { float = '0' + float; } - stackingOrder.push(parseFloat('0.' + float)); + stackingOrder.push(DEFAULT_ORDER + parseFloat('0.0' + float)); return stackingOrder; }