Skip to content

Commit

Permalink
rule update #1674
Browse files Browse the repository at this point in the history
  • Loading branch information
shunguoy committed Oct 25, 2023
1 parent 7a75e2f commit bd52e01
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -465,61 +465,52 @@ export class RPTUtil {
public static isInline(element) {
if (!element) return false;

var uStyle = getComputedStyle(element);
if (!uStyle) return false;
const udisplay = cStyle.getPropertyValue("display");
// focus on inline element only
const style = getComputedStyle(element);
if (!style) return false;
const udisplay = style.getPropertyValue("display");
// inline element only
if (udisplay !== 'inline')
return false;

const parent = element.parentElement;
if (parent) {
var cStyle = getComputedStyle(parent);
var display = cStyle.getPropertyValue("display");
const style = getComputedStyle(parent);
const display = style.getPropertyValue("display");
// an inline element is inside a block. note <body> is a block element too
if (display === 'block' || display === 'inline-block') {
let multipleInline = false;
let containText = false;
// more than one inline elements with text in the same line: <inline>text<target>, <target><inline>text, text<target><inline>
// one or more inline elements with text in the same line: text<target>, <target>text, <inline>+text<target>, <target><inline>+text, text<target><inline>+
let walkNode = element.nextSibling;
while (walkNode) {
while (!containText && walkNode) {
// note browsers insert Text nodes to represent whitespaces.
if (walkNode.nodeType === Node.TEXT_NODE && walkNode.nodeValue && walkNode.nodeValue.trim().length > 0) {
containText = true;
} else if (walkNode.nodeType === Node.ELEMENT_NODE) {
cStyle = getComputedStyle(walkNode);
display = cStyle.getPropertyValue("display");
if (display !== 'inline') {
multipleInline = false;
break;
}
multipleInline = true;
const cStyle = getComputedStyle(walkNode);
const cDisplay = cStyle.getPropertyValue("display");
if (cDisplay !== 'inline') break;
}
walkNode = walkNode.nextSibling;
}

walkNode = element.previousSibling;
while (walkNode) {
if (walkNode.nodeType === Node.TEXT_NODE && walkNode.nodeValue && walkNode.nodeValue.trim().length > 0) {
containText = true;
} else if (walkNode.nodeType === Node.ELEMENT_NODE) {
cStyle = getComputedStyle(walkNode);
display = cStyle.getPropertyValue("display");
if (display !== 'inline') {
multipleInline = false;
break;
if (!containText) {
walkNode = element.previousSibling;
while (!containText && walkNode) {
// note browsers insert Text nodes to represent whitespaces.
if (walkNode.nodeType === Node.TEXT_NODE && walkNode.nodeValue && walkNode.nodeValue.trim().length > 0) {
containText = true;
} else if (walkNode.nodeType === Node.ELEMENT_NODE) {
const cStyle = getComputedStyle(walkNode);
const cDisplay = cStyle.getPropertyValue("display");
if (cDisplay !== 'inline') break;
}
multipleInline = true;
walkNode = walkNode.previousSibling;
}
walkNode = walkNode.previousSibling;
}

// an inline element is the only element in the line inside a block
// note browsers insert Text nodes to represent whitespaces.
if (parent.childNodes.length === 1)
return false;

// multiple inline elements are in the same line with text


// one or more inline elements are in the same line with text
if (containText) return true;
// one or more inline elements are in the same inline without text
return false;
}
}
// all other cases
Expand Down
111 changes: 82 additions & 29 deletions accessibility-checker-engine/src/v4/rules/target_spacing_sufficient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
*****************************************************************************/

import { RPTUtil } from "../../v2/checker/accessibility/util/legacy";
import { Rule, RuleResult, RuleContext, RulePass, RuleContextHierarchy, RulePotential } from "../api/IRule";
import { Rule, RuleResult, RuleContext, RulePass, RuleContextHierarchy, RuleFail, RulePotential } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
import { VisUtil } from "../../v2/dom/VisUtil";
import { DOMMapper } from "../../v2/dom/DOMMapper";
import { getDefinedStyles, getComputedStyle } from "../util/CSSUtil";
import { getComputedStyle } from "../util/CSSUtil";

export let target_spacing_sufficient: Rule = {
id: "target_spacing_sufficient",
Expand All @@ -25,8 +25,12 @@
help: {
"en-US": {
"group": "target_spacing_sufficient.html",
"pass": "target_spacing_sufficient.html",
"potential_obscured": "target_spacing_sufficient.html"
"pass_spacing": "target_spacing_sufficient.html",
"pass_sized": "target_spacing_sufficient.html",
"pass_inline": "target_spacing_sufficient.html",
"pass_default": "target_spacing_sufficient.html",
"violation": "target_spacing_sufficient.html",
"potential_overlap": "target_spacing_sufficient.html"
}
},
messages: {
Expand All @@ -36,7 +40,6 @@
"pass_sized": "The target’s size is more than 24 CSS pixels",
"pass_inline": "The target is in a sentence or its size is otherwise constrained by the line-height of non-target text",
"pass_default": "The size of the target is determined by the user agent and is not modified by the author",
"pass": "The element is not entirely covered by other content",
"violation": "The center of the <0> target is less than 12 CSS pixels from the bounding box (edge) of an adjacent target <1>",
"potential_overlap": "Ensure the overlapped <0> element meets a minimum target size or has sufficient spacing from the overlapping element"
}
Expand All @@ -55,23 +58,17 @@
if (RPTUtil.getAncestor(ruleContext, ["pre", "code", "script", "meta", 'head']) !== null
|| nodeName === "body" || nodeName === "html" )
return null;
console.log("node=" + nodeName +", inline=" +RPTUtil.isInline(ruleContext));
if (RPTUtil.isInline(ruleContext))
return null;


// ignore hidden, non-target, or inline element without text in the same line
if (!VisUtil.isNodeVisible(ruleContext) || !RPTUtil.isTarget(ruleContext))
return null;




console.log("node=" + nodeName +", id=" + ruleContext.getAttribute('id') +", inline=" +RPTUtil.isInline(ruleContext));
if (RPTUtil.isInline(ruleContext))
return RulePass("pass_inline");

const bounds = context["dom"].bounds;

//in case the bounds not available
if (!bounds) return null;

//ignore
if (bounds['height'] === 0 || bounds['width'] === 0 )
if (!bounds || bounds['height'] === 0 || bounds['width'] === 0 )
return null;

var doc = ruleContext.ownerDocument;
Expand All @@ -92,20 +89,24 @@
return;

const mapper : DOMMapper = new DOMMapper();
let violations = [];
let before = true;
let minX = 24;
let minY = 24;
let adjacentX = null;
let adjacentY = null;
for (let i=0; i < elems.length; i++) {
const elem = elems[i] as HTMLElement;
/**
* the nodes returned from querySelectorAll is in document order
* if two elements overlap and z-index are not defined, then the node rendered earlier will be overlaid by the node rendered later
* filter out the elements that’re descendant or ancestors of the target element<X>
*/
if (ruleContext.contains(elem)) {
//the next node in elems will be after the target node (ruleContext).
before = false;
continue;
}
if (!VisUtil.isNodeVisible(elem) || elem.contains(ruleContext)) continue;
if (!VisUtil.isNodeVisible(elem) || !RPTUtil.isTarget(elem) || elem.contains(ruleContext)) continue;

const bnds = mapper.getBounds(elem);
if (bnds.height === 0 || bnds.width === 0) continue;
Expand All @@ -117,23 +118,75 @@
if (!z_index || isNaN(Number(z_index)))
z_index = "0";
}

// ignore if the target is entirely covered: tabbable target handled by element_tabbable_unobscured and tabindex=-1 ignored
if (bnds.top <= bounds.top && bnds.left <= bounds.left && bnds.top + bnds.height >= bounds.top + bounds.height
&& bnds.left + bnds.height >= bounds.left + bounds.width
&& (before ? parseInt(zindex) < parseInt(z_index): parseInt(zindex) <= parseInt(z_index)))
// if the target is entirely covered: tabbable target handled by element_tabbable_unobscured and tabindex=-1 ignored
continue;
&& bnds.left + bnds.height >= bounds.left + bounds.width)
if (before ? parseInt(zindex) < parseInt(z_index): parseInt(zindex) <= parseInt(z_index))
return null;
else {
if (bnds.height >= 24 && bnds.width >= 24)
return RulePass("pass_sized");
return RuleFail("violation", [nodeName, elem.nodeName.toLowerCase()]);
}

// the element overlaps with target
if (((bounds.top >= bnds.top && bounds.top <= bnds.top + bnds.height) || (bounds.top <= bnds.top && bounds.top + bounds.height > bnds.top))
&& ((bounds.left >= bnds.left && bounds.left <= bnds.left + bnds.width) || (bounds.left <= bnds.left && bounds.left + bounds.width > bnds.left)))
return null;

else { // no overlap
if (bnds.height >= 24 && bnds.width >= 24)
return RulePass("pass_sized");

// the element is in the horizontally same row
let disX = 24;
let disY = 24;
if ((bounds.top >= bnds.top && bounds.top <= bnds.top + bnds.height) || (bounds.top <= bnds.top && bounds.top + bounds.height > bnds.top))
disX = Math.min( Math.abs(bounds.left - bnds.left), Math.abs(bounds.left - (bnds.left + bnds.width)), Math.abs(bounds.left + bounds.width - (bnds.left + bnds.width)), Math.abs(bounds.left + bounds.width - bnds.left));

// the element is in the horizontally same column
if ((bounds.left >= bnds.left && bounds.left <= bnds.left + bnds.width) || (bounds.left <= bnds.left && bounds.left + bounds.width > bnds.left))
disY = Math.min(Math.abs(bounds.top - bnds.top), Math.abs(bounds.top - (bnds.top + bnds.height)), Math.abs(bounds.top + bounds.height - (bnds.top + bnds.height)), Math.abs(bounds.top + bounds.height - bnds.top));

if (disX < minX) {
minX = disX;
adjacentX = elem;
}
if (disY < minY) {
minY = disY;
adjacentY = elem;
}

}
}

if (Math.round(bounds.width/2) + minX < 12 || Math.round(bounds.height/2) + minY < 12) {
if (Math.round(bounds.width/2) + minX < Math.round(bounds.height/2) + minY)
return RuleFail("violation", [nodeName, adjacentX.nodeName.toLowerCase()]);
return RuleFail("violation", [nodeName, adjacentY.nodeName.toLowerCase()]);
}
//if (bounds.top <= bnds.top && bounds.top + bounds.height >= bnds.top || bounds.top <= bnds.top+ bounds.height && bounds.top + bounds.height >= bnds.top + bnds.height)


//minX = min(minX, min( abs(bounds.left - bnds.left), abs(bounds.left - bnds.right), abs(bounds.right - bnds.right), abs(bounds.right - bnds.left))

//if (bounds.left <= bnds.left && bounds.left+bounds.width >= bnds.left+bnds.width || bounds.left <= bnds.left + bnds.width && bounds.left + bounds.width >= bnds.right)

//minY = min(minY, min(abs(bounds.top - bnds.top), abs(bounds.top - bnds.bottom), abs(bounds.bottom - bnds.bottom), abs(bounds.bottom - bnds.top)))



// if the target is on top of an overlapping elements
/**
if (bnds.height !== 0 && bnds.width !== 0
&& bnds.top <= bounds.top && bnds.left <= bounds.left && bnds.top + bnds.height >= bounds.top + bounds.height
&& bnds.left + bnds.height >= bounds.left + bounds.width
&& (before ? parseInt(zindex) < parseInt(z_index): parseInt(zindex) <= parseInt(z_index)))
violations.push(elem);
}
*/

if (violations.length > 0)
return RulePotential("potential_obscured", []);

return RulePass("pass");
return RulePass("pass_spacing");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
</head>
<body>
<div style="width:150px;">
<div>This is X <a href="#" style="display:inline;">l</a> in text. This is Y <a href="#" style="display:inline;">l</a> in text.</div>
<div style="margin-top: 2rem;">
<div style="width:110px;line-height:50px;"><a href="#" style="display:inline;">z</a><a href="#" style="display:inline;">v</a>.</div>
<div>This is link <a id='x' href="#" style="display:inline;">x</a> in text. <br> <a id='y' href="#" style="display:inline;">This is link y</a> <br> in text.</div>
<p>

<div style="margin-top: 2rem;">
<div style="width:110px;line-height:50px;">&nbsp;<a id='a1' href="#" style="display:inline;">a</a>&nbsp;</div>
<div style="width:110px;line-height:50px;">&nbsp;<a id='z1' href="#" style="display:inline;">z</a>&nbsp;<a id='v1' href="#" style="display:inline;">v</a></div>
<div style="width:110px;line-height:50px;">&nbsp;&nbsp;<a id='z2' href="#" style="display:inline;">z</a>&nbsp;&nbsp;<a id='v2' href="#" style="display:inline;">v</a>.</div>
<div style="width:110px;line-height:50px;">&nbsp;<a id='z3' href="#" style="display:inline;">z</a>&nbsp;.&nbsp;<a id='v3'href="#" style="display:inline;">v</a></div>
<div style="width:110px;line-height:50px;">&nbsp;&nbsp;.&nbsp;&nbsp;<a id='z4' href="#" style="display:inline;">z</a>&nbsp;&nbsp;<a id='v4' href="#" style="display:inline;">v</a></div>
</div>

</body>
Expand Down

0 comments on commit bd52e01

Please sign in to comment.