Max Design

Published:

Did you know that with some simple JavaScript, you can highlight headings, display accessible names, check image alternatives, outline landmarks, and inspect many other accessibility features on any page?

Let’s explore how to open the console and use a few simple scripts to inspect accessibility features on any page.

What is the console?

The console is a tool built into every modern browser. It lets you:

Think of it as a place to try things out and see how the page behaves. You’re not modifying the real website — just running temporary code in your own browser to inspect or experiment with what’s on the page.

How to open the console

Mac

Windows

How to use these scripts

Each example script in this article follows the same simple process. You don’t need to install anything, and you can’t break the website — these scripts only display information visually or in the console.

  1. Open the browser console
  2. Paste the script
  3. Press ENTER
  4. View the results
  5. Refresh the page to clear the overlays and return to normal.

Some quick examples

Here’s a simple script you can try first. It shows every link on the page as a NodeList in the console, which is useful for quick checks.

document.querySelectorAll("a");

Or you could highlight all links on the page:

document.querySelectorAll("a").forEach(a => a.style.outline = "2px solid red");

Some people turn snippets like these into bookmarklets (small saved JavaScript actions). That can be useful, but for teaching and quick testing, the console is often easier. I explain why at the end of the article.

Some read-to-use examples

The following examples are more advanced, but all use the same basic idea: outline elements and place badges using simple DOM queries. You don’t need to understand or memorise every line — just paste them in and explore the results. Feel free to adapt or optimise them as needed.

To test these scripts on the same pages shown in the videos, try these:

1. Display all headings:

View the script
// ----------------------
// Scroll-Safe Overlay Container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

// ----------------------
// Colour map per heading level
// ----------------------
const colours = {
  H1: "red",
  H2: "green",
  H3: "blue",
  H4: "purple",
  H5: "deepPink",
  H6: "black"
};

// ----------------------
// Main Logic
// ----------------------
document.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(h => {
  
  const tag = h.tagName.toUpperCase();
  const colour = colours[tag] || "#147580"; // fallback just in case

  // Border
  h.style.outline = `3px solid ${colour}`;

  // Position
  const rect = h.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating badge
  const badge = document.createElement("div");
  badge.textContent = tag;
  badge.style.position = "absolute";
  badge.style.left = `${left - 20}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = colour;
  badge.style.color = "white";
  badge.style.padding = "5px 8px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
});

2. Display all buttons:

View the script
// ----------------------
// Scroll-safe overlay container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

// ----------------------
// Helper: Compute accessible name
// ----------------------
function getAccessibleName(el) {
  // aria-label
  const ariaLabel = el.getAttribute("aria-label");
  if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();

  // aria-labelledby
  const labelledby = el.getAttribute("aria-labelledby");
  if (labelledby) {
    const ids = labelledby.trim().split(/\s+/);
    const texts = ids.map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : "";
    }).filter(Boolean);
    if (texts.length) return texts.join(" ");
  }

  // value for input buttons
  if (el.tagName === "INPUT") {
    const value = el.getAttribute("value");
    if (value && value.trim()) return value.trim();
  }

  // visible text
  const txt = el.textContent.trim();
  if (txt) return txt;

  return "(no name)";
}

// ----------------------
// Button selector
// ----------------------
const buttonSelector = `
  button,
  [role="button"],
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
`;

const buttons = Array.from(document.querySelectorAll(buttonSelector));

// ----------------------
// Process each button
// ----------------------
buttons.forEach(btn => {
  const name = getAccessibleName(btn);

  // Add outline
  btn.style.outline = "3px solid #ca6510";

  // Compute location
  const rect = btn.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating badge
  const badge = document.createElement("div");
  badge.textContent = `Button: ${name}`;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 28}px`;
  badge.style.background = "#ca6510";
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.fontSize = "16px";
  badge.style.borderRadius = "5px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
});

3. Display image alternatives:

View the script
// ----------------------
// Scroll-Safe Overlay Container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);


