Optimize notes rendering reducing DOM updates

This commit is contained in:
Adrian Gruntkowski 2024-12-06 12:59:15 +01:00
parent 8774683919
commit 8e8c37b1c5

View file

@ -113,48 +113,95 @@ editNote.addEventListener("deleteNote", async (e) => {
Notes.saveStorage(); Notes.saveStorage();
}); });
// All notes are currently re-rendered on each storage // The notes rendering routine is optimized to replace only
// update. The routine will be optimized to replace only // nodes that actually changed.
// nodes that actually changed - most likely based on unique
// note IDs associated with block elements. let currentNotes = {};
function notesEqual(note1, note2) {
return note1.id === note2.id && note1.updated === note2.updated;
}
async function render() { async function render() {
const notes = await Notes.all(); const notes = await Notes.all();
const notesContainer = document.querySelector("#notes"); const notesContainer = document.querySelector("#notes");
notesContainer.replaceChildren();
let previousId = null;
let notePrecedence = {};
const ids = [];
notes.forEach((n) => {
notePrecedence[n.id] = previousId;
ids.push(n.id);
previousId = n.id;
});
Object.keys(currentNotes)
.filter((id) => !ids.includes(id))
.forEach((id) => {
delete currentNotes[id];
document.getElementById(id).remove();
});
notes.forEach((note) => { notes.forEach((note) => {
const container = document.createElement("div"); const existingNote = currentNotes[note.id];
container.id = note.id;
container.classList.add("note");
container.classList.add("readonly");
if (note.type === "note") { if (!existingNote) {
container.replaceChildren(...renderText(note.content)); const noteElement = renderNote(note);
} else if (note.type === "tasklist") { const beforeId = notePrecedence[note.id];
const list = document.createElement("ul");
note.content.forEach((task) => { if (!beforeId) {
const item = document.createElement("li"); notesContainer.prepend(noteElement);
const check = document.createElement("p"); } else {
check.textContent = task.checked ? "☑" : "☐"; const before = document.getElementById(beforeId);
item.appendChild(check); if (before) {
const itemContent = document.createElement("p"); before.after(noteElement);
itemContent.replaceChildren(...renderText(task.content)); } else {
item.appendChild(itemContent); notesContainer.prepend(noteElement);
list.append(item); }
}); }
} else if (!notesEqual(existingNote, note)) {
container.appendChild(list); const noteElement = renderNote(note);
const existing = document.getElementById(note.id);
existing.replaceWith(noteElement);
} }
notesContainer.appendChild(container);
container.addEventListener("click", async (e) => {
newNote.classList.add("hidden");
editNote.classList.remove("hidden");
const note = await Notes.get(container.id);
editNote.load(note);
});
}); });
currentNotes = {};
notes.forEach((n) => (currentNotes[n.id] = n));
}
function renderNote(note) {
const container = document.createElement("div");
container.id = note.id;
container.classList.add("note");
container.classList.add("readonly");
if (note.type === "note") {
container.replaceChildren(...renderText(note.content));
} else if (note.type === "tasklist") {
const list = document.createElement("ul");
note.content.forEach((task) => {
const item = document.createElement("li");
const check = document.createElement("p");
check.textContent = task.checked ? "☑" : "☐";
item.appendChild(check);
const itemContent = document.createElement("p");
itemContent.replaceChildren(...renderText(task.content));
item.appendChild(itemContent);
list.append(item);
});
container.appendChild(list);
}
container.addEventListener("click", async (e) => {
newNote.classList.add("hidden");
editNote.classList.remove("hidden");
const note = await Notes.get(container.id);
editNote.load(note);
});
return container;
} }