Katie Dektar | ea84ef8 | 2017-12-04 17:17:33 | [diff] [blame] | 1 | # Offscreen, Invisible and Size |
| 2 | |
| 3 | This document explains how Chrome interprets the guidelines to apply the labels |
| 4 | Offscreen and Invisible to nodes, and how the bounding box is calculated. |
| 5 | |
| 6 | ## Background |
| 7 | |
| 8 | In general, screen reading tools may be interested in all nodes regardless of |
| 9 | whether they are presented to sighted users, but other Accessibility tools may |
| 10 | care what is visible to sighted users. |
| 11 | |
| 12 | Specifically, tools like Select-to-Speak and Switch Access should not look at |
| 13 | nodes which are “offscreen”, “invisible”, or size=(0,0), as these are not |
| 14 | visible on the screen for mouse interactions. On the other hand, ChromeVox and |
| 15 | other screen readers may care about some of those nodes, which allow developers |
| 16 | to insert buttons visible only to users with a screen reader, or to navigate |
| 17 | below the fold. |
| 18 | |
| 19 | ## Offscreen |
| 20 | In Chrome, we define Offscreen as: |
| 21 | |
| 22 | >Any object is offscreen if it is fully clipped or scrolled out of view by any |
| 23 | of its ancestors so that it is not rendered on the screen. |
| 24 | |
| 25 | For example, the staticText node here is offscreen: |
| 26 | ```html |
| 27 | <div style="width:0; height; 0; overflow: hidden"> |
| 28 | This text should be marked "offscreen", although its parent is not. |
| 29 | </div> |
| 30 | ``` |
| 31 | |
| 32 | As background, [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx) |
| 33 | defines Offscreen as an object is not rendered, but not because it was |
Quinten Yearsley | 317532d | 2021-10-20 17:10:31 | [diff] [blame] | 34 | programmatically hidden: |
Katie Dektar | ea84ef8 | 2017-12-04 17:17:33 | [diff] [blame] | 35 | |
| 36 | >The object is clipped or has scrolled out of view, but it is not |
| 37 | programmatically hidden. If the user makes the viewport larger, more of the |
| 38 | object will be visible on the computer screen. |
| 39 | |
| 40 | In Chrome, we interpret this to mean that an object is fully clipped or |
| 41 | scrolled out of view by its parent or ancestors. The main difference is that |
| 42 | we are being explicit that any ancestor clipping a node can make it offscreen, |
| 43 | not just a rootWebArea or scrollable ancestor. |
| 44 | |
| 45 | ### Technical Implementation |
| 46 | Offscreen is calculated in [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc). |
| 47 | In this function, we walk up the accessibility tree adjusting a node's bounding |
| 48 | box to the frame of its ancestors. If the box is clipped because it lies |
| 49 | outside an ancestor's bounds, and that ancestor clips its children (i.e. |
| 50 | overflow:hidden, overflow:scroll, or it is a rootWebArea), offscreen is set to |
| 51 | true. |
| 52 | |
| 53 | ## Invisible |
Quinten Yearsley | 317532d | 2021-10-20 17:10:31 | [diff] [blame] | 54 | A node is marked Invisible in Chrome if it is hidden programmatically. In some |
Katie Dektar | ea84ef8 | 2017-12-04 17:17:33 | [diff] [blame] | 55 | cases, invisible nodes are simply excluded from the accessibility tree. Chrome |
| 56 | defines invisible as: |
| 57 | |
| 58 | >Invisible means that a node or its ancestor is explicitly invisible. |
| 59 | |
| 60 | This is the same as the definition from [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx): |
| 61 | |
| 62 | >The object is programmatically hidden. |
| 63 | |
| 64 | For example, these nodes are invisible: |
| 65 | |
| 66 | ```html |
| 67 | <div style="display:none"> |
| 68 | This text should be marked 'invisible', along with its parent div. |
| 69 | </div> |
| 70 | |
| 71 | <div style="visibility:hidden"> |
| 72 | This text should also be marked 'invisible' along with its parent div. |
| 73 | </div> |
Katie Dektar | ea84ef8 | 2017-12-04 17:17:33 | [diff] [blame] | 74 | ``` |
| 75 | |
Katie Dektar | 513c95c | 2022-09-23 18:36:33 | [diff] [blame] | 76 | ### Technical implementation |
| 77 | See `AXObject::IsVisible()` ([source](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/accessibility/ax_object.cc)). |
| 78 | |
| 79 | Note: `Opacity: 0` is explicitly not treated as invisible. |
| 80 | |
Katie Dektar | ea84ef8 | 2017-12-04 17:17:33 | [diff] [blame] | 81 | ## Bounding box calculation |
| 82 | A node's bounding box (location and size) are calculated based on its |
| 83 | intrinsic width, height and location, and the sizes of its ancestors. |
| 84 | We calculate size clipped by ancestors by default, but can also expose an |
| 85 | unclipped size through the [automation API](https://developer.chrome.com/extensions/automation). |
| 86 | |
| 87 | The unclipped bounding box is helpful if you want to know the current |
| 88 | coordinates of an element that's scrolled out of view, so you know how |
| 89 | much to scroll to bring it in view. |
| 90 | |
| 91 | The clipped bounding box is helpful if you want to draw a visible bounding |
| 92 | box around the element on the screen. Clipping the bounding box helps |
| 93 | sighted users understand what container the element is in, even if it's |
| 94 | not currently visible. Without clipping you end up with elements floating |
| 95 | outside of windows. |
| 96 | |
| 97 | ### Technical implementation |
| 98 | A node's location and size are calculated in[AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc), |
| 99 | and so closely tied to the offscreen calculation. In this function, we walk up |
| 100 | the accessibility tree adjusting a node's bounding box to the frame of its |
| 101 | ancestors. |
| 102 | |
| 103 | In general, this calculation is straight forward. But there are several edge |
| 104 | cases: |
| 105 | |
| 106 | * If a node has no intrinsic size, its size will be taken from the union of |
| 107 | its children. |
| 108 | |
| 109 | ```html |
| 110 | <!-- The outer div here has no size, so we use its child for its bounding box --> |
| 111 | <div style="visibility:hidden" aria-hidden=false> |
| 112 | <div style="visibility:visible"> |
| 113 | Visible text |
| 114 | </div> |
| 115 | </div> |
| 116 | ``` |
| 117 | |
| 118 | * If a node still has no size after that union, its bounds will be set to the |
| 119 | size of the nearest ancestor which has size. However, this node will be marked |
| 120 | `offscreen`, because it isn't visible to the user. |
| 121 | |
| 122 | * This is useful for exposing nodes to screen reader users. |
| 123 | |
| 124 | In addition, [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc) |
| 125 | is used to determine how location and size may be clipped by ancestors, |
| 126 | allowing bounding boxes to reflect the location of a node clipped to its |
| 127 | ancestors. |
| 128 | |
| 129 | * If a node is fully clipped by its ancestors such that the intersection of its |
| 130 | bounds and an ancestor's bounds are size 0, it will be pushed to the nearest |
| 131 | edge of the ancestor with width 1 or height 1. |
| 132 | |
| 133 | * We use width and height 1 instead of 0 to distinguish between empty or |
| 134 | unknown sized nodes vs known small or clipped sized nodes. |
| 135 | |
| 136 | Both clipped and unclipped location and size are exposed through the |
| 137 | [Chrome automation API](https://developer.chrome.com/extensions/automation). |
| 138 | |
| 139 | * `location` is the global location of a node as clipped by its ancestors. If |
| 140 | a node is fully scrolled off a rootWebArea in X, for example, its location will |
| 141 | be the height of the rootWebArea, and its height will be 1. The Y position and width will be unchanged. |
| 142 | |
| 143 | * `unclippedLocation` is the global location of a node ignoring any clipping |
| 144 | by ancestors. If a node is fully scrolled off a rootWebArea in X, for example, |
| 145 | its location will simply be larger than the height of the rootWebArea, and its |
| 146 | size will also be unchanged. |