// ----------------------
// Main Logic
// ----------------------
document.querySelectorAll("img").forEach(img => {

  // Determine alt text
  const alt = img.getAttribute("alt");
  const altText = (alt && alt.trim()) ? alt.trim() : "(missing alt)";

  // Add visible outline
  img.style.outline = "2px solid #b23a48";

  // Get absolute coordinates
  const rect = img.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Create floating badge
  const badge = document.createElement("div");
  badge.textContent = altText;
  badge.style.position = "absolute";
  badge.style.left = `${left -20}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = "#b23a48";
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";

  // Add to overlay layer
  overlay.appendChild(badge);
});

4. Display all links:

View the script
// ----------------------
// Accessible Name Helper
// ----------------------
function getLinkName(el) {

  // 1. aria-labelledby
  const labelledby = el.getAttribute("aria-labelledby");
  if (labelledby) {
    const parts = labelledby.split(/\s+/).map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : "";
    });
    const name = parts.join(" ").trim();
    if (name) return name;
  }

  // 2. aria-label
  const ariaLabel = el.getAttribute("aria-label");
  if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();

  // 3. Text content
  const text = el.textContent.trim();
  if (text) return text;

  // 4. Images inside links (alt text)
  const img = el.querySelector("img");
  if (img) {
    const alt = img.getAttribute("alt");
    if (alt && alt.trim()) return alt.trim();
  }

  return "(no accessible name)";
}


// ----------------------
// Scroll-Safe Overlay Container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);


// ----------------------
// Main Logic
// ----------------------
document.querySelectorAll("a").forEach(a => {

  const accName = getLinkName(a);

  // Add border to the link
  a.style.outline = "2px solid #6a1b9a";

  // Get page coordinates
  const rect = a.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Create floating label
  const badge = document.createElement("div");
  badge.textContent = accName;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 28}px`;
  badge.style.background = "#6a1b9a";
  badge.style.color = "white";
  badge.style.padding = "4px 6px";
  badge.style.borderRadius = "4px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";

  overlay.appendChild(badge);
});

5. Display all lists and list items:

View the script
// -------------------------------------------
// Scroll-safe overlay container
// -------------------------------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);


