svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Svelte 5 : <tr> is invalid inside <table>

Open mquandalle opened this issue 2 years ago • 13 comments

Describe the bug

The following markup throws an exception

<table><tr></tr></table>
````	

### Reproduction

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE6tWSsvMSS1WsoquVspLzE1VslJyLChQ0lEqqSwAcYrLUnNKUoH84vzSomSQiE1JYlJOqp1NSZGdjT6EAAsA1eTmp2SmZaamKFmVFJWm1sbWAgBDd7JdXgAAAA==

### Logs

_No response_

### System Info

```shell
Svelte 5

Severity

annoyance

mquandalle avatar Dec 05 '23 19:12 mquandalle

The check is happening here.

https://github.com/sveltejs/svelte/blob/cb529fc666a3fb5fd35b27b91c61494a615a8106/packages/svelte/src/compiler/phases/2-analyze/validation.js#L231-L255

It says in the spec that tr is a valid tag, along with td and th. But a tbody should be inserted as a parent tag automatically.
image

So, is svelte trying to enforce the manual addition of tbody?

I can make the PR to allow tr, td and th if that's a valid case.

sidharthv96 avatar Dec 08 '23 06:12 sidharthv96

There is a problem here with hydration. If the SSR generates a table that looks different than the DOM that browser creates via the normalization (e.g. adding a missing tbody), the mismatch can cause the hydration to fail.

The issue was already known, would recommend waiting for maintainer feedback on this.

brunnerh avatar Dec 08 '23 12:12 brunnerh

I don't think Svelte should add the missing tbody here. That would be very confusing to have the framework adding tags not present in the source. Since browsers are accepting a <tr> directly inside a <table> svelte should leave the markup unchanged as does Svelte 4 and other UI frameworks.

mquandalle avatar Dec 08 '23 13:12 mquandalle

This is the result of the limitation of the new way of component rendering - creating all the elements in one go and extracting references to the elements. But when a browser corrects the template structure, it breaks the reference extraction.

Maybe the compiler can just do all the structure correction under the hood. However, it can lead to a confusing experience when a CSS rule doesn't apply to an element or apply to another element because the DOM structure in the source and in the browser differs. So it makes sense to write a markup that will not change its structure.

7nik avatar Jan 09 '24 21:01 7nik

On MDN <tbody> are omitted in some of the examples https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table#simple_table, so I think it is a reasonable way to write the markup for a table, and Svelte rejecting it as invalid is unexpected.

Although if this is an unavoidable technical limitation of the new rendering model that should be explicitly documented, especially in the migration guide.

mquandalle avatar Jan 12 '24 20:01 mquandalle

From @trueadm on an earlier discussion we had about this:

The browser always inserts a <tbody> when you don't put one in. We need to have a <tbody> then to avoid breaking our template mechanic in Svelte 5. There's not a super simple solution here. We either have to try and detect this at runtime or we try and do it statically between modules somehow, but both are pretty difficult. Solid, etc. have the same error. They push for the developer to fix their code and they class this as invalid, as does React.

One possible way would be when we append templates, we can see what the nodes are that we append and check if the parent we're appending to is a <table> and appropriately add a tbody element, and vice versa for when we traverse the elements. This will incur a small overhead cost for checking though, so maybe we can somehow get the compiler to give us a good estimate by looking at the template to see if we're dealing with <tr> elements.

benmccann avatar Jan 19 '24 22:01 benmccann

In my case, <tbody> is there, but <tr> are inside a {#if}{/if} section, the error is raised.

Reproduction

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE31R3UrFMAx-lVIFz0FhTu9mN_A5nBdbl2GxS-eaCofSd7fr_gTl3CXfX9LU815psLx48xybAXjBX8eRP3C6jHNjv0ETxN4aN8kZEVZOaqSqxpo0EJPGIbGS3VpqCE6P55fIRK53KEkZZArlBAMgnc7Mz0xNi-e-ZPksplCjyI5YFK0jik6DUiv5Wfo9IqSxCbUF8yknuRfH4qam1ZCEgj6g6aplqKBprRJREVhiuchi-Qd--h9-_gXHcsmbsW2KoNZ0l1Xhb1S_nSc-NRxxxyLbLeDLNdqyfNdku8gXoC1cdXcG765nZKoP67LrhrFKd4p_O5hO9Qo6XtDkILyHHzQhkBkWAgAA

R3D2 avatar May 03 '24 18:05 R3D2

@R3D2 you forgot about <td>/<th>. You cannot add text right to <tr>.

7nik avatar May 03 '24 19:05 7nik

Maybe the compiler can just do all the structure correction under the hood. However, it can lead to a confusing experience when a CSS rule doesn't apply to an element or apply to another element because the DOM structure in the source and in the browser differs. So it makes sense to write a markup that will not change its structure.

If the compiler were to also include display: contents; on the automatically inserted <tbody>, then that shouldn't be an issue.

apokaliptis avatar May 14 '24 10:05 apokaliptis

It still will be issue for cases like this:

/* style only own rows */
table.mytable > tr:even {
  background: #aaa;
}

And you will be puzzled why it doesn't work on the following markup:

<table class="mytable">
  <tr>
    <td>row 1</td>
  </tr>
  <tr>
    <td>row 2</td>
  </tr>
</table>

7nik avatar May 14 '24 11:05 7nik

Maybe the compiler can just do all the structure correction under the hood. However, it can lead to a confusing experience when a CSS rule doesn't apply to an element or apply to another element because the DOM structure in the source and in the browser differs. So it makes sense to write a markup that will not change its structure.

If the compiler were to also include display: contents; on the automatically inserted <tbody>, then that shouldn't be an issue.

That won't work, the browser will remove the <div> from the table and insert it outside regardless of the display property.

trueadm avatar May 14 '24 11:05 trueadm

It still will be issue for cases like this:

/* style only own rows */
table.mytable > tr:even {
  background: #aaa;
}

Oh, I see. I generally avoid > since I never know when I'm going to have to throw in a random <div> to resolve layout/position issue.

That won't work, the browser will remove the <div> from the table and insert it outside regardless of the display property.

I don't follow. What is the div you're referring to?

apokaliptis avatar May 14 '24 12:05 apokaliptis

Sorry I misread your message. I thought you said div with display contents. Unfortunately, the automatically inserted tbody is done via the browser when we clone the HTML template without it.

trueadm avatar May 14 '24 12:05 trueadm

I ran into this as well, but in my case I needed to put my th's inside a tr in the thead. Before, the tr wasn't needed there.

anders-hard avatar May 24 '24 07:05 anders-hard

I wonder why this issue is being kept open. The team can do nothing to prevent the error. So, the best they can do is display a more detailed error message, e.g. X is invalid inside Y. Y can contain only A, B, C, ... or include a link to the docs with a detailed explanation of the error.

7nik avatar May 24 '24 07:05 7nik

Right, it's a documentation issue so I'd assume it's being added to the breaking changes list :)

anders-hard avatar Jun 03 '24 17:06 anders-hard