@@ -178,19 +178,15 @@ function sortPageBackground(elmStack) {
178178 }
179179 return bgNodes ;
180180}
181-
182181/**
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
185184 * @memberof axe.commons.color
186185 * @instance
187- * @param {Element } elm
188- * @return {Array }
186+ * @param {DOMRect } rect
187+ * @return {Object }
189188 */
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 ) {
194190 let x , y ;
195191 if ( rect . left > window . innerWidth ) {
196192 return ;
@@ -205,7 +201,95 @@ color.getBackgroundStack = function(elm) {
205201 Math . ceil ( rect . top + ( rect . height / 2 ) ) ,
206202 window . innerHeight - 1 ) ;
207203
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 ; }
209293 elmStack = includeMissingElements ( elmStack , elm ) ;
210294 elmStack = dom . reduceToElementsBelowFloating ( elmStack , elm ) ;
211295 elmStack = sortPageBackground ( elmStack ) ;
0 commit comments