diff --git a/accessibility-checker-engine/src/v4/rules/input_label_exists.ts b/accessibility-checker-engine/src/v4/rules/input_label_exists.ts
index 797b992f1..060663c02 100644
--- a/accessibility-checker-engine/src/v4/rules/input_label_exists.ts
+++ b/accessibility-checker-engine/src/v4/rules/input_label_exists.ts
@@ -176,7 +176,7 @@ export const input_label_exists: Rule = {
passed = CommonUtil.hasInnerContentHidden(ruleContext);
}
*/
- const pair = AccNameUtil.computeAccessibleName(ruleContext);console.log("node="+ruleContext.nodeName+", pair="+JSON.stringify(pair));
+ const pair = AccNameUtil.computeAccessibleName(ruleContext);
passed = pair && pair.name && pair.name.trim().length > 0;
if (passed) {
diff --git a/accessibility-checker-engine/src/v4/rules/input_label_visible.ts b/accessibility-checker-engine/src/v4/rules/input_label_visible.ts
index 839175158..f605c89f2 100644
--- a/accessibility-checker-engine/src/v4/rules/input_label_visible.ts
+++ b/accessibility-checker-engine/src/v4/rules/input_label_visible.ts
@@ -18,6 +18,7 @@ import { CommonUtil } from "../util/CommonUtil";
import { FragmentUtil } from "../../v2/checker/accessibility/util/fragment";
import { VisUtil } from "../util/VisUtil";
import { DOMUtil } from "../../v2/dom/DOMUtil";
+import { AccNameUtil } from "../util/AccNameUtil";
export const input_label_visible: Rule = {
id: "input_label_visible",
@@ -94,10 +95,11 @@ export const input_label_visible: Rule = {
}
}
}
-
+
+ const pair = AccNameUtil.computeAccessibleName(ruleContext);
// check visible label for input or button
if (nodeName === 'input' || nodeName === 'button') {
-
+ /**
if (CommonUtil.hasImplicitLabel(ruleContext))
return RulePass("pass");
@@ -116,6 +118,9 @@ export const input_label_visible: Rule = {
if (type === 'image' && CommonUtil.attributeNonEmpty(ruleContext, "alt"))
return RulePass("pass");
}
+ */
+ if (pair && pair.name && pair.name.trim().length > 0 && (pair.nameFrom === 'label' || pair.nameFrom === 'internal' || pair.nameFrom === 'alt'))
+ return RulePass("pass");
}
// custom widget submission is not in scope for this success criteria (IBMa/equal-access#204) if it is not associated with data entry
@@ -128,13 +133,17 @@ export const input_label_visible: Rule = {
// check if any visible text from the control.
// note that (1) the text doesn’t need to be associated with the control to form a relationship
- // and (2) the text doesn't need to follow accessible name requirement (e.g. nameFrom)
- if (!CommonUtil.isInnerTextEmpty(ruleContext))
+ // (2) the text doesn't need to follow accessible name requirement (e.g. nameFrom)
+ // and (3) an alternative tooltip exists that can be made visible through mouseover
+ /**if (!CommonUtil.isInnerTextEmpty(ruleContext))
return RulePass("pass");
-
+
// check if an alternative tooltip exists that can be made visible through mouseover
if (CommonUtil.attributeNonEmpty(ruleContext, "title"))
return RulePass("pass");
+ */
+ if (pair && pair.name && pair.name.trim().length > 0 && (pair.nameFrom === 'text' || pair.nameFrom === 'title'))
+ return RulePass("pass");
// check if any descendant with an alternative tooltip that can be made visible through mouseover
// only consider img and svg, and other text content of the descendant is covered in the isInnerText above
@@ -158,12 +167,15 @@ export const input_label_visible: Rule = {
}
}
- if (nodeName === "optgroup" && CommonUtil.attributeNonEmpty(ruleContext, "label"))
+ /**if (nodeName === "optgroup" && CommonUtil.attributeNonEmpty(ruleContext, "label"))
return RulePass("pass");
if (nodeName == "option" && (CommonUtil.attributeNonEmpty(ruleContext, "label") || ruleContext.innerHTML.trim().length > 0))
return RulePass("pass");
-
+ */
+ if ((nodeName === "optgroup" || nodeName == "option") && (pair && pair.name && pair.name.trim().length > 0 && (pair.nameFrom === 'label' || pair.nameFrom === 'content')))
+ return RulePass("pass");
+
// Determine if this is referenced by a combobox. If so, the label belongs to the combobox
let id = ruleContext.getAttribute("id");
if (id && id.trim().length > 0) {
diff --git a/accessibility-checker-engine/src/v4/util/AccNameUtil.ts b/accessibility-checker-engine/src/v4/util/AccNameUtil.ts
index f5acd86a0..cc3b88a81 100644
--- a/accessibility-checker-engine/src/v4/util/AccNameUtil.ts
+++ b/accessibility-checker-engine/src/v4/util/AccNameUtil.ts
@@ -39,7 +39,7 @@ export class AccNameUtil {
// get aria label even for the role where the name is prohibited or is 'presentation' or 'none'
let accName = AriaUtil.getAriaLabel(elem);
if (accName && accName.trim() !== "") {
- CacheUtil.setCache(elem, "ELEMENT_ACCESSBLE_NAME", {"name":accName, "nameFrom": "ariaLabel"});
+ CacheUtil.setCache(elem, "ELEMENT_ACCESSBLE_NAME", {"name":CommonUtil.truncateText(accName), "nameFrom": "ariaLabel"});
return {"name":CommonUtil.truncateText(accName), "nameFrom": "ariaLabel"};
}
@@ -82,7 +82,7 @@ export class AccNameUtil {
let placeholder = elem.getAttribute("placeholder");
if (placeholder && placeholder.trim().length > 0) {
placeholder = CommonUtil.truncateText(placeholder);
- CacheUtil.setCache(elem, "ELEMENT_ACCESSBLE_NAME", placeholder);
+ CacheUtil.setCache(elem, "ELEMENT_ACCESSBLE_NAME", {"name":placeholder, "nameFrom": "placeholder"});
return {"name":placeholder, "nameFrom": "placeholder"};
}
}
@@ -265,10 +265,20 @@ export class AccNameUtil {
}
});
if (text.trim() !== '')
- return {"name":text.trim(), "nameFrom": "iamges"};
+ return {"name":text.trim(), "nameFrom": "alt"};
}
}
+ // optgroup
+ // label participate in accessible name calculation: https://www.w3.org/TR/html-aam-1.0/#att-label
+ // The label attribute must be specified. Its value gives the name of the group
+ // the value is disabled in the interface
+ if (nodeName === "optgroup" || nodeName === "option" || nodeName === "track") {
+ const label = elem.getAttribute("label");
+ if (label && label.trim().length > 0)
+ return {"name":CommonUtil.truncateText(label), "nameFrom": "label"};
+ }
+
// svg
if (nodeName === "svg") {
const pair = AccNameUtil.computeAccessibleNameForSVGElement(elem);
@@ -341,52 +351,6 @@ export class AccNameUtil {
}
}
- // calculate accessible name for custom elements marked with aria
- /**public static computeAccessibleNameFromAttribute(elem: Element) : any | null {
- const nodeName = elem.nodeName.toLowerCase();
- const role = AriaUtil.getResolvedRole(elem);
-
- // textbox etc. return its text value
- if (role === "textbox") {
- const name = elem.getAttribute("value");
- if (name && name.trim().length > 0)
- return {"name":CommonUtil.truncateText(name), "nameFrom": "value"};
- }
-
- // for combobox or listbox roles, return the text alternative of the chosen option.
- if (role === "combobox" || role === "listbox") {
- const selectedId = elem.getAttribute("aria-activedescendant") || elem.getAttribute("aria-selected") || elem.getAttribute("aria-checked");
- if (selectedId) {
- let selectedOption = elem.ownerDocument.getElementById(selectedId);
- if (selectedOption && !DOMUtil.sameNode(elem, selectedOption)) {
- const pair = AccNameUtil.computeAccessibleName(selectedOption);
- if (pair && pair.name)
- return {"name": pair.name, "nameFrom": "option"};
- }
- }
- }
-
- // for range role type, including "progressbar", "scrollbar", "slider", "spinbutton" roles
- if (["progressbar", "scrollbar", "slider", "spinbutton", "meter"].includes(role)) {
- // If the aria-valuetext property is present, return its value
- let value = elem.getAttribute("aria-valuetext");
- if (value && value.trim().length > 0)
- return {"name":value, "nameFrom": "aria-valuetext"};
- // Otherwise, if the aria-valuenow property is present, return its value,
- value = elem.getAttribute("aria-valuenow");
- if (value && value.trim().length > 0)
- return {"name":CommonUtil.truncateText(value), "nameFrom": "aria-valuenow"};
-
- // finally use native value attribute
- value = elem.getAttribute("value");
- if (value && value.trim().length > 0)
- return {"name":CommonUtil.truncateText(value), "nameFrom": "value"};
- }
-
- // no accessible name exists
- return null;
- }*/
-
// calculate accessible name for custom elements marked with aria
public static computeAccessibleNameFromContent(elem: Element) : any | null {
const nodeName = elem.nodeName.toLowerCase();
diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts
index 971aae58e..2c648373c 100644
--- a/accessibility-checker-engine/src/v4/util/CommonUtil.ts
+++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts
@@ -851,27 +851,34 @@ export class CommonUtil {
public static getFormFieldLabel(elem) : string | null {
// get the label from the attribute "for" of the label element
// Get only the non-hidden labels for element
+ let value = "";
+ let label = null;
let labelElem = CommonUtil.getLabelForElementHidden(elem, true);
-
- /** if it's not label with for attribute, then find implicit label
- * cases for explict label:
- *
- *
- * cases for implicit label:
- *
- */
- if (!labelElem) {
+ if (labelElem) {
+ // value directly from element text
+ label = labelElem.innerText; // ignore hidden text
+ } else {
+ /** if it's not label with for attribute, then find implicit label
+ * cases for explict label:
+ *
+ *
+ * cases for implicit label:
+ *
+ */
labelElem = CommonUtil.getAncestor(elem, "label");
- if (!labelElem || labelElem.tagName.toLowerCase() !== "label" || !CommonUtil.isFirstFormElement(labelElem, elem))
- return null;
+ if (labelElem && labelElem.tagName.toLowerCase() === "label" && CommonUtil.isFirstFormElement(labelElem, elem)) {
+ let parentClone = labelElem.cloneNode(true);
+ // exclude all the text from the first form element since they might also
+ // have inner content that is part of innerText
+ parentClone = CommonUtil.removeAllFormElementsFromLabel(parentClone);
+ label = CommonUtil.getInnerText(parentClone);
+ } else
+ return null;
}
-
- let value = "";
- // value directly from element text
- let label = labelElem.innerText; // ignore hidden text
+
if (label && label.trim() !== "")
value += label.trim();
-
+
// value from child element attribute
label = CommonUtil.getLabelTextFromAttribute(labelElem, true);
if (label && label.trim() !== "")
@@ -906,13 +913,6 @@ export class CommonUtil {
break;
}
}
- //let labeledElem = null;
- /**if (labelElem.hasAttribute("for")) {
- const id = labelElem.getAttribute("for").trim();
- labeledElem = document.getElementById(id);
- if (!labeledElem || DOMUtil.sameNode(labeledElem, labelElem))
- labeledElem = null;
- }*/
let nw = new DOMWalker(labelElem);
let text = '';