Morph page updates instead of replacing them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
377f534f6b
commit
3df9f29b1d
2 changed files with 50 additions and 5 deletions
1
game.py
1
game.py
|
|
@ -6753,6 +6753,7 @@ ORDER_RECIPES, ORDER_RAW = _order_sources()
|
|||
# line here (new / balance / fix), and a new release a new entry on top.
|
||||
PATCH_NOTES = [
|
||||
{"date": "2026-10-28", "title": "Queue", "notes": [
|
||||
("fix", "The page no longer redraws itself when an action finishes or a poll lands: scroll, hovers and tooltips stay put."),
|
||||
("fix", "Crafting cards: buttons stay on one row on phones and narrow cards."),
|
||||
("new", "\"Out of:\" lists every missing reagent."),
|
||||
("new", "Updated guild roster layout"),
|
||||
|
|
|
|||
|
|
@ -680,14 +680,58 @@ function render() {
|
|||
animate();
|
||||
}
|
||||
|
||||
// Only touch the DOM when the markup changed. Replacing an element mid-swipe
|
||||
// cancels the swipe on iOS, and polls arrive every few seconds.
|
||||
// Only touch the DOM when the markup changed, and then only the nodes that did:
|
||||
// swapping the whole page flickers, drops hovers and scroll, and replacing an
|
||||
// element mid-swipe cancels the swipe on iOS. Polls arrive every few seconds.
|
||||
function setHtml(el, html) {
|
||||
if (el.dataset.html === html) return;
|
||||
const scroll = el.scrollTop;
|
||||
el.innerHTML = html;
|
||||
const next = document.createElement("template");
|
||||
next.innerHTML = html;
|
||||
morphChildren(el, next.content);
|
||||
el.dataset.html = html;
|
||||
el.scrollTop = scroll;
|
||||
}
|
||||
|
||||
// Patch `from`'s children into `to`'s, pairing them up by position.
|
||||
function morphChildren(from, to) {
|
||||
let old = from.firstChild, fresh = to.firstChild;
|
||||
while (fresh) {
|
||||
const after = fresh.nextSibling;
|
||||
if (!old) from.appendChild(fresh);
|
||||
else if (old.nodeType === fresh.nodeType && old.nodeName === fresh.nodeName) {
|
||||
morphNode(old, fresh);
|
||||
old = old.nextSibling;
|
||||
} else {
|
||||
const gone = old;
|
||||
old = old.nextSibling;
|
||||
from.replaceChild(fresh, gone);
|
||||
}
|
||||
fresh = after;
|
||||
}
|
||||
while (old) {
|
||||
const gone = old;
|
||||
old = old.nextSibling;
|
||||
gone.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function morphNode(old, fresh) {
|
||||
if (old.nodeType !== Node.ELEMENT_NODE) {
|
||||
if (old.nodeValue !== fresh.nodeValue) old.nodeValue = fresh.nodeValue;
|
||||
return;
|
||||
}
|
||||
// A fire's canvas sizes itself and holds its drawing: setting its size clears it.
|
||||
if (old.nodeName === "CANVAS" && old.dataset.flame === fresh.dataset.flame) return;
|
||||
for (const { name } of [...old.attributes]) if (!fresh.hasAttribute(name)) old.removeAttribute(name);
|
||||
for (const { name, value } of fresh.attributes) if (old.getAttribute(name) !== value) old.setAttribute(name, value);
|
||||
morphChildren(old, fresh);
|
||||
// What's typed or picked lives in properties, not attributes. Kept fields keep theirs (see render).
|
||||
if (old.dataset.keep === undefined) {
|
||||
if (old.nodeName === "INPUT") {
|
||||
old.checked = fresh.checked;
|
||||
if (old.value !== fresh.value) old.value = fresh.value;
|
||||
} else if (old.nodeName === "TEXTAREA" && old.value !== fresh.value) old.value = fresh.value;
|
||||
else if (old.nodeName === "SELECT") old.value = fresh.value;
|
||||
}
|
||||
}
|
||||
|
||||
function setMenu(open) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue