Common nesting issues in HTML

by Silvestar published on

HTML is such a lovely language. Browsers will almost always display something for you, no matter what you put in the HTML document. Heck, you could omit all tags, and it will still work.

That’s all nice, but as web professionals, we should aim to write valid code. But even professional web developers make mistakes. Here are some examples of common nesting issues in HTML.

Anchors and buttons

Let’s start with anchors and buttons. It is common to see buttons inside anchors out in the web wilderness.

<a href="#">
<button>Button</button>
</a>

According to the specification, the anchor element must not contain any other interactive element, so <button> elements cannot live inside the <a> element.

Permitted content
Transparent, except that no descendant may be interactive content or an a element, and no descendant may have a specified tabindex attribute.

- the a element on MDN

If we validate the code against the HTML validator, we can see that it's invalid.

Error message in the HTML validator: The element button must not appear as a descedant of the a element.

In the example below, when you click on a button, you will get two alerts, which is far from ideal.

See the Pen Nesting issue: <a> and <button> by Silvestar Bistrović (@CiTA) on CodePen.

You should avoid nesting interactive elements inside each other. It might affect user expectations and the functionality of your website negatively. If you need a link that looks like a button, use the a element and style it like a button. This also applies to links nested in buttons.

Lists

Let's have a look at other common HTML elements, <ul> and <ol>. Their direct children shouldn’t be any other element than <li>, <script>, or <template>.

<ul>
<li>List item 1</li>
<div>Div item</div>
<li>List item 2</li>
<span>Div item</span>
<div>Div item</div>
</ul>

Using div or similar elements as direct children of lists is wrong. The final output is messed up, and users might have issues distinguishing list items from non-list items, as seen in the example below. <span> items are tough to identify, as they are rendered inline with list items.

See the Pen Nesting issue: <list> by Silvestar Bistrović (@CiTA) on CodePen.

Instead, only use allowed elements inside list elements.

Permitted content
Zero or more <li>, <script> and <template> elements.

the ul element on MDN

Note that <dl>, the description list element, accepts <div> elements.

<dl>
<div>
<dt>HTML</dt>
<dd>HTML is a…</dd>
</div>
<div>
<dt>CSS</dt>
<dd>CSS is a…</dd>
</div>
</dl>

Same element type nesting

Some HTML elements mustn’t be nested in their element type. For example, the <main> element shouldn’t be nested inside another <main> element.

<main>
...
<main>
...
</main>
</main>

According to the spec, there should only be one visible <main> element. That makes sense because there are no multiple main contents in a page. We want a single main element that holds the main content.
If you don’t respect this rule, nothing will be broken visually, but this could affect SEO and the accessibility of a page.

A document mustn't have more than one <main> element that doesn't have the hidden attribute specified.

the main element on MDN

The <header> and <footer> elements also shouldn't be nested inside themselves.

<header>
...
<header>
...
</header>
</header>

The difference between these two and the <main> element is that you can have multiple <header> and <footer> elements in a document, if you’re using them inside <main>, <section>, <article>, <aside>, or <nav> elements.

When used outside of the these elements, they are considered as landmarks. In short, landmarks are organized areas of your page which help keyboard and screen reader users understand and navigate the page more easily.

Many other elements, like anchors and form elements, don’t allow nesting inside themselves either. However, unlike header, footer, and main, nesting these elements could break their functionality and styling.

Fieldset/Legend

The <legend> element is used to label its parent <fieldset>. It should be the first child of the <fieldset> element and it must not be nested inside another element.

<fieldset>
<div>
<legend>Legend</legend>
</div>

<input type="radio" id="l" name="shirt1">
<label for="l">Large</label>

<input type="radio" id="m" name="shirt1">
<label for="m">Medium</label>
</fieldset>

The above code is wrong because since the legend is wrapped in a div, the fieldset has no accessible name. This may cause a group of radio buttons not being announced as a group and the legend might not have a sematic relation with the radio buttons. The visual appearance breaks as well. The legend isn’t positioned on the border of the fieldset, but below it.

See the Pen Nesting issue: <legend> by Silvestar Bistrović (@CiTA) on CodePen.

Permitted parents
A <fieldset> whose first child is this <legend> element.

- the legend element on MDN

Even if the legend is not the first child of the fieldset, visually it will be rendered as the first element within its parent. This can be confusing for screen reader users because the visual order doesn't match DOM order.

See the Pen Nesting issue: <legend> by Silvestar Bistrović (@CiTA) on CodePen.

Conclusion

Let’s face it: an invalid HTML structure will not break your site in most cases, at least visually. Some might say that’s the beauty of HTML, but others say it is a curse.
Nevertheless, a weak HTML structure might make your site less usable and affect your SEO score. Not only that, assistive technologies might not announce your content correctly. And finally, your code might be rendered incorrectly if browsers decide to penalise invalid HTML in the future (less likely, though). So try to avoid HTML errors at all costs.

There are all sorts of tools that you could use to help you avoid common issues, from online validator tools like Nu HTML Validator to text editor extensions like HTMLHint. If you're unsure about nesting one tag inside another, you could always go to Caninclude for confirmation.

About Silvestar

Silvestar is a fearless web developer and he enjoys creating pixel-perfect, responsive, and modern websites. His focus is on delivering the best experience for users on every device and he specializes in making faster, lighter, and more secure sites.

Website: silvestar.codes