This example demonstrates prepend and append
behavior using reusable DOM node references.
Each post is a stateful DOM element.
Posts preserve local identity,
event listeners,
and runtime ownership while being reordered.
// =====================================
// ID
// =====================================
let nextId = 1;
// =====================================
// FEED DATA
// =====================================
let posts = [];
// =====================================
// FEED ROOT
// =====================================
const feed =
document
.createElement("div")
.setAttributes({
class: "feed"
});
// =====================================
// RENDER
// =====================================
function renderFeed() {
feed.setChildren(
...posts
);
}
// =====================================
// CREATE POST
// =====================================
function createPost(text) {
const id =
nextId++;
// ===================================
// ELEMENT REFERENCE
// ===================================
const post =
document
.createElement("div")
.setAttributes({
class: "post"
})
.setState({
text,
edits: 0
})
.setChildren(({
state,
setState
}) => [
// =============================
// HEADER
// =============================
document
.createElement("div")
.setAttributes({
class: "post-header"
})
.setChildren(
document
.createElement("strong")
.setText(
`Post #${id}`
),
document
.createElement("div")
.setAttributes({
class: "post-id"
})
.setText(
`Edits: ${state.edits}`
)
),
// =============================
// TEXT
// =============================
document
.createElement("div")
.setAttributes({
class: "post-text"
})
.setText(
state.text
),
// =============================
// ACTIONS
// =============================
document
.createElement("div")
.setAttributes({
class: "post-actions"
})
.setChildren(
// ===========================
// EDIT
// ===========================
document
.createElement("button")
.setText(
"Edit"
)
.setEvents({
click() {
const next =
prompt(
"Edit post",
state.text
);
if (
next === null
) {
return;
}
setState({
text: next,
edits:
state.edits + 1
});
}
}),
// ===========================
// DELETE
// ===========================
document
.createElement("button")
.setAttributes({
class: "secondary"
})
.setText(
"Delete"
)
.setEvents({
click() {
posts = posts.filter(
(p) => p !== post
);
renderFeed();
}
})
)
]);
return post;
}
// =====================================
// INPUT
// =====================================
const textarea =
document
.createElement("textarea")
.setAttributes({
placeholder:
"Write something..."
});
// =====================================
// CREATE CONTROLS
// =====================================
const controls =
document
.createElement("div")
.setAttributes({
class: "row"
})
.setChildren(
// ===============================
// PREPEND
// ===============================
document
.createElement("button")
.setText(
"Prepend Post"
)
.setEvents({
click() {
const text =
textarea.value.trim();
if (!text) {
return;
}
const post =
createPost(text);
posts.unshift(post);
renderFeed();
textarea.value = "";
}
}),
// ===============================
// APPEND
// ===============================
document
.createElement("button")
.setText(
"Append Post"
)
.setEvents({
click() {
const text =
textarea.value.trim();
if (!text) {
return;
}
const post =
createPost(text);
posts.push(post);
renderFeed();
textarea.value = "";
}
})
);
// =====================================
// APP
// =====================================
app.setChildren(
textarea,
controls,
feed
);