JavaScript can create new elements and put them on the page. That is how lists, cards and messages are built from data.
createElement
<div id="app"></div>
<script>
const p = document.createElement("p"); // created, but not on the page yet
p.textContent = "I was made by JavaScript";
p.className = "note";
document.querySelector("#app").append(p); // now it is on the page
</script>
Two steps: create the element, then insert it somewhere. A new element is not visible until you do.
A small component
<style>
.card { border: 1px solid #ddd; border-radius: 8px; padding: 10px; margin: 6px 0; max-width: 240px; }
.card h3 { margin: 0 0 4px; }
</style>
<div id="cards"></div>
<script>
function createCard({ title, text }) {
const card = document.createElement("article");
card.classList.add("card");
const heading = document.createElement("h3");
heading.textContent = title;
const body = document.createElement("p");
body.textContent = text;
card.append(heading, body);
return card;
}
const posts = [
{ title: "First post", text: "Hello there." },
{ title: "Second post", text: "More content." }
];
const container = document.querySelector("#cards");
for (const post of posts) {
container.append(createCard(post));
}
</script>
textContent keeps user data safe. Building with createElement takes more lines than a string template, but you never have to escape anything.
Setting attributes
const link = document.createElement("a");
link.href = "/about";
link.textContent = "About";
link.setAttribute("rel", "noopener");
The template element
For bigger pieces of markup, put an inert <template> in the HTML and clone it.
<template id="row">
<li><strong class="name"></strong> - <span class="role"></span></li>
</template>
<ul id="team"></ul>
<script>
const template = document.querySelector("#row");
const team = document.querySelector("#team");
for (const person of [{ name: "Maya", role: "Dev" }, { name: "Arjun", role: "Designer" }]) {
const row = template.content.cloneNode(true);
row.querySelector(".name").textContent = person.name;
row.querySelector(".role").textContent = person.role;
team.append(row);
}
</script>
The markup stays in HTML, where it belongs, and JavaScript only fills in the data.
Cloning
element.cloneNode(true) makes a copy, including children. cloneNode(false) copies only the element itself.