tbody element not supported as a component root
ianroberts opened this issue · comments
I have a use case where I want to use reef.component
to populate the rows of a table. The table columns are not dynamic, so I tried putting this directly in the HTML:
<table id="my-table">
<thead>
<tr>
<th>Col 1</th>
<th>Col 2</th>
</thead>
<tbody></tbody>
</table>
and then binding a reef.component
to the tbody
:
const rows = reef.signal([]);
function template() {
return rows.map(r => `
<tr>
<td>${row.col1}</td>
<td>${row.col2}</td>
</tr>
`).join('');
}
reef.component('#my-table tbody', template);
However this does not work - the tr
and td
elements are not included, instead the content of all the td
elements is concatenated together and placed directly in the tbody
element (which the browser interprets as placing all of the content inside the first cell of the first row of the table).
Test case: https://codepen.io/ianroberts/pen/zYeYLyM
The underlying reason appears to be the use of DOMParser
, which expects a whole HTML document - if you give it a fragment it will do its best, synthesizing the html
, head
and body
elements and putting the elements of the fragment inside the body
(or head
for things like style
), but this only works if the top-level elements of the fragment are ones that would normally be allowed directly inside the body
or head
element. However tr
is only valid inside a table
or tbody
, and td
is only valid inside a tr
.
Possible fix
Before passing the templated string to DOMParser.parseFromString
, wrap it in a <template>
element:
function stringToHTML (str) {
// Create document
let parser = new DOMParser();
let doc = parser.parseFromString(`<html><body><template>${str}</template></body></html>`, 'text/html');
if(doc.body) {
// extract the document fragment from the parsed <template>
return doc.body.children[0].content;
} else {
return document.createElement('body');
}
}
A <template>
can contain any HTML elements, including things like <style>
that normally live in the head
, and elements like tr
that are normally only usable in specific contexts.
The current workaround would be to generate the entire table
using Reef, rather than just the tbody
dynamic section, but my overall HTML page is generated by a server-side template language (Grails GSP) and I would like to be able to use GSP tags in the non-Reef parts of the page.
Same behaviour confirmed in all of Chrome, Opera, Firefox and Safari (on Mac OS 13).
Well documented issue, and thanks for the PR as well!