Skip to content

Instantly share code, notes, and snippets.

@akool
Created November 7, 2023 13:22
Show Gist options
  • Select an option

  • Save akool/a758f23716cf9fdc73ba5331c5c6a9f5 to your computer and use it in GitHub Desktop.

Select an option

Save akool/a758f23716cf9fdc73ba5331c5c6a9f5 to your computer and use it in GitHub Desktop.
Web component of a table with customizable number of cells
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<custom-table tabindex="0">
<style>
:host {
display: flex;
position: relative;
--table-padding: 1.6rem;
}
#table-container {
position: relative;
padding: var(--table-padding);
}
.custom-table__button {
width: calc(var(--table-padding) - 0.4rem);
height: calc(var(--table-padding) - 0.4rem);
box-sizing: border-box;
padding: 0;
line-height: 1;
}
#add-row-btn,
#remove-row-btn {
position: absolute;
bottom: calc(var(--table-padding) + 0.2rem);
}
#add-row-btn {
left: 0.2rem;
}
#remove-row-btn {
right: 0.2rem;
}
#add-column-btn,
#remove-column-btn {
position: absolute;
right: calc(var(--table-padding) + 0.2rem);
}
#add-column-btn {
top: 0.2rem;
}
#remove-column-btn {
bottom: 0.2rem;
}
td {
border: 1px solid lightgrey;
min-width: 1em;
min-height: 1em;
padding: 0.1em 0.2em;
}
td:empty::after {
content: '\0000a0'; /* non-breaking space */
visibility: hidden;
}
</style>
<div id="table-container">
<button class="custom-table__button" id="add-row-btn" type="button" title="Add Row">+</button>
<button class="custom-table__button" id="remove-row-btn" type="button" title="Remove Row">-</button>
<table>
<tbody id="table-body">
</tbody>
</table>
<button class="custom-table__button" id="add-column-btn" type="button" title="Add Column">+</button>
<button class="custom-table__button" id="remove-column-btn" type="button" title="Remove Column">-</button>
</div>
</custom-table>
<script>
class CustomTable extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = this.innerHTML;
this.tableBody = this.shadowRoot.getElementById('table-body');
this.addRowBtn = this.shadowRoot.getElementById('add-row-btn');
this.removeRowBtn = this.shadowRoot.getElementById('remove-row-btn');
this.addColumnBtn = this.shadowRoot.getElementById('add-column-btn');
this.removeColumnBtn = this.shadowRoot.getElementById('remove-column-btn');
this.addRowBtn.addEventListener('click', () => this.addRow());
this.removeRowBtn.addEventListener('click', () => this.removeRow());
this.addColumnBtn.addEventListener('click', () => this.addColumn());
this.removeColumnBtn.addEventListener('click', () => this.removeColumn());
this.addEventListener('focusin', () => this.setEditable(true));
this.addEventListener('focusout', () => this.setEditable(false));
// Initialize table with two rows and two columns
this.addRow();
this.addRow();
this.addColumn();
this.addColumn();
}
createCell() {
const cell = document.createElement('td');
return cell;
}
setEditable(editable) {
this.tableBody.querySelectorAll('td').forEach(cell => {
cell.contentEditable = editable ? 'true' : 'false';
});
}
addRow() {
const row = document.createElement('tr');
const columnCount = this.tableBody.querySelector('tr') ? this.tableBody.querySelector('tr').children.length : 0;
for (let i = 0; i < columnCount; i++) {
row.appendChild(this.createCell());
}
this.tableBody.appendChild(row);
}
removeRow() {
if (this.tableBody.children.length > 1) {
this.tableBody.removeChild(this.tableBody.lastChild);
}
}
addColumn() {
this.tableBody.querySelectorAll('tr').forEach(row => {
row.appendChild(this.createCell());
});
}
removeColumn() {
if (this.tableBody.querySelector('tr') && this.tableBody.querySelector('tr').children.length > 1) {
this.tableBody.querySelectorAll('tr').forEach(row => {
row.removeChild(row.lastChild);
});
}
}
}
customElements.define('custom-table', CustomTable);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment