JSFiddle - React, Tailwind, and code Playground
HTML
<table class="table">
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
CSS
.table {
table-layout: fixed;
width: 100%;
}
.table td {
border: 1px solid #000000;
height: 30px;
}
.table td:not(.is-edit):hover {
border-color: red;
cursor: pointer;
}
.table td input {
box-sizing: border-box;
height: 100%;
width: 100%;
}
JavaScript
const table = document.querySelector('.table');
const rows = table.querySelectorAll('tr');
const STORAGE_KEY = 'table-data';
const currentData = (() => {
const json = window.localStorage.getItem(STORAGE_KEY);
if (json) {
try {
const data = JSON.parse(json);
return data;
} catch (e) {}
}
return {};
})();
const saveData = () => {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(currentData));
};
const handleCellClick = (key, node) => {
if (node.classList.contains('is-edit')) {
return;
}
const input = document.createElement('input');
input.type = 'text';
input.value = currentData[key] || '';
input.onblur = () => {
currentData[key] = input.value;
saveData();
node.replaceChild(document.createTextNode(input.value), input);
node.classList.remove('is-edit');
};
if (node.firstChild) {
node.replaceChild(input, node.firstChild);
} else {
node.appendChild(input);
}
node.classList.add('is-edit');
};
rows.forEach((rowItem, rowIndex) => {
const cells = rowItem.querySelectorAll('td');
cells.forEach((cellItem, cellIndex) => {
const key = `${rowIndex}-${cellIndex}`;
cellItem.addEventListener('click', () => {
handleCellClick(key, cellItem);
});
cellItem.textContent = currentData[key] || '';
});
});