Learn how to display and update content on a web page using
innerHTML, textContent, and dynamic content
techniques — the core of every interactive user interface.
Output to HTML refers to the process of updating or displaying
content inside HTML elements using JavaScript, primarily through properties like
innerHTML and textContent.
These properties let you change what a user sees on the page without reloading it — updating a heading after a button click, showing a result after a calculation, or building an entire list dynamically from data.
JavaScript can change what is displayed on the page — choose the right tool based on whether you need formatting or just plain text:
<b>, <ul>, <img> are rendered
textContent but respects CSS visibility — hidden text is excluded
input.value, textarea.value
<p id="box1">Original text</p>
<script>
let el = document.getElementById("box1");
el.innerHTML = "<b>Hello <em>Aman</em>!</b>";
// The <b> and <em> tags ARE rendered — text appears bold + italic
</script>
innerHTML replaces the element's content with a new HTML string<p id="box2">Original text</p>
<script>
let el = document.getElementById("box2");
el.textContent = "<b>Hello Aman</b>";
// The tags are NOT rendered — they appear as literal text: <b>Hello Aman</b>
</script>
textContent treats the value as raw text — HTML tags are escaped and displayed literallyinnerHTML because no HTML parsing is required<ul id="list"></ul>
<script>
let fruits = ["Apple", "Banana", "Mango"];
let html = "";
fruits.forEach(function(fruit) {
html += "<li>" + fruit + "</li>";
});
document.getElementById("list").innerHTML = html;
</script>
innerHTMLinnerHTML += inside the loop<p id="msg">Hello World</p>
<input id="nameIn" type="text" value="Aman">
<script>
// Reading existing content
let para = document.getElementById("msg");
console.log(para.textContent); // "Hello World"
console.log(para.innerHTML); // "Hello World" (same if no inner tags)
// Reading an input's value
let input = document.getElementById("nameIn");
console.log(input.value); // "Aman"
// Writing — update the paragraph dynamically
para.innerHTML = "<strong>Updated: " + input.value + "</strong>";
</script>
textContent and innerHTML can both read existing content — not just write itinput.value reads (or sets) what's currently in an input field — not the HTML attribute, the live valueThink of a notice board at a school:
Click ▶ Preview and try all three buttons — each demonstrates a different way to update content: plain text, HTML, and a dynamically generated list.
<!DOCTYPE html>
<html>
<head><title>Output to HTML</title></head>
<body style="font-family:sans-serif;padding:24px;background:#f8f9fa">
<h2 id="title">Original Title</h2>
<p id="content">Original content paragraph.</p>
<ul id="list"></ul>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:12px">
<button onclick="changeText()"
style="padding:8px 14px;background:#f7df1e;border:none;border-radius:6px;
cursor:pointer;font-weight:700">
Change Text
</button>
<button onclick="changeHTML()"
style="padding:8px 14px;background:#82aaff;border:none;border-radius:6px;
cursor:pointer;font-weight:700;color:#111">
Inject HTML
</button>
<button onclick="buildList()"
style="padding:8px 14px;background:#4ade80;border:none;border-radius:6px;
cursor:pointer;font-weight:700;color:#111">
Build List
</button>
<button onclick="resetAll()"
style="padding:8px 14px;background:#f87171;border:none;border-radius:6px;
cursor:pointer;font-weight:700;color:#fff">
Reset
</button>
</div>
<script>
function changeText() {
// textContent — safe, no HTML rendering
document.getElementById("title").textContent = "Updated with textContent";
document.getElementById("content").textContent =
"<b>This tag is NOT rendered</b> — shown as plain text.";
}
function changeHTML() {
// innerHTML — HTML is parsed and rendered
document.getElementById("title").innerHTML =
"Updated with <span style='color:#e07b00'>innerHTML</span>";
document.getElementById("content").innerHTML =
"<b>This tag IS rendered</b> — text is bold!";
}
function buildList() {
let items = ["JavaScript", "HTML", "CSS", "DOM"];
let html = "";
items.forEach(item => html += "<li>" + item + "</li>");
document.getElementById("list").innerHTML = html;
}
function resetAll() {
document.getElementById("title").textContent = "Original Title";
document.getElementById("content").textContent = "Original content paragraph.";
document.getElementById("list").innerHTML = "";
}
</script>
</body>
</html>
<p id="para">Click the button to change me.</p>
<button onclick="updatePara()">Update</button>
<script>
function updatePara() {
// use textContent to change the paragraph text
}
</script>
textContent to change the paragraph to "Text updated successfully!"innerHTML with a <b> tag — observe the differencetextContent = ""<div id="output"></div>
<button onclick="showWelcome()">Show Welcome</button>
<script>
function showWelcome() {
// Use innerHTML to inject bold welcome text
}
</script>
innerHTML to display "<b>Welcome Aman</b>" — confirm the bold rendersinnerHTML assignmenttextContent = ""<p id="t"></p>
<script>
document.getElementById("t").innerHTML = "<b>Bold!</b>";
</script>
innerHTML parses the assigned string as HTML, so <b>Bold!</b> renders as bold text. textContent would show the literal string <b>Bold!</b> as plain characters.
<p id="u"></p>
<script>
document.getElementById("u").textContent = "<b>Bold?</b>";
</script>
textContent treats the value as raw text and escapes any HTML characters. The output is the literal string <b>Bold?</b> — tags visible, nothing bold.
<div id="v"></div>
<script>
document.getElementById("v").textContent = "<h1>Hello</h1>";
</script>
textContent escapes angle brackets, so < becomes < and > becomes > in the DOM. The browser shows the raw tag text — not a rendered heading.
Never use innerHTML with user-provided input — it opens an XSS (Cross-Site Scripting) vulnerability. Always use textContent for user data:
<input id="userInput" type="text" value="<img onerror=alert(1) src=x>">
<p id="safe"></p>
<p id="unsafe"></p>
<script>
let data = document.getElementById("userInput").value;
// ✓ Safe — tag is treated as text, no script runs
document.getElementById("safe").textContent = data;
// ✗ Risky — if data contains a script tag, it executes
document.getElementById("unsafe").innerHTML = data;
</script>
Build the entire HTML string in a variable first, then assign it once — this is far more efficient than appending to innerHTML inside a loop:
<ul id="items"></ul>
<script>
let names = ["Alice", "Bob", "Carol", "Dave"];
// ✗ Slow — reads and rewrites innerHTML on every iteration
// names.forEach(n => document.getElementById("items").innerHTML += "<li>" + n + "</li>");
// ✓ Fast — builds string first, one DOM write at the end
let html = names.map(n => "<li>" + n + "</li>").join("");
document.getElementById("items").innerHTML = html;
</script>
To clear an element's content, set textContent = "" — it is faster and safer than innerHTML = "" because it removes all nodes without HTML parsing:
let el = document.getElementById("output");
// ✓ Preferred — no HTML parsing overhead
el.textContent = "";
// Also works but slightly slower:
el.innerHTML = "";
Use template literals for clean multi-part HTML strings — they are far more readable than concatenating with +:
let name = "Aman";
let score = 95;
let grade = "A+";
// Old way — messy
let html1 = "<h3>" + name + "</h3><p>Score: " + score + " | Grade: " + grade + "</p>";
// Modern way — clean and readable
let html2 = `<h3>${name}</h3><p>Score: ${score} | Grade: ${grade}</p>`;
console.log(html2);
One uses innerHTML, one uses textContent — predict
exactly what the user sees for each, then check in the preview.
<p id="a"></p>
<p id="b"></p>
<script>
document.getElementById("a").innerHTML = "<b>Hello</b>";
document.getElementById("b").textContent = "<b>Hello</b>";
</script>
// Both elements receive the same string: "<b>Hello</b>"
// Element #a — innerHTML
// The browser PARSES the string as HTML
// <b> is a valid tag → it is rendered
// User sees: Hello ← bold text
// Element #b — textContent
// The browser treats the string as RAW TEXT
// <b> and </b> are escaped to <b> and </b>
// User sees: <b>Hello</b> ← literal characters, not bold
// Final visual output:
// #a: Hello ← BOLD (HTML rendered)
// #b: <b>Hello</b> ← PLAIN TEXT (tags shown as characters)
// Key rule to remember:
// innerHTML → interpret as HTML markup
// textContent → interpret as safe plain text