Как сделать пользовательскую таблицу в пользовательском элементе?
Я работаю над пользовательским элементом с lit-element. Я хочу сделать свой собственный элемент таблицы, но есть некоторые проблемы.
Первый. Казалось, браузер позволяет tr
, td
теги только для table
,
<!-- in html -->
<my-table>
<tr>
<td>A</td>
<td>B</td>
</tr>
</my-table>
<!-- chrome rendered -->
<my-table> A B </my-table>
Во- вторых. Это может имитировать с помощью CSS, но CSS не может реализовать colspan.
<my-table> <!-- display: table -->
<my-tr> <!-- display: table-row -->
<my-td>A</my-td> <!-- display: table-cell -->
<my-td colspan="3">B</my-td> <!-- There is no way to implement colspan -->
</my-tr>
</my-table>
В третьих. Я пытаюсь стилизовать с "display: content" на хосте. Казалось бы, работа, но хост не имеет данных для размеров коробки, как clientHeight
,
class MyTd extends LitElement {
render() {
return html`
<style>
:host { display: content; }
td {
all: inherit; /* It make to inherit host's style */
display: table-cell;
}
</style>
<td>
${html`<slot></slot>`} // I don't know why it work.
</td>
`;
}
}
// other js file.
const td = document.querySelector('my-td');
td.clientHeight; // 0
Я могу переопределить clientHeight
и что-нибудь еще (offsetHeight
, getBoundingClientRect
...) но я не знаю, что это правильно, и это единственный способ.
Есть ли другой способ создать пользовательскую таблицу или в моем мышлении что-то не так?
1 ответ
На самом деле это не лучший вариант использования настраиваемого элемента - вопрос в том, зачем мне использовать
<my-td>
когда есть и везде работает?
Вы можете использовать
column-count
в CSS для имитации таблиц, но у вас есть более серьезные проблемы - браузеры (и связанные с ними инструменты) видят DOM для
<table>
в виде структурированной таблицы данных, а ваш
<my-table>
это просто еще один настраиваемый элемент.
Это также нарушает иерархию DOM для таблицы - даже если ваши элементы обертывают связанные элементы таблицы, которые они не наследуют и не расширяют .
Итак, для вашего первого примера ваша DOM выглядит примерно так:
<my-table>
#shadow-root
<tr>
<td>A</td>
<td>B</td>
</tr>
</my-table>
Ничто, что браузер обычно не делает для подключения ячеек к строкам и строк к таблицам, не будет работать через эти теневые границы DOM, и
<tr>
отображается точно так, как если бы он находился в корне нового фрагмента документа.
Точно так же ваша DOM для третьей попытки выглядит примерно так:
<my-table>
#shadow-root
<table>
<my-tr>
#shadow-root
<tr>
<my-td>
#shadow-root
<td>
Каждый из этих теневых корней - это новый изолированный фрагмент.
Возникает вопрос: что вы хотите делать с этой структурой?
Вы хотите что - то , что выглядит как таблица , но использует пользовательские элементы для строк и ячеек , рассмотреть возможность использования
display: grid
макеты вместо этого. Он намного мощнее таблиц, и в настраиваемых элементах CSS гораздо лучше поддерживается, чем довольно непоследовательный
display: table-cell
.
Если вы хотите, чтобы что-то было структурировано как таблица, подумайте о том, чтобы поместить всю таблицу в теневой корень и передать данные как свойство
@customElement('my-table')
class MyTable
extends LitElement {
render() {
return html`
<table>
${this.items.map(i => html`
<tr>
<td>A</td>
<td>B</td>
</tr>`)}
</table>
`;
}
@property({attribute: false})
items: readonly RowRecordType[];
}
Затем вызовите это с префиксом свойства:
<my-table .items=${theListOfRows}></my-table>
Наконец, если вы хотите расширить
<td>
вы можете добавить к нему
is=
синтаксис, но это было немного тупиком в спецификации, не поддерживается вообще в Safari или Opera и нестабильно в остальном.
Вы можете расширить ячейку:
class MyCell
extends HTMLTableCellElement { ...
customElements.define('my-cell', MyCell, { extends: 'td' });
А потом сделайте:
<table>
<tr>
<td is="my-cell">A</td>
<td is="my-cell">B</td>
</tr>
</table>
Однако это не поддерживается LitElement, и вам лучше напрямую реализовать свой собственный веб-компонент.