@@ -178,19 +178,15 @@ function sortPageBackground(elmStack) {
178
178
}
179
179
return bgNodes ;
180
180
}
181
-
182
181
/**
183
- * Get all elements rendered underneath the current element, In the order they are displayed (front to back)
184
- * @method getBackgroundStack
182
+ * Get coordinates for an element's client rects or bounding client rect
183
+ * @method getCoords
185
184
* @memberof axe.commons.color
186
185
* @instance
187
- * @param {Element } elm
188
- * @return {Array }
186
+ * @param {DOMRect } rect
187
+ * @return {Object }
189
188
*/
190
- color . getBackgroundStack = function ( elm ) {
191
- // allows inline elements spanning multiple lines to be evaluated
192
- let multipleRects = elm . getClientRects ( ) ;
193
- let rect = multipleRects . length > 1 ? multipleRects [ 0 ] : elm . getBoundingClientRect ( ) ;
189
+ color . getCoords = function ( rect ) {
194
190
let x , y ;
195
191
if ( rect . left > window . innerWidth ) {
196
192
return ;
@@ -205,7 +201,95 @@ color.getBackgroundStack = function(elm) {
205
201
Math . ceil ( rect . top + ( rect . height / 2 ) ) ,
206
202
window . innerHeight - 1 ) ;
207
203
208
- let elmStack = Array . from ( document . elementsFromPoint ( x , y ) ) ;
204
+ return { x, y} ;
205
+ } ;
206
+ /**
207
+ * Get elements from point for block and inline elements, excluding line breaks
208
+ * @method getRectStack
209
+ * @memberof axe.commons.color
210
+ * @instance
211
+ * @param {Element } elm
212
+ * @return {Array }
213
+ */
214
+ color . getRectStack = function ( elm ) {
215
+ let boundingCoords = color . getCoords ( elm . getBoundingClientRect ( ) ) ;
216
+ if ( boundingCoords ) {
217
+ // allows inline elements spanning multiple lines to be evaluated
218
+ let rects = Array . from ( elm . getClientRects ( ) ) ;
219
+ let boundingStack = Array . from ( document . elementsFromPoint ( boundingCoords . x , boundingCoords . y ) ) ;
220
+ if ( rects && rects . length > 1 ) {
221
+ let filteredArr = rects . filter ( ( rect ) => {
222
+ // exclude manual line breaks in Chrome/Safari
223
+ return rect . width && rect . width > 0 ;
224
+ } )
225
+ . map ( ( rect ) => {
226
+ let coords = color . getCoords ( rect ) ;
227
+ if ( coords ) {
228
+ return Array . from ( document . elementsFromPoint ( coords . x , coords . y ) ) ;
229
+ }
230
+ } ) ;
231
+ // add bounding client rect stack for comparison later
232
+ filteredArr . splice ( 0 , 0 , boundingStack ) ;
233
+ return filteredArr ;
234
+ } else {
235
+ return [ boundingStack ] ;
236
+ }
237
+ }
238
+ return null ;
239
+ } ;
240
+ /**
241
+ * Get filtered stack of block and inline elements, excluding line breaks
242
+ * @method filteredRectStack
243
+ * @memberof axe.commons.color
244
+ * @instance
245
+ * @param {Element } elm
246
+ * @return {Array }
247
+ */
248
+ color . filteredRectStack = function ( elm ) {
249
+ let rectStack = color . getRectStack ( elm ) ;
250
+ if ( rectStack && rectStack . length === 1 ) {
251
+ // default case, elm.getBoundingClientRect()
252
+ return rectStack [ 0 ] ;
253
+ }
254
+ else if ( rectStack && rectStack . length > 1 ) {
255
+ let boundingStack = rectStack . shift ( ) ;
256
+ let isSame ;
257
+ // iterating over arrays of DOMRects
258
+ rectStack . forEach ( ( rectList , index ) => {
259
+ if ( index === 0 ) { return ; }
260
+ // if the stacks are the same, use the first one. otherwise, return null.
261
+ let rectA = rectStack [ index - 1 ] ,
262
+ rectB = rectStack [ index ] ;
263
+
264
+ // if elements in clientRects are the same
265
+ // or the boundingClientRect contains the differing element, pass it
266
+ isSame = rectA . every ( function ( element , elementIndex ) {
267
+ return element === rectB [ elementIndex ] ;
268
+ } ) || boundingStack . includes ( elm ) ;
269
+ } ) ;
270
+ if ( ! isSame ) {
271
+ axe . commons . color . incompleteData . set ( 'bgColor' , 'elmPartiallyObscuring' ) ;
272
+ return null ;
273
+ }
274
+ // pass the first stack if it wasn't partially covered
275
+ return rectStack [ 0 ] ;
276
+ } else {
277
+ // rect outside of viewport
278
+ axe . commons . color . incompleteData . set ( 'bgColor' , 'outsideViewport' ) ;
279
+ return null ;
280
+ }
281
+ } ;
282
+ /**
283
+ * Get all elements rendered underneath the current element, In the order they are displayed (front to back)
284
+ * @method getBackgroundStack
285
+ * @memberof axe.commons.color
286
+ * @instance
287
+ * @param {Element } elm
288
+ * @return {Array }
289
+ */
290
+ color . getBackgroundStack = function ( elm ) {
291
+ let elmStack = color . filteredRectStack ( elm ) ;
292
+ if ( elmStack === null ) { return null ; }
209
293
elmStack = includeMissingElements ( elmStack , elm ) ;
210
294
elmStack = dom . reduceToElementsBelowFloating ( elmStack , elm ) ;
211
295
elmStack = sortPageBackground ( elmStack ) ;
0 commit comments