Introduction
HTML has strict rules about which elements can be nested — leading to broken layouts, JavaScript errors, and accessibility issues. This guide covers the most common nesting mistakes and how to fix them.
The Form-in-Table Problem
The Rule
A <form> element cannot span multiple table rows. It must be completely contained within a single <td> element.
Why It Breaks
HTML parserng form submissions to fail.
Wrong: Form Spanning Table Rows
<!-- WRONG: form is inside <tr> but outside <td> -->
<table>
<tbody>
<tr>
<td>Product Name</td>
<td>¥32.00</td>
<!-- form starts here, inside <tr> but not inside <td> -->
<form action="/carts/169" method="post">
<td>
<input type="number" name="cart[amount]" value="7">
<input type="submit" value="Update">
</td>
</form>
<td><a href="/carts/169" data-method="delete">Delete</a></td>
</tr>
</tbody>
</table>
What happens: The browser moves the <form> outside the <table>, breaking the layout. Firefox shows the invalid HTML highlighted in red in the source view.
Correct: Form Completely Inside a <td>
<!-- CORRECT: form is completely inside a <td> -->
<table>
<tbody>
<tr>
<td>Product Name</td>
<td>¥32.00</td>
<td>
<!-- form is entirely within this <td> -->
<fo
<input type="number" name="cart[amount]" value="7" min="1" max="100">
<button type="submit" class="btn btn-primary btn-sm">Update</button>
</form>
</td>
<td>
<a href="/carts/169" data-method="delete" class="btn btn-danger btn-sm">Delete</a>
</td>
</tr>
</tbody>
</table>
Alternative: Use div Layout Instead of Table
For shopping carts and product lists, a flexbox or grid layout is often cleaner than a table:
<div class="product-row">
<span class="product-name">Product Name</span>
<span class="product-price">¥32.00</span>
ources
- [MDN: HTML Content Categories](https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories)
- [W3C HTML Validator](https://validator.w3.org/)
- [HTML Spec: The form element](https://html.spec.whatwg.org/multipage/form-elements.html#the-form-element)
- [Forms in Tables (Jukka Korpela)](https://www.cs.tut.fi/~jkorpela/forms/tables.html)
nsole shows warnings for some nesting errors.
### W3C Validator
Online validator
https://validator.w3.org/
CLI validator
npm install -g html-validate html-validate index.html
### In-Browser Check
// Check for HTML parsing errors in the browser const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, ’text/html’); const errors = doc.querySelectorAll(‘parsererror’); if (errors.length > 0) { console.error(‘HTML parsing errors:’, errors); }
## Rests, text | Other `<a>` elements, `<button>` |
| `<button>` | Inline elements, text | `<a>`, `<input>`, `<button>` |
| `<ul>`, `<ol>` | `<li>` elements | Direct text, other elements |
| `<table>` | `<thead>`, `<tbody>`, `<tfoot>`, `<caption>` | Direct `<tr>` or `<td>` |
| `<tr>` | `<td>`, `<th>` | Direct text, other elements |
| `<form>` | Block and inline elements | Another `<form>` |
## Validating Your HTML
### Browser DevTools
Firefox highlights invalid HTML in red in the source view. Chrome's DevTools co<table> -->
<table>
<td>Cell</td>
</table>
<!-- CORRECT: <td> must be inside <tr>, which must be inside <tbody>/<thead> -->
<table>
<tbody>
<tr>
<td>Cell</td>
</tr>
</tbody>
</table>
HTML Content Model Reference
Understanding content models helps avoid nesting mistakes:
| Element | Can Contain | Cannot Contain |
|---|---|---|
<p> |
Inline elements, text | Block elements (<div>, <p>, <ul>) |
<span> |
Inline elements, text | Block elements |
<a> |
Inline elemen button inside a link –> | |
### List Items Outside Lists
- Item 1
- Item 2
Table Structure Violations
<!-- WRONG: <td> directly inside lid</div>
</span>
<!-- CORRECT: use block inside block, or inline inside inline -->
<div>
<div>This is valid</div>
</div>
<span>
<em>This is valid</em>
</span>
Paragraphs Inside Paragraphs
<!-- WRONG: <p> cannot contain block elements -->
<p>
Some text
<div>A div inside a paragraph</div>
More text
</p>
<!-- CORRECT: use separate paragraphs or a div -->
<p>Some text</p>
<div>A div</div>
<p>More text</p>
Interactive Elements Inside Interactive Elements
<!-- WRONG: (button clicks have no effect), the cause is often invalid HTML nesting. Turbolinks is stricter about HTML validity than a regular page load.
**Symptoms:**
- Button click does nothing
- No network request in DevTools
- Works after disabling Turbolinks
**Fix:** Ensure the form is completely inside a `<td>`, not spanning `<tr>` boundaries.
## Other Common HTML Nesting Mistakes
### Block Elements Inside Inline Elements
```html
<!-- WRONG: <div> (block) inside <span> (inline) -->
<span>
<div>This is inva <form action="/carts/169" method="post" class="quantity-form">
<input type="number" name="cart[amount]" value="7" min="1" max="100">
<button type="submit">Update</button>
</form>
<a href="/carts/169" data-method="delete">Delete</a>
</div>
</div>
Turbolinks and Invalid HTML
If you’re using Rails with Turbolinks and a form inside a table isn’t working
Comments