Skip to content

DOM & HTML basics

Every page Playwright tests is built from HTML, and the browser turns that HTML into a live tree called the DOM. Locators, roles, and accessibility checks all operate on that tree — so a clear picture of it makes the rest of the course click. No prior front-end experience needed.

An HTML element is written with a tag. Most come in pairs — an opening tag and a closing tag — with content in between:

<button type="submit" id="login-btn">Log In</button>

That single element has three kinds of information:

  • Tag namebutton. It says what kind of element this is.
  • Attributestype="submit" and id="login-btn". Name/value pairs that configure the element.
  • Text contentLog In. The human-readable text inside it.

This is the exact Log In button from TestMarket Lab, and every locator strategy reads off one of those three parts: getByRole('button', { name: 'Log In' }) uses the tag + text; page.locator('#login-btn') uses an attribute.

When the browser loads HTML, it parses it into the DOM (Document Object Model) — a tree where every element is a node, nested inside its parent:

<form class="login-form">
<label for="email">Email</label>
<input id="email" name="email">
<button type="submit">Log In</button>
</form>

Here the <form> is the parent; the <label>, <input>, and <button> are its children (and siblings of each other). “The DOM” just means this live, in-memory tree — and it can change after load as JavaScript adds or removes nodes, which is why Playwright re-queries the tree on every action instead of trusting a stale snapshot.

This parent/child structure is exactly what chained locators and CSS combinators navigate:

// "the button inside the login form" — parent → child
page.locator('.login-form').getByRole('button', { name: 'Log In' })

Two attributes show up everywhere, and the difference matters for testing:

idclass
Uniquenessone per pageshared by many
HTML<input id="email"><div class="product-card">
CSS selector#email.product-card
As a locatorprecise — matches onematches every card

Because an id is unique, #email is a precise, stable hook. Because a class is shared, .product-card matches every product on the shop page — useful for “all products,” but a strict-mode trap when you mean just one (see Module 2).

HTML elements carry meaning, not just appearance. A <button> is a button to the browser and to a screen reader; a <nav> is a navigation region. Choosing the meaningful (“semantic”) tag is what makes a page accessible — and testable by role.

Semantic elementImplicit roleWhat it means
<button>buttona clickable action
<a href>linknavigation to a URL
<nav>navigationa set of nav links
<h1><h6>headinga section title
<input type="text">textboxa text field
<input type="checkbox">checkboxa toggle
<main>, <header>, <footer>main / banner / contentinfopage landmarks

Each element has an implicit ARIA role — the category assistive technology announces it as. This is the bridge to two course skills:

  • getByRole (Module 2) finds elements by that role and their accessible name: getByRole('button', { name: 'Log In' }).
  • Accessibility testing (Module 11) uses tools like axe to flag elements that lack a proper role or accessible name — e.g. a clickable <div> that should have been a <button>, or an <img> with no alt.

The accessible name is the label a screen reader reads for an element — usually its visible text (<button>Log In</button>), or an attribute when there’s no text (<img alt="Company logo">, <button aria-label="Close">×</button>). getByRole('…', { name }) matches on exactly that.

Semantic HTML and testability are the same goal. When the app uses a real <button> with clear text:

  • a screen-reader user hears “Log In, button,”
  • getByRole('button', { name: 'Log In' }) finds it,
  • and an axe audit passes.

When it uses a styled <div> with no role, all three break. That’s why the course prefers role-based locators: a test that’s hard to write by role is often a sign the markup has an accessibility problem worth fixing.

<tag attr="value">text</tag> element = tag + attributes + text
tag name what kind of element it is
attribute name="value" pair that configures it
text content the readable text inside
DOM the live tree the browser builds from HTML
parent/child nesting — what chained locators navigate
id unique per page → #id, precise locator
class shared by many → .class, matches many
role what an element IS (button, link, heading)
accessible name the label a screen reader reads