HTML DOM Tree Viewer

Draws the structure of pasted markup and then measures it: nesting depth, the share of elements that are plain divs, heading order and missing alt attributes.

Enable JavaScript to customise; default output below.

A parser rather than a regular expression, so an unclosed element is closed at its parent and a "<" inside a script is not a tag.

Zero shows everything. Useful on a page where the interesting structure is in the first few levels.

Live preview tree.txt
└─ div.wrap
   ├─ header#top
   │  ├─ h1  "The title"
   │  └─ nav
   │     └─ ul
   │        ├─ li
   │        │  └─ a  "Home"
   │        └─ li
   │           └─ a  "About"
   └─ main
      ├─ section.hero
      │  ├─ h3  "A heading that skipped a level"
      │  ├─ p  "Some copy."
      │  ├─ img
      │  └─ img
      └─ div
         └─ div
            └─ div
               └─ span  "Nested for no reason"

Elements                          19
Deepest nesting                   6 levels
  reading                         shallow enough that the indentation still tells you where you are
Divs and spans                    5, 26.3% of all elements
  reading                         a reasonable share: most elements here say what they are

Most common tags
  div                             4
  li                              2
  a                               2
  img                             2
  header                          1
  h1                              1

Headings                          2
  first one                       h1, "The title"
  h1 count                        1
  levels skipped                  1, such as jumping to h3

Worth looking at
  images with no alt attribute    1, which is different from alt="" and is never correct
  elements with over six classes  0
  inline style attributes         1

19 elements, 6 levels deep, 26.3% of them divs or spans. Those three
numbers describe the shape of a page better than reading it does, and
the third is the one that predicts how it will behave for anybody
navigating by landmarks.

The nesting is shallow enough to follow. Past about a dozen levels the
indentation stops helping and starts hiding the structure it is meant to
show.

1 image here has no alt attribute at all. That is not the same as
alt="": an empty alt says the image is decorative, and a missing one
says nobody decided.

Heading levels are what a screen reader uses to skim a page, which is
why a skipped level matters more than it looks: it implies a section
that is not there.

1 element carries an inline style attribute. Each one outranks your
stylesheet, cannot be overridden without !important, and is invisible to
anybody looking in the CSS for why something looks wrong.

This is a parser rather than a regular expression, so an unclosed
element is closed at its parent and a "<" inside a script is not a tag.
What you see is what a browser would build, near enough to be useful.

A tree is a diagnosis, not a plan. The useful next step is usually
replacing a handful of the divs with the elements that say what they
are: header, nav, main, section, article, footer.

Output is valid and updates as you type.

Pasted markup is unreadable for one of two reasons. Either it is minified, which a formatter fixes, or it is nested so deeply that the indentation has stopped meaning anything, which nothing fixes by reformatting.

This is for the second case. It draws the structure, and then measures it: how deep the tree goes, what share of the elements are plain divs and spans, whether the headings are in order, and whether any image is missing an alt attribute rather than having an empty one.

Three numbers describe the shape of a page better than reading it does. Element count, depth, and the div share. The third is the one that predicts how the page behaves for somebody navigating by landmarks.

How to use

  1. Paste the markup. A fragment is fine; a whole page is fine.
  2. Read the tree, then the measurements underneath it.
  3. Set a depth limit if the interesting structure is in the first few levels.

Example

└─ div.wrap
   ├─ header#top
   │  ├─ h1  "The title"
   │  └─ nav
   │     └─ ul
   │        ├─ li
   │        │  └─ a  "Home"
   │        └─ li
   │           └─ a  "About"
   └─ main
      ├─ section.hero
      │  ├─ h3  "A heading that skipped a level"
      │  ├─ p  "Some copy."
      │  ├─ img
      │  └─ img
      └─ div
         └─ div
            └─ div
               └─ span  "Nested for no reason"
Elements                          26
Deepest nesting                   15 levels
  reading                         past 12 levels the indentation has stopped conveying structure
Divs and spans                    14, 53.8% of all elements
  reading                         more than half the elements carry no meaning of their own

Headings                          2
  first one                       h1, "The title"
  h1 count                        1
  levels skipped                  1, such as jumping to h3

Worth looking at
  images with no alt attribute    1, which is different from alt="" and is never correct
  elements with over six classes  0
  inline style attributes         1

The h1 followed by an h3 is the most common heading mistake there is, and it is invisible when reading the page: visually the h3 looks like the next thing down.

Pitfalls

A missing alt is not an empty alt. alt="" says the image is decorative, which is a decision. No alt at all says nobody decided, and a screen reader falls back to reading the filename.

One h1 per document. Everything else hangs below it. Several h1s flattens the outline that headings exist to provide.

A skipped level implies a section that is not there. Going from h1 to h3 tells anybody skimming by heading that they have missed something.

Depth costs more than readability. Deep trees make layout slower, selectors longer and CSS harder to reason about. Past about a dozen levels, something in the middle is almost always unnecessary.

Div share is a proxy, not a verdict. A page can be perfectly accessible with plenty of divs. Above half, though, there are usually landmarks missing: header, nav, main, footer.

Inline styles outrank your stylesheet. Each one needs !important to override and is invisible to anybody reading the CSS to find out why something looks wrong.

The tree is a diagnosis, not a plan. The useful next step is replacing a handful of divs with elements that say what they are.

Compatibility

Runs in the browser: nothing is uploaded and nothing is stored.

The parsing is the project’s own HTML parser rather than a regular expression, so void elements never take children, script and style contents are raw text, and an unclosed element is closed by its parent instead of swallowing the rest of the document. What the tree shows is close to what a browser would build.

The alt check uses a helper that asks whether the attribute is present at all, because the usual accessor returns an empty string for both a missing attribute and an empty one, and those mean different things. The test suite pins that distinction.

The heading checks read every heading in document order, which is the order a screen reader uses, rather than by position on the page. A level counts as skipped when it is more than one below the previous heading.

Depth is measured in element levels, not in characters of indentation, so reformatting the input does not change any of the numbers.

Frequently asked questions

Why does the element count differ from my editor’s?
Text nodes and comments are not elements. This counts elements only, which is what depth and the div share are about.
What counts as a hollow element?
div and span. Both are grouping elements with no meaning of their own, which is exactly their purpose and also why a page made mostly of them has no structure to navigate.
Is deep nesting always bad?
No. A table or a deeply structured menu legitimately nests. The number is a prompt to look, not a verdict.
How do I fix a div-heavy page?
Start at the top level: the outer wrappers are usually header, nav, main and footer already, in everything but name. Renaming those four is often most of the improvement.
Does it handle a whole page with scripts in it?
Yes. Script and style contents are treated as raw text, so a < inside JavaScript is not read as a tag.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.