diff --git a/lib/rules/scrollable-region-focusable-matches.js b/lib/rules/scrollable-region-focusable-matches.js index 34c4b08d77..a9fff1f6fa 100644 --- a/lib/rules/scrollable-region-focusable-matches.js +++ b/lib/rules/scrollable-region-focusable-matches.js @@ -1,21 +1,58 @@ import hasContentVirtual from '../commons/dom/has-content-virtual'; import isComboboxPopup from '../commons/aria/is-combobox-popup'; +import sanitize from '../commons/text/sanitize'; import { querySelectorAll, getScroll } from '../core/utils'; +// magic number of allowed negligence if element goes outside scrolling area +// set to a ~1em past the scroll area. can modify if needed +const buffer = 13; + export default function scrollableRegionFocusableMatches(node, virtualNode) { + const boundingRect = virtualNode.boundingClientRect; return ( // The element scrolls - getScroll(node, 13) !== undefined && + getScroll(node, buffer) !== undefined && // It's not a combobox popup, which commonly has keyboard focus added isComboboxPopup(virtualNode) === false && // And there's something actually worth scrolling to - isNoneEmptyElement(virtualNode) + hasScrollableContent(node, virtualNode, boundingRect) ); } -function isNoneEmptyElement(vNode) { - return querySelectorAll(vNode, '*').some(elm => - // (elm, noRecursion, ignoreAria) - hasContentVirtual(elm, true, true) - ); +function hasScrollableContent(node, virtualNode, boundingRect) { + return querySelectorAll(virtualNode, '*').some(vNode => { + const hasContent = hasContentVirtual(vNode, true, true); + if (!hasContent) { + return false; + } + + return getChildTextRects(vNode).some( + rect => + // part or all of the element is outside the scroll area + rect.left - boundingRect.left + rect.width > + node.clientWidth + buffer || + rect.top - boundingRect.top + rect.height > node.clientHeight + buffer + ); + }); +} + +function getChildTextRects(vNode) { + const boundingRect = vNode.boundingClientRect; + const clientRects = []; + + vNode.actualNode.childNodes.forEach(textNode => { + if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') { + return; + } + + clientRects.push(...getContentRects(textNode)); + }); + + return clientRects.length ? clientRects : [boundingRect]; +} + +function getContentRects(node) { + const range = document.createRange(); + range.selectNodeContents(node); + return Array.from(range.getClientRects()); } diff --git a/test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.html b/test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.html index 750785c2be..02f9741c63 100644 --- a/test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.html +++ b/test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.html @@ -3,9 +3,12 @@ -
Content
++ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium + doloremque laudantium. +
Content
++ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tincidunt + nisi quis elit volutpat dignissim. Vivamus quis bibendum nisl. Duis id + imperdiet quam. Sed cursus elit condimentum lectus viverra, quis molestie + erat ullamcorper. Ut ut elit nulla. Fusce fermentum aliquam augue, vitae + blandit diam dignissim ut. Aliquam feugiat velit tempor molestie tempor. + Nunc placerat et ante id imperdiet. Integer volutpat, tortor ut facilisis + tincidunt, sapien ex molestie metus, vel eleifend tortor sapien vitae + elit. Pellentesque vel tristique odio. Duis ante augue, luctus eget + eleifend ut, malesuada sit amet diam. Duis viverra blandit erat ac ornare. + Quisque ut auctor justo. +
Content
++ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc + tincidunt nisi quis elit volutpat dignissim. Vivamus quis bibendum + nisl. Duis id imperdiet quam. Sed cursus elit condimentum lectus + viverra, quis molestie erat ullamcorper. Ut ut elit nulla. Fusce + fermentum aliquam augue, vitae blandit diam dignissim ut. Aliquam + feugiat velit tempor molestie tempor. Nunc placerat et ante id + imperdiet. Integer volutpat, tortor ut facilisis tincidunt, sapien ex + molestie metus, vel eleifend tortor sapien vitae elit. Pellentesque + vel tristique odio. Duis ante augue, luctus eget eleifend ut, + malesuada sit amet diam. Duis viverra blandit erat ac ornare. Quisque + ut auctor justo. +
Content
++ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium + doloremque laudantium. +
Contents
+