// -------------------------------------------
// Badge helper
// -------------------------------------------
function drawBadge(el, label, color) {
  // Outline element
  el.style.outline = `3px solid ${color}`;

  // Position calculation with scroll offsets
  const rect = el.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY - 28; // float above element

  // Create floating badge
  const badge = document.createElement("div");
  badge.textContent = label;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top}px`;
  badge.style.background = color;
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
}


// -------------------------------------------
// 1. Highlight UL / OL / DL
// -------------------------------------------
document.querySelectorAll("ul").forEach(el => {
  drawBadge(el, "UL", "#006d77");
});

document.querySelectorAll("ol").forEach(el => {
  drawBadge(el, "OL", "#005f8f");
});

document.querySelectorAll("dl").forEach(el => {
  drawBadge(el, "DL", "#4a148c");
});


// -------------------------------------------
// 2. Highlight LI / DT / DD
// -------------------------------------------
document.querySelectorAll("li").forEach(el => {
  drawBadge(el, "LI", "#8a5a44");
});

document.querySelectorAll("dt").forEach(el => {
  drawBadge(el, "DT", "#7f3d9c");
});

document.querySelectorAll("dd").forEach(el => {
  drawBadge(el, "DD", "#b83b5e");
});

6. Display table captions and table headers:

View the script
/* -----------------------------------------
   Shared overlay container (scroll-safe)
------------------------------------------ */
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

/* -----------------------------------------
   1. Highlight all TABLES
------------------------------------------ */
document.querySelectorAll("table").forEach(table => {
  // Add visible outline
  table.style.outline = "3px solid #d35400";

  // Get caption text (if present)
  const captionEl = table.querySelector("caption");
  const caption = captionEl && captionEl.textContent.trim()
    ? captionEl.textContent.trim()
    : "(no caption)";

  // Compute position
  const rect = table.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating label for the table
  const badge = document.createElement("div");
  badge.textContent = `Table: ${caption}`;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = "#d35400";
  badge.style.color = "white";
  badge.style.padding = "4px 6px";
  badge.style.borderRadius = "4px";
  badge.style.fontSize = "14px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";

  overlay.appendChild(badge);
});

/* -----------------------------------------
   2. Highlight all TH cells
------------------------------------------ */
document.querySelectorAll("th").forEach(th => {
  // Add outline
  th.style.outline = "2px dashed #8e44ad";

  // Compute position
  const rect = th.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating label
  const thBadge = document.createElement("div");
  thBadge.textContent = "TH";
  thBadge.style.position = "absolute";
  thBadge.style.left = `${left}px`;
  thBadge.style.top = `${top - 20}px`;
  thBadge.style.background = "#8e44ad";
  thBadge.style.color = "white";
  thBadge.style.padding = "2px 4px";
  thBadge.style.borderRadius = "3px";
  thBadge.style.fontSize = "16px";
  thBadge.style.whiteSpace = "nowrap";
  thBadge.style.pointerEvents = "none";

  overlay.appendChild(thBadge);
});

7. Display landmark roles and their names:

View the script
// ----------------------
// Scroll-safe overlay container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

// ----------------------
// Landmark roles + colours
// ----------------------
const landmarkRoles = [
  "banner",
  "navigation",
  "main",
  "contentinfo",
  "complementary",
  "search",
  "region",
];

const roleColors = {
  banner: "#0a6ebd",
  navigation: "#b85c00",
  main: "#006400",
  contentinfo: "#7a0099",
  complementary: "#990000",
  search: "#357a38",
  region: "#666600"
};

/* Native HTML elements that map to landmarks:
   header, nav, main, footer, aside, section (if named), article (if named)
*/
const selectors = [
  "header",
  "nav",
  "main",
  "footer",
  "aside",
  "section",
  "article",
  "[role]"
].join(",");

// ----------------------
// Helper: compute landmark role
// ----------------------
function getLandmarkRole(el) {
  const role = el.getAttribute("role");
  if (landmarkRoles.includes(role)) return role;

  const tag = el.tagName.toLowerCase();
  if (tag === "header") return "banner";
  if (tag === "nav") return "navigation";
  if (tag === "main") return "main";
  if (tag === "footer") return "contentinfo";
  if (tag === "aside") return "complementary";

  if ((tag === "section" || tag === "article") &&
      (el.hasAttribute("aria-label") || el.hasAttribute("aria-labelledby"))) {
    return "region";
  }

  return null;
}

// ----------------------
// Helper: compute accessible name
// ----------------------
function getAccessibleName(el) {
  const ariaLabel = el.getAttribute("aria-label");
  if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();

  const labelledby = el.getAttribute("aria-labelledby");
  if (labelledby) {
    const ids = labelledby.split(" ");
    const texts = ids.map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : "";
    }).filter(Boolean);
    if (texts.length) return texts.join(" ");
  }

  return "(no name)";
}

// ----------------------
// Main logic
// ----------------------
document.querySelectorAll(selectors).forEach(el => {
  const role = getLandmarkRole(el);
  if (!role) return;

  // Apply outline using role colour
  const color = roleColors[role] || "#0a6ebd";
  el.style.outline = `3px solid ${color}`;

  const rect = el.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  const accName = getAccessibleName(el);

  const badge = document.createElement("div");
  badge.textContent = accName !== "(no name)" ? `${role}: ${accName}` : role;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = color;
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
});

8. Display form labels:

View the script
function getAccName(el) {

  // 1. aria-labelledby
  const labelledby = el.getAttribute("aria-labelledby");
  if (labelledby) {
    const parts = labelledby.split(/\s+/).map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : "";
    });
    const name = parts.join(" ").trim();
    if (name) return name;
  }

  // 2. aria-label
  const ariaLabel = el.getAttribute("aria-label");
  if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();

  // 3. label[for]
  if (el.id) {
    const explicit = document.querySelector(`label[for="${el.id}"]`);
    if (explicit) {
      const name = explicit.textContent.trim();
      if (name) return name;
    }
  }

  // 4. nested label
  const nestedLabel = el.closest("label");
  if (nestedLabel) {
    const name = nestedLabel.textContent.trim();
    if (name) return name;
  }

  // 5. element text content
  const text = el.textContent.trim();
  if (text) return text;

  // 6. placeholder fallback
  const placeholder = el.getAttribute("placeholder");
  if (placeholder && placeholder.trim()) return placeholder.trim();

  return "(no accessible name)";
}


// -------------------------------------------
// SCROLL-SAFE OVERLAY (absolute, not fixed)
// -------------------------------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);


// -------------------------------------------
// Elements to inspect
// -------------------------------------------
const controls = document.querySelectorAll(
  'input, select, textarea, [role="textbox"], [role="combobox"], [role="spinbutton"]'
);


// -------------------------------------------
// Main rendering logic
// -------------------------------------------
controls.forEach(ctrl => {

  const accName = getAccName(ctrl);

  // Outline the control
  ctrl.style.outline = "2px solid #0a558c";

  // Compute absolute coordinates
  const rect = ctrl.getBoundingClientRect();
  const left = rect.left + window.scrollX - 20;
  const top = rect.top + window.scrollY - 30;

  // Create floating badge
  const badge = document.createElement("div");
  badge.textContent = accName;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top}px`;
  badge.style.background = "#0a558c";
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";

  overlay.appendChild(badge);
});

9. Display form field descriptions:

View the script
/* -----------------------------------------
   Shared overlay container (scroll-safe)
------------------------------------------ */
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

/* -----------------------------------------
   Find all elements with aria-describedby
------------------------------------------ */
document.querySelectorAll("[aria-describedby]").forEach(el => {

  const idList = el.getAttribute("aria-describedby")
                   .trim()
                   .split(/\s+/);

  // Compute description text from referenced elements
  const description = idList
    .map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : `(missing element #${id})`;
    })
    .filter(Boolean)
    .join(" ");

  // Add visible outline
  el.style.outline = "3px solid #0d6efd";

  // Compute absolute element position
  const rect = el.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Create floating badge
  const badge = document.createElement("div");
  badge.textContent = `Description: ${description}`;
  badge.style.position = "absolute";
  badge.style.left = `${left -10}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = "#0d6efd";
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";

  overlay.appendChild(badge);
});

10. Display fieldsets and captions:

View the script
// ----------------------
// Scroll-safe overlay container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);


// ----------------------
// Helpers
// ----------------------

// Accessible name helper (shared with radio-group)
function getAccessibleName(el) {
  // 1. aria-label
  const ariaLabel = el.getAttribute("aria-label");
  if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();

  // 2. aria-labelledby
  const labelledby = el.getAttribute("aria-labelledby");
  if (labelledby) {
    const ids = labelledby.split(" ");
    const texts = ids.map(id => {
      const ref = document.getElementById(id);
      return ref ? ref.textContent.trim() : "";
    }).filter(Boolean);
    if (texts.length) return texts.join(" ");
  }

  return "(no name)";
}


// ----------------------
// 1. FIELDSETS
// ----------------------
document.querySelectorAll("fieldset").forEach(fs => {
  
  // Compute accessible name from LEGEND
  const legend = fs.querySelector("legend");
  const name = legend && legend.textContent.trim()
    ? legend.textContent.trim()
    : "(no name)";

  // Outline fieldset
  fs.style.outline = "3px solid #1e8449";

  // Position of fieldset
  const rect = fs.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating badge
  const badge = document.createElement("div");
  badge.textContent = `Fieldset: ${name}`;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = "#1e8449";
  badge.style.color = "white";
  badge.style.padding = "4px 6px";
  badge.style.borderRadius = "4px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
});


// ----------------------
// 2. ARIA RADIO-GROUPS
// ----------------------
document.querySelectorAll('[role="radiogroup"]').forEach(rg => {

  const accName = getAccessibleName(rg);

  // Outline group
  rg.style.outline = "3px solid #7d3c98";

  // Position of the element
  const rect = rg.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Floating badge
  const badge = document.createElement("div");
  badge.textContent = `Radio-group: ${accName}`;
  badge.style.position = "absolute";
  badge.style.left = `${left}px`;
  badge.style.top = `${top - 30}px`;
  badge.style.background = "#7d3c98";
  badge.style.color = "white";
  badge.style.padding = "5px";
  badge.style.borderRadius = "5px";
  badge.style.fontSize = "16px";
  badge.style.whiteSpace = "nowrap";
  badge.style.pointerEvents = "none";
  badge.style.zIndex = "999999";

  overlay.appendChild(badge);
});

11. Display focusable elements:

This simple script logs each element as it receives keyboard focus. Press TAB through the page, and you’ll see every focusable element listed in the console:

View the script
document.addEventListener('focusin', () => {
  console.log('Focused element:', document.activeElement);
});

12. Display aria-owns:

View the script
// ----------------------
// Scroll-safe overlay container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

// Colour for aria-owns highlights
const ownsColor = "#c2185b"; // pink-ish

// ----------------------
// Main logic
// ----------------------
document.querySelectorAll("[aria-owns]").forEach(owner => {
  const ownsValue = owner.getAttribute("aria-owns");
  if (!ownsValue || !ownsValue.trim()) {
    return;
  }

  const ids = ownsValue.trim().split(/\s+/);

  // Outline the owner
  owner.style.outline = `3px solid ${ownsColor}`;

  // Position for the owner's badge
  const ownerRect = owner.getBoundingClientRect();
  const ownerLeft = ownerRect.left + window.scrollX;
  const ownerTop = ownerRect.top + window.scrollY;

  // Badge for the owner element
  const ownerBadge = document.createElement("div");
  ownerBadge.textContent = `aria-owns: ${ownsValue}`;
  ownerBadge.style.position = "absolute";
  ownerBadge.style.left = `${ownerLeft}px`;
  ownerBadge.style.top = `${ownerTop - 30}px`;
  ownerBadge.style.background = ownsColor;
  ownerBadge.style.color = "white";
  ownerBadge.style.padding = "5px";
  ownerBadge.style.borderRadius = "5px";
  ownerBadge.style.fontSize = "16px";
  ownerBadge.style.whiteSpace = "nowrap";
  ownerBadge.style.pointerEvents = "none";
  ownerBadge.style.zIndex = "999999";
  overlay.appendChild(ownerBadge);

  // Highlight each owned element
  ids.forEach(id => {
    const owned = document.getElementById(id);
    if (!owned) {
      // Optional: log missing references to the console
      console.warn(`aria-owns references missing element with id="${id}"`, owner);
      return;
    }

    // Outline the owned element (dashed to distinguish from owner)
    owned.style.outline = `3px dashed ${ownsColor}`;

    const rect = owned.getBoundingClientRect();
    const left = rect.left + window.scrollX;
    const top = rect.top + window.scrollY;

    const ownedBadge = document.createElement("div");
    ownedBadge.textContent = `owned: #${id}`;
    ownedBadge.style.position = "absolute";
    ownedBadge.style.left = `${left}px`;
    ownedBadge.style.top = `${top - 24}px`;
    ownedBadge.style.background = ownsColor;
    ownedBadge.style.color = "white";
    ownedBadge.style.padding = "3px 5px";
    ownedBadge.style.borderRadius = "5px";
    ownedBadge.style.fontSize = "14px";
    ownedBadge.style.whiteSpace = "nowrap";
    ownedBadge.style.pointerEvents = "none";
    ownedBadge.style.zIndex = "999999";
    overlay.appendChild(ownedBadge);
  });
});

13. Display aria-controls:

View the script
// ----------------------
// Scroll-safe overlay container
// ----------------------
const overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.pointerEvents = "none";
overlay.style.zIndex = "999999";
document.body.appendChild(overlay);

// Colour for aria-controls highlights
const controlsColor = "#00796b"; // teal

// ----------------------
// Main logic
// ----------------------
document.querySelectorAll("[aria-controls]").forEach(controller => {
  const controlsValue = controller.getAttribute("aria-controls");
  if (!controlsValue || !controlsValue.trim()) {
    return;
  }

  const ids = controlsValue.trim().split(/\s+/);

  // Outline the controller element
  controller.style.outline = `3px solid ${controlsColor}`;

  // Coordinates for controller badge
  const rect = controller.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  // Badge for the controller
  const controllerBadge = document.createElement("div");
  controllerBadge.textContent = `aria-controls: ${controlsValue}`;
  controllerBadge.style.position = "absolute";
  controllerBadge.style.left = `${left}px`;
  controllerBadge.style.top = `${top - 30}px`;
  controllerBadge.style.background = controlsColor;
  controllerBadge.style.color = "white";
  controllerBadge.style.padding = "5px";
  controllerBadge.style.borderRadius = "5px";
  controllerBadge.style.fontSize = "16px";
  controllerBadge.style.whiteSpace = "nowrap";
  controllerBadge.style.pointerEvents = "none";
  controllerBadge.style.zIndex = "999999";
  overlay.appendChild(controllerBadge);

  // Highlight each controlled element
  ids.forEach(id => {
    const controlled = document.getElementById(id);

    if (!controlled) {
      console.warn(`aria-controls references missing element with id="${id}"`, controller);
      return;
    }

    // Outline controlled element (dashed to distinguish)
    controlled.style.outline = `3px dashed ${controlsColor}`;

    const cRect = controlled.getBoundingClientRect();
    const cLeft = cRect.left + window.scrollX;
    const cTop = cRect.top + window.scrollY;

    const controlledBadge = document.createElement("div");
    controlledBadge.textContent = `controls: #${id}`;
    controlledBadge.style.position = "absolute";
    controlledBadge.style.left = `${cLeft}px`;
    controlledBadge.style.top = `${cTop - 24}px`;
    controlledBadge.style.background = controlsColor;
    controlledBadge.style.color = "white";
    controlledBadge.style.padding = "3px 5px";
    controlledBadge.style.borderRadius = "5px";
    controlledBadge.style.fontSize = "14px";
    controlledBadge.style.whiteSpace = "nowrap";
    controlledBadge.style.pointerEvents = "none";
    controlledBadge.style.zIndex = "999999";
    overlay.appendChild(controlledBadge);
  });
});

14. Display focus order:

View the script
// ----------------------
// Focus order tracker (number as you TAB)
// ----------------------
let focusStep = 0;

// Create one floating badge for the currently focused element
const focusBadge = document.createElement("div");
focusBadge.style.position = "absolute";
focusBadge.style.background = "#111";
focusBadge.style.color = "white";
focusBadge.style.padding = "5px 8px";
focusBadge.style.borderRadius = "6px";
focusBadge.style.fontSize = "16px";
focusBadge.style.whiteSpace = "nowrap";
focusBadge.style.pointerEvents = "none";
focusBadge.style.zIndex = "999999";
document.body.appendChild(focusBadge);

function getLabel(el) {
  // Small, readable label to help identify the element
  const tag = el.tagName.toLowerCase();
  const id = el.id ? `#${el.id}` : "";
  const role = el.getAttribute("role");
  const name =
    (el.getAttribute("aria-label") || "").trim() ||
    (el.textContent || "").trim().slice(0, 40);

  const roleText = role ? `[role="${role}"]` : "";
  const nameText = name ? ` "${name.replace(/\s+/g, " ")}"` : "";

  return `${tag}${id}${roleText}${nameText}`;
}

function placeBadge(el) {
  const rect = el.getBoundingClientRect();
  const left = rect.left + window.scrollX;
  const top = rect.top + window.scrollY;

  focusBadge.style.left = `${left}px`;
  focusBadge.style.top = `${top - 30}px`;
}

function onFocusIn(e) {
  const el = e.target;
  if (!el || el === document.body || el === document.documentElement) return;

  focusStep += 1;

  // Outline the focused element so it’s obvious
  el.style.outline = "3px solid #111";

  // Update badge content + position
  focusBadge.textContent = `${focusStep}. ${getLabel(el)}`;
  placeBadge(el);
}

// Keep the badge aligned if the page scrolls/resizes while focused
function onScrollOrResize() {
  const el = document.activeElement;
  if (!el || el === document.body || el === document.documentElement) return;
  placeBadge(el);
}

document.addEventListener("focusin", onFocusIn, true);
window.addEventListener("scroll", onScrollOrResize, true);
window.addEventListener("resize", onScrollOrResize, true);

console.log("Focus order tracker active. Press Tab to step through focusable elements.");

Why not convert these into bookmarklets?

You can convert any of these snippets into bookmarklets, but for teaching and quick testing, pasting JavaScript into the console is often faster and more flexible. Bookmarklets need to be encoded, saved, and managed manually in your browser. In contrast, running scripts in the console lets you:

Using the console also mirrors how real accessibility debugging works — quick checks, small iterations, and rapid feedback.

Conclusion

These simple scripts are designed to help you understand how accessibility features appear in the browser.

They’re not a replacement for manual testing or assistive technologies, but they can make patterns, errors, and relationships much easier to see while testing a page.