β createElement() - Create a new element
πΉ What is document.createElement()?
document.createElement() is used to create a new HTML element using JavaScript and then attach it to the DOM.
let element = document.createElement('tagName');It returns a new HTML element (Node).
The element is created in memory and not visible until added to the DOM.
1οΈβ£ Create and Add a <div>
<!DOCTYPE html>
<html>
<body>
<script>
const div = document.createElement('div');
div.innerHTML = '<p>CreateElement example</p>';
document.body.appendChild(div);
</script>
</body>
</html><p>CreateElement example</p>2οΈβ£ Add id and class to an Element
const div = document.createElement('div');
div.id = 'content';
div.className = 'note';
div.innerHTML = '<p>CreateElement example</p>';
document.body.appendChild(div);<div id="content" class="note">
<p>CreateElement example</p>
</div>3οΈβ£ Add Text to a <div>
const div = document.createElement('div');
const text = document.createTextNode('CreateElement example');
div.appendChild(text);
document.body.appendChild(div);CreateElement example4οΈβ£ Add an Element Inside Another Element
const div = document.createElement('div');
const h2 = document.createElement('h2');
h2.textContent = 'Heading inside div';
div.appendChild(h2);
document.body.appendChild(div);<h2>Heading inside div</h2>5οΈβ£ Create List Items (<li>)
βοΈ HTML
<ul id="menu">
<li>Home</li>
</ul>π§ JavaScript
const menu = document.querySelector('#menu');
let li = document.createElement('li');
li.textContent = 'Products';
menu.appendChild(li);
li = document.createElement('li');
li.textContent = 'About Us';
menu.appendChild(li);<ul id="menu">
<li>Home</li>
<li>Products</li>
<li>About Us</li>
</ul>6οΈβ£ Create a <script> Dynamically
const script = document.createElement('script');
script.src = '/lib.js';
document.body.appendChild(script);π Async Script
function loadJSAsync(url) {
const script = document.createElement('script');
script.src = url;
script.async = true;
document.body.appendChild(script);
}
loadJSAsync('/lib.js');<div class="infographic-createel">
<style>
.infographic-createel {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-createel[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
}
.infographic-createel * { box-sizing: border-box; }
.infographic-createel h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-createel p { margin: 0; }
.infographic-createel button { font-family: inherit; }
.ce-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.ce-hero { text-align: center; margin-bottom: 1.2rem; }
.ce-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.ce-hero p { color: var(--ink-soft); margin-top: 0.4rem; font-size: 0.84rem; }
.ce-tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: center; margin-bottom: 1.2rem; }
.ce-tab { border: 1px solid var(--line); background: var(--paper-alt); color: var(--ink-soft); padding: 0.34rem 0.55rem; font-size: 0.62rem; font-weight: 700; cursor: pointer; font-family: 'IBM Plex Mono', monospace; }
.ce-tab.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.ce-tab:hover:not(.is-active) { border-color: var(--ink-soft); color: var(--ink); }
.ce-panel { display: none; }
.ce-panel.is-active { display: block; }
.ce-code { background: #1e1e1e; color: #d4d4d4; border-radius: 5px; padding: 0.6rem 0.8rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.66rem; line-height: 1.6; margin-bottom: 1rem; white-space: pre; overflow-x: auto; }
.tok-kw { color: #569cd6; }
.tok-fn { color: #dcdcaa; }
.tok-str { color: #ce9178; }
.tok-var { color: #9cdcfe; }
.tok-punc { color: #d4d4d4; }
/* memory staging */
.ce-memory { width: min(260px, 100%); margin: 0 auto; text-align: center; border: 1.5px dashed var(--line); border-radius: 6px; padding: 0.55rem 0.7rem; min-height: 2.6em; display: flex; align-items: center; justify-content: center; opacity: 0.35; transition: opacity 0.35s ease, border-color 0.35s ease; }
.ce-memory-label { position: absolute; }
.ce-memory.is-shown { opacity: 1; border-color: var(--ink-soft); }
.ce-memory-content { font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; color: var(--ink-soft); }
.ce-memory-tag { display: block; font-size: 0.56rem; color: var(--ink-soft); opacity: 0.7; margin-top: -1.3rem; margin-bottom: 0.6rem; }
.ce-arrow-row { text-align: center; margin: 0.5rem 0; font-family: 'IBM Plex Mono', monospace; font-size: 0.6rem; color: var(--ink-soft); opacity: 0.4; transition: opacity 0.3s ease; }
.ce-arrow-row.is-lit { opacity: 1; color: var(--accent); }
/* mini rendered page */
.ce-page { border: 1.5px solid var(--line); background: var(--paper-alt); border-radius: 6px; overflow: hidden; margin-top: 0.5rem; }
.ce-page-bar { background: #2b2b2b; padding: 0.4rem 0.6rem; display: flex; gap: 0.3rem; }
.ce-page-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; }
.ce-page-body { padding: 0.9rem 1rem; background: var(--paper); min-height: 3em; }
.ce-result-el { border: 1.5px solid var(--line); border-radius: 4px; padding: 0.4rem 0.6rem; font-size: 0.8rem; opacity: 0; transform: translateY(-8px) scale(0.95); transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease; }
.ce-result-el.is-shown { opacity: 1; transform: translateY(0) scale(1); border-color: var(--good); background: var(--good-soft); }
.ce-result-el .ce-restag { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 0.58rem; color: var(--ink-soft); margin-bottom: 0.15rem; }
.ce-result-el .ce-innerbox { border: 1.5px dashed var(--good); border-radius: 4px; padding: 0.25rem 0.4rem; margin-top: 0.3rem; font-size: 0.76rem; }
.ce-static-el { border: 1.5px dashed var(--line); border-radius: 4px; padding: 0.3rem 0.55rem; font-size: 0.78rem; margin-bottom: 0.35rem; color: var(--ink); }
.ce-static-el .ce-restag { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 0.58rem; color: var(--ink-soft); margin-bottom: 0.1rem; }
.ce-play { display: block; margin: 1rem auto 0; border: none; background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; }
.ce-play:active { transform: scale(0.96); }
.ce-play:disabled { opacity: 0.45; cursor: default; }
.ce-note { text-align: center; font-size: 0.78rem; color: var(--ink-soft); padding-top: 1.3rem; }
.ce-note strong { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
.infographic-createel *, .infographic-createel *::before, .infographic-createel *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ce-wrap">
<div class="ce-hero">
<h1>document.createElement()</h1>
<p>Made in memory first β invisible until it's actually attached.</p>
</div>
<div class="ce-tabs">
<button class="ce-tab is-active" data-tab="basic" type="button">basic div</button>
<button class="ce-tab" data-tab="idclass" type="button">id + class</button>
<button class="ce-tab" data-tab="textnode" type="button">text node</button>
<button class="ce-tab" data-tab="nested" type="button">nested element</button>
<button class="ce-tab" data-tab="list" type="button">list items</button>
<button class="ce-tab" data-tab="script" type="button">dynamic script</button>
</div>
<!-- 1. basic div -->
<div class="ce-panel is-active" data-panel="basic">
<div class="ce-code"><span class="tok-kw">const</span> <span class="tok-var">div</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'div'</span><span class="tok-punc">);</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span> <span class="tok-punc">=</span> <span class="tok-str">'<p>CreateElement example</p>'</span><span class="tok-punc">;</span>
<span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">div</span><span class="tok-punc">);</span></div>
<div class="ce-memory" id="mem-basic"><div class="ce-memory-content"><div><p>CreateElement example</p></div></div></div>
<div class="ce-arrow-row" id="arrow-basic">β appendChild(div)</div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body"><div class="ce-result-el" id="res-basic"><span class="ce-restag"><body></span>CreateElement example</div></div>
</div>
<button class="ce-play" data-run="basic" type="button">run</button>
<p class="ce-note">The element exists the instant it's created β it just isn't on the page yet.</p>
</div>
<!-- 2. id + class -->
<div class="ce-panel" data-panel="idclass">
<div class="ce-code"><span class="tok-kw">const</span> <span class="tok-var">div</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'div'</span><span class="tok-punc">);</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-var">id</span> <span class="tok-punc">=</span> <span class="tok-str">'content'</span><span class="tok-punc">;</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-var">className</span> <span class="tok-punc">=</span> <span class="tok-str">'note'</span><span class="tok-punc">;</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span> <span class="tok-punc">=</span> <span class="tok-str">'<p>CreateElement example</p>'</span><span class="tok-punc">;</span>
<span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">div</span><span class="tok-punc">);</span></div>
<div class="ce-memory" id="mem-idclass"><div class="ce-memory-content"><div id="content" class="note">...</div></div></div>
<div class="ce-arrow-row" id="arrow-idclass">β appendChild(div)</div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body"><div class="ce-result-el" id="res-idclass"><span class="ce-restag"><div id="content" class="note"></span>CreateElement example</div></div>
</div>
<button class="ce-play" data-run="idclass" type="button">run</button>
<p class="ce-note">Set properties on it before it's even attached β same object either way.</p>
</div>
<!-- 3. text node -->
<div class="ce-panel" data-panel="textnode">
<div class="ce-code"><span class="tok-kw">const</span> <span class="tok-var">div</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'div'</span><span class="tok-punc">);</span>
<span class="tok-kw">const</span> <span class="tok-var">text</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createTextNode</span><span class="tok-punc">(</span><span class="tok-str">'CreateElement example'</span><span class="tok-punc">);</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">text</span><span class="tok-punc">);</span>
<span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">div</span><span class="tok-punc">);</span></div>
<div class="ce-memory" id="mem-textnode"><div class="ce-memory-content"><div> + #text "CreateElement example"</div></div>
<div class="ce-arrow-row" id="arrow-textnode">β appendChild(div)</div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body"><div class="ce-result-el" id="res-textnode"><span class="ce-restag"><body></span>CreateElement example</div></div>
</div>
<button class="ce-play" data-run="textnode" type="button">run</button>
<p class="ce-note">Text itself is a node too β you build it and attach it the same way.</p>
</div>
<!-- 4. nested -->
<div class="ce-panel" data-panel="nested">
<div class="ce-code"><span class="tok-kw">const</span> <span class="tok-var">div</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'div'</span><span class="tok-punc">);</span>
<span class="tok-kw">const</span> <span class="tok-var">h2</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'h2'</span><span class="tok-punc">);</span>
<span class="tok-var">h2</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">'Heading inside div'</span><span class="tok-punc">;</span>
<span class="tok-var">div</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">h2</span><span class="tok-punc">);</span>
<span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">div</span><span class="tok-punc">);</span></div>
<div class="ce-memory" id="mem-nested"><div class="ce-memory-content"><div> + <h2>Heading inside div</h2></div></div>
<div class="ce-arrow-row" id="arrow-nested">β appendChild(div)</div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body"><div class="ce-result-el" id="res-nested"><span class="ce-restag"><body></span><div class="ce-innerbox"><h2> Heading inside div</div></div></div>
</div>
<button class="ce-play" data-run="nested" type="button">run</button>
<p class="ce-note">Nest elements before attaching, and the whole structure moves in <strong>together</strong>, in one appendChild.</p>
</div>
<!-- 5. list items -->
<div class="ce-panel" data-panel="list">
<div class="ce-code"><span class="tok-kw">const</span> <span class="tok-var">menu</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">querySelector</span><span class="tok-punc">(</span><span class="tok-str">'#menu'</span><span class="tok-punc">);</span>
<span class="tok-kw">let</span> <span class="tok-var">li</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'li'</span><span class="tok-punc">);</span>
<span class="tok-var">li</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">'Products'</span><span class="tok-punc">;</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">li</span><span class="tok-punc">);</span>
<span class="tok-var">li</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'li'</span><span class="tok-punc">);</span>
<span class="tok-var">li</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">'About Us'</span><span class="tok-punc">;</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">li</span><span class="tok-punc">);</span></div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body">
<div class="ce-static-el"><span class="ce-restag"><ul id="menu"></span>Home</div>
<div class="ce-result-el" id="res-list1" style="margin-bottom: 0.35rem;"><span class="ce-restag"><li></span>Products</div>
<div class="ce-result-el" id="res-list2"><span class="ce-restag"><li></span>About Us</div>
</div>
</div>
<button class="ce-play" data-run="list" type="button">run</button>
<p class="ce-note">Loop the same three steps β create, set, append β to build up a whole list.</p>
</div>
<!-- 6. dynamic script -->
<div class="ce-panel" data-panel="script">
<div class="ce-code"><span class="tok-kw">function</span> <span class="tok-fn">loadJSAsync</span><span class="tok-punc">(url) {</span>
<span class="tok-kw">const</span> <span class="tok-var">script</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'script'</span><span class="tok-punc">);</span>
<span class="tok-var">script</span><span class="tok-punc">.</span><span class="tok-var">src</span> <span class="tok-punc">=</span> <span class="tok-var">url</span><span class="tok-punc">;</span>
<span class="tok-var">script</span><span class="tok-punc">.</span><span class="tok-var">async</span> <span class="tok-punc">=</span> <span class="tok-kw">true</span><span class="tok-punc">;</span>
<span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">script</span><span class="tok-punc">);</span>
<span class="tok-punc">}</span>
<span class="tok-fn">loadJSAsync</span><span class="tok-punc">(</span><span class="tok-str">'/lib.js'</span><span class="tok-punc">);</span></div>
<div class="ce-memory" id="mem-script"><div class="ce-memory-content"><script src="/lib.js" async></div></div>
<div class="ce-arrow-row" id="arrow-script">β appendChild(script)</div>
<div class="ce-page">
<div class="ce-page-bar"><div class="ce-page-dot"></div><div class="ce-page-dot"></div><div class="ce-page-dot"></div></div>
<div class="ce-page-body"><div class="ce-result-el" id="res-script" style="display:inline-flex; align-items:center; gap:0.4rem;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:15px;height:15px;"><path d="M12 13v8l-4-4"/><path d="m12 21 4-4"/><path d="M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284"/></svg>
fetching lib.js β nothing visible on the page
</div></div>
</div>
<button class="ce-play" data-run="script" type="button">run</button>
<p class="ce-note"><strong><script></strong> renders nothing β appending it just makes the browser start loading and running the file.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-createel')
|| document.querySelector('.infographic-createel');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
wrapper.querySelectorAll('.ce-tab').forEach(tab => {
tab.addEventListener('click', () => {
wrapper.querySelectorAll('.ce-tab').forEach(t => t.classList.remove('is-active'));
wrapper.querySelectorAll('.ce-panel').forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
wrapper.querySelector('.ce-panel[data-panel="' + tab.dataset.tab + '"]').classList.add('is-active');
});
});
function runSimple(key) {
const mem = wrapper.querySelector('#mem-' + key);
const arrow = wrapper.querySelector('#arrow-' + key);
const res = wrapper.querySelector('#res-' + key);
mem.classList.remove('is-shown');
arrow.classList.remove('is-lit');
res.classList.remove('is-shown');
void mem.offsetWidth;
setTimeout(() => mem.classList.add('is-shown'), 60);
setTimeout(() => arrow.classList.add('is-lit'), 500);
setTimeout(() => { res.classList.add('is-shown'); mem.classList.remove('is-shown'); }, 950);
}
['basic', 'idclass', 'textnode', 'nested', 'script'].forEach(key => {
wrapper.querySelector('[data-run="' + key + '"]').addEventListener('click', () => runSimple(key));
});
wrapper.querySelector('[data-run="list"]').addEventListener('click', () => {
const r1 = wrapper.querySelector('#res-list1');
const r2 = wrapper.querySelector('#res-list2');
r1.classList.remove('is-shown');
r2.classList.remove('is-shown');
void r1.offsetWidth;
setTimeout(() => r1.classList.add('is-shown'), 200);
setTimeout(() => r2.classList.add('is-shown'), 800);
});
})();
</script>π Summary
document.createElement()β creates an elementappendChild()β adds it to the DOMUse:
innerHTMLβ add HTMLtextContent/createTextNode()β add textid,classNameβ set attributes
Element is created first β added later β then visible on page
β appendChild() - append a node to a list of child nodes of a specified parent node.
πΉ What is appendChild()?
appendChild() is used to add a node as the last child of a parent element.
parentNode.appendChild(childNode);π Key Points
It adds elements at the end of a parent.
It returns the appended child.
If the child already exists in the DOM, it is moved, not duplicated.
1οΈβ£ Basic appendChild() Example
βοΈ HTML
<ul id="menu"></ul>π§ JavaScript
function createMenuItem(name) {
const li = document.createElement('li');
li.textContent = name;
return li;
}
const menu = document.querySelector('#menu');
menu.appendChild(createMenuItem('Home'));
menu.appendChild(createMenuItem('Services'));
menu.appendChild(createMenuItem('About Us'));<ul id="menu">
<li>Home</li>
<li>Services</li>
<li>About Us</li>
</ul>π How it Works
Create
<li>usingcreateElement()Add text using
textContentAttach
<li>to<ul>usingappendChild()
2οΈβ£ Moving an Existing Node (Important Concept)
βοΈ HTML (Before)
<ul id="first-list">
<li>Everest</li>
<li>Fuji</li>
<li>Kilimanjaro</li>
</ul>
<ul id="second-list">
<li>Karakoram Range</li>
<li>Denali</li>
<li>Mont Blanc</li>
</ul>π§ JavaScript
const firstList = document.querySelector('#first-list');
const everest = firstList.firstElementChild;
const secondList = document.querySelector('#second-list');
secondList.appendChild(everest);<ul id="first-list">
<li>Fuji</li>
<li>Kilimanjaro</li>
</ul>
<ul id="second-list">
<li>Karakoram Range</li>
<li>Denali</li>
<li>Mont Blanc</li>
<li>Everest</li>
</ul>βοΈImportant Note
appendChild()moves the elementThe element is removed from its old parent
No duplicate nodes are created
<div class="infographic-appendchild">
<style>
.infographic-appendchild {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
--bad: #c8452e;
--bad-soft: #f7e3df;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-appendchild[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
--bad: #e0715a;
--bad-soft: #2e211d;
}
.infographic-appendchild * { box-sizing: border-box; }
.infographic-appendchild h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-appendchild p { margin: 0; }
.infographic-appendchild button { font-family: inherit; }
.ac-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.ac-hero { text-align: center; margin-bottom: 1.2rem; }
.ac-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.ac-hero p { color: var(--ink-soft); margin-top: 0.4rem; font-size: 0.84rem; }
.ac-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center; margin-bottom: 1.2rem; }
.ac-tab { border: 1px solid var(--line); background: var(--paper-alt); color: var(--ink-soft); padding: 0.36rem 0.65rem; font-size: 0.66rem; font-weight: 700; cursor: pointer; font-family: 'IBM Plex Mono', monospace; }
.ac-tab.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.ac-tab:hover:not(.is-active) { border-color: var(--ink-soft); color: var(--ink); }
.ac-panel { display: none; }
.ac-panel.is-active { display: block; }
.ac-code { background: #1e1e1e; color: #d4d4d4; border-radius: 5px; padding: 0.6rem 0.8rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.66rem; line-height: 1.6; margin-bottom: 1rem; white-space: pre; overflow-x: auto; }
.tok-kw { color: #569cd6; }
.tok-fn { color: #dcdcaa; }
.tok-str { color: #ce9178; }
.tok-var { color: #9cdcfe; }
.tok-punc { color: #d4d4d4; }
.ac-page { border: 1.5px solid var(--line); background: var(--paper-alt); border-radius: 6px; overflow: hidden; margin-bottom: 1rem; }
.ac-page-bar { background: #2b2b2b; padding: 0.4rem 0.6rem; display: flex; gap: 0.3rem; }
.ac-page-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; }
.ac-page-body { padding: 0.9rem 1rem; background: var(--paper); }
.ac-item { border: 1.5px solid var(--line); border-radius: 4px; padding: 0.35rem 0.6rem; font-size: 0.8rem; margin-bottom: 0.35rem; opacity: 0; transform: translateY(-8px) scale(0.95); transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease; }
.ac-item.is-shown { opacity: 1; transform: translateY(0) scale(1); border-color: var(--good); background: var(--good-soft); }
.ac-item .ac-tag { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 0.56rem; color: var(--ink-soft); margin-bottom: 0.1rem; }
.ac-play { display: block; margin: 0 auto; border: none; background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; }
.ac-play:active { transform: scale(0.96); }
.ac-play:disabled { opacity: 0.45; cursor: default; }
.ac-note { text-align: center; font-size: 0.78rem; color: var(--ink-soft); padding-top: 1.3rem; }
.ac-note strong { color: var(--ink); }
/* moving-node section */
.ac-lists { display: grid; grid-template-columns: 1fr 1fr; gap: 0.7rem; margin-bottom: 1rem; }
@media (max-width: 480px) { .ac-lists { grid-template-columns: 1fr; } }
.ac-listbox { border: 1.5px solid var(--line); background: var(--paper-alt); border-radius: 6px; padding: 0.6rem 0.7rem; }
.ac-listbox .ac-listtitle { font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem; color: var(--ink-soft); margin-bottom: 0.45rem; text-align: center; }
.ac-litem { border: 1.5px solid var(--line); border-radius: 4px; padding: 0.3rem 0.5rem; font-size: 0.76rem; margin-bottom: 0.3rem; background: var(--paper); transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease, max-height 0.4s ease, margin 0.4s ease, padding 0.4s ease; overflow: hidden; }
.ac-litem.moving-out { opacity: 0; transform: translateX(30px) scale(0.85); max-height: 0; margin-bottom: 0; padding-top: 0; padding-bottom: 0; border-width: 0; }
.ac-litem.moving-in { opacity: 0; transform: translateY(-8px) scale(0.9); }
.ac-litem.moving-in.is-in { opacity: 1; transform: translateY(0) scale(1); border-color: var(--accent); background: var(--accent-soft); }
.ac-litem.highlight { border-color: var(--accent); background: var(--accent-soft); }
.ac-badge-row { display: flex; justify-content: center; gap: 0.5rem; margin: 0.8rem 0; }
.ac-badge { font-family: 'IBM Plex Mono', monospace; font-size: 0.66rem; padding: 0.25rem 0.6rem; border: 1.5px solid var(--line); color: var(--ink-soft); opacity: 0; transition: opacity 0.3s ease, border-color 0.3s ease, color 0.3s ease, background 0.3s ease; }
.ac-badge.is-shown { opacity: 1; }
.ac-badge.moved { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.ac-badge.noDup { border-color: var(--bad); color: var(--bad); background: var(--bad-soft); text-decoration: line-through; }
@media (prefers-reduced-motion: reduce) {
.infographic-appendchild *, .infographic-appendchild *::before, .infographic-appendchild *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ac-wrap">
<div class="ac-hero">
<h1>appendChild()</h1>
<p>Adds a node as the last child. If that node's already somewhere else, it moves.</p>
</div>
<div class="ac-tabs">
<button class="ac-tab is-active" data-tab="build" type="button">building a list</button>
<button class="ac-tab" data-tab="move" type="button">moving an existing node</button>
</div>
<!-- building a list -->
<div class="ac-panel is-active" data-panel="build">
<div class="ac-code"><span class="tok-kw">function</span> <span class="tok-fn">createMenuItem</span><span class="tok-punc">(name) {</span>
<span class="tok-kw">const</span> <span class="tok-var">li</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'li'</span><span class="tok-punc">);</span>
<span class="tok-var">li</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-var">name</span><span class="tok-punc">;</span>
<span class="tok-kw">return</span> <span class="tok-var">li</span><span class="tok-punc">;</span>
<span class="tok-punc">}</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-fn">createMenuItem</span><span class="tok-punc">(</span><span class="tok-str">'Home'</span><span class="tok-punc">));</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-fn">createMenuItem</span><span class="tok-punc">(</span><span class="tok-str">'Services'</span><span class="tok-punc">));</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-fn">createMenuItem</span><span class="tok-punc">(</span><span class="tok-str">'About Us'</span><span class="tok-punc">));</span></div>
<div class="ac-page">
<div class="ac-page-bar"><div class="ac-page-dot"></div><div class="ac-page-dot"></div><div class="ac-page-dot"></div></div>
<div class="ac-page-body">
<span style="font-family:'IBM Plex Mono',monospace;font-size:0.6rem;color:var(--ink-soft);display:block;margin-bottom:0.4rem;"><ul id="menu"></span>
<div class="ac-item" id="bi1"><span class="ac-tag"><li></span>Home</div>
<div class="ac-item" id="bi2"><span class="ac-tag"><li></span>Services</div>
<div class="ac-item" id="bi3"><span class="ac-tag"><li></span>About Us</div>
</div>
</div>
<button class="ac-play" data-run="build" type="button">run</button>
<p class="ac-note">Each call lands <strong>after</strong> the last one β always appended at the end.</p>
</div>
<!-- moving an existing node -->
<div class="ac-panel" data-panel="move">
<div class="ac-code"><span class="tok-kw">const</span> <span class="tok-var">firstList</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">querySelector</span><span class="tok-punc">(</span><span class="tok-str">'#first-list'</span><span class="tok-punc">);</span>
<span class="tok-kw">const</span> <span class="tok-var">everest</span> <span class="tok-punc">=</span> <span class="tok-var">firstList</span><span class="tok-punc">.</span><span class="tok-var">firstElementChild</span><span class="tok-punc">;</span>
<span class="tok-kw">const</span> <span class="tok-var">secondList</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">querySelector</span><span class="tok-punc">(</span><span class="tok-str">'#second-list'</span><span class="tok-punc">);</span>
<span class="tok-var">secondList</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">everest</span><span class="tok-punc">);</span></div>
<div class="ac-lists">
<div class="ac-listbox">
<div class="ac-listtitle">#first-list</div>
<div class="ac-litem highlight" id="everestItem">Everest</div>
<div class="ac-litem">Fuji</div>
<div class="ac-litem">Kilimanjaro</div>
</div>
<div class="ac-listbox">
<div class="ac-listtitle">#second-list</div>
<div class="ac-litem">Karakoram Range</div>
<div class="ac-litem">Denali</div>
<div class="ac-litem">Mont Blanc</div>
<div class="ac-litem moving-in" id="everestClone">Everest</div>
</div>
</div>
<div class="ac-badge-row">
<span class="ac-badge moved" id="badgeMoved">moved to second-list</span>
<span class="ac-badge noDup" id="badgeDup">no duplicate left behind</span>
</div>
<button class="ac-play" data-run="move" type="button">run</button>
<p class="ac-note"><strong>Everest</strong> is the exact same node β it never existed in two places at once.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-appendchild')
|| document.querySelector('.infographic-appendchild');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
wrapper.querySelectorAll('.ac-tab').forEach(tab => {
tab.addEventListener('click', () => {
wrapper.querySelectorAll('.ac-tab').forEach(t => t.classList.remove('is-active'));
wrapper.querySelectorAll('.ac-panel').forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
wrapper.querySelector('.ac-panel[data-panel="' + tab.dataset.tab + '"]').classList.add('is-active');
});
});
/* building a list */
wrapper.querySelector('[data-run="build"]').addEventListener('click', () => {
const items = ['bi1', 'bi2', 'bi3'].map(id => wrapper.querySelector('#' + id));
items.forEach(i => i.classList.remove('is-shown'));
items.forEach((item, i) => setTimeout(() => item.classList.add('is-shown'), 200 + i * 400));
});
/* moving an existing node */
wrapper.querySelector('[data-run="move"]').addEventListener('click', () => {
const everest = wrapper.querySelector('#everestItem');
const clone = wrapper.querySelector('#everestClone');
const badgeMoved = wrapper.querySelector('#badgeMoved');
const badgeDup = wrapper.querySelector('#badgeDup');
everest.classList.remove('moving-out');
clone.classList.remove('is-in');
badgeMoved.classList.remove('is-shown');
badgeDup.classList.remove('is-shown');
void everest.offsetWidth;
setTimeout(() => { everest.classList.add('moving-out'); }, 100);
setTimeout(() => { clone.classList.add('is-in'); }, 500);
setTimeout(() => { badgeMoved.classList.add('is-shown'); }, 900);
setTimeout(() => { badgeDup.classList.add('is-shown'); }, 1150);
});
})();
</script>π§Ύ Summary
appendChild()β adds a child at the endWorks with new or existing elements
Existing elements are moved, not copied
Commonly used with:
createElement()querySelector()textContent
Create β Append β Display
β textContent() and innerHTML()
πΉ What is textContent?
textContent is used to get or set only the text of an element and all its child nodes.
let text = node.textContent;1οΈβ£ Reading textContent
βοΈ HTML
<div id="note">
JavaScript textContent Demo!
<span style="display:none">Hidden Text!</span>
<!-- comment -->
</div>π§ JavaScript
const note = document.getElementById('note');
console.log(note.textContent);JavaScript textContent Demo!
Hidden Text!π Key Point
Includes hidden text
Ignores HTML comments
Does not care about CSS
2οΈβ£ textContent vs innerText
console.log(note.innerText);JavaScript textContent Demo!π Difference
Property | Hidden Text | CSS Aware | Performance |
|---|---|---|---|
| β Yes | β No | β‘ Fast |
| β No | β Yes | π’ Slower (reflow) |
β innerText triggers reflow (browser recalculates layout)
3οΈβ£ Setting textContent
note.textContent = 'This is a note';This is a noteπ Important
Removes all child elements
Inserts only plain text
Safe from XSS attacks
<div class="infographic-textcontent">
<style>
.infographic-textcontent {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
--bad: #c8452e;
--bad-soft: #f7e3df;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-textcontent[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
--bad: #e0715a;
--bad-soft: #2e211d;
}
.infographic-textcontent * { box-sizing: border-box; }
.infographic-textcontent h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-textcontent p { margin: 0; }
.infographic-textcontent button { font-family: inherit; }
.tc3-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.tc3-hero { text-align: center; margin-bottom: 1.2rem; }
.tc3-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.tc3-hero p { color: var(--ink-soft); margin-top: 0.4rem; font-size: 0.84rem; }
.tc3-tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: center; margin-bottom: 1.2rem; }
.tc3-tab { border: 1px solid var(--line); background: var(--paper-alt); color: var(--ink-soft); padding: 0.34rem 0.6rem; font-size: 0.64rem; font-weight: 700; cursor: pointer; font-family: 'IBM Plex Mono', monospace; }
.tc3-tab.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.tc3-tab:hover:not(.is-active) { border-color: var(--ink-soft); color: var(--ink); }
.tc3-panel { display: none; }
.tc3-panel.is-active { display: block; }
.tc3-page { border: 1.5px solid var(--line); background: var(--paper-alt); border-radius: 6px; overflow: hidden; margin-bottom: 0.9rem; }
.tc3-page-bar { background: #2b2b2b; padding: 0.4rem 0.6rem; display: flex; gap: 0.3rem; }
.tc3-page-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; }
.tc3-page-body { padding: 0.9rem 1rem; background: var(--paper); }
.tc3-container { border: 1.5px dashed var(--line); border-radius: 5px; padding: 0.5rem; }
.tc3-tag { font-family: 'IBM Plex Mono', monospace; font-size: 0.56rem; color: var(--ink-soft); display: block; margin-bottom: 0.3rem; }
.tc3-node { border-radius: 4px; margin-top: 0.3rem; transition: border-color 0.35s ease, background 0.35s ease, box-shadow 0.35s ease, opacity 0.3s ease; }
.tc3-node:first-of-type { margin-top: 0; }
.tc3-node.txtline { border: 1.5px dashed var(--line); padding: 0.3rem 0.5rem; font-size: 0.78rem; color: var(--ink); }
.tc3-node.hidden-el { border: 1.5px dashed var(--line); padding: 0.3rem 0.5rem; font-size: 0.78rem; color: var(--ink-soft); text-decoration: line-through; opacity: 0.7; }
.tc3-node.comment { border: 1.5px dotted var(--line); padding: 0.15rem 0.5rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.6rem; color: var(--ink-soft); opacity: 0.55; }
.tc3-node .tc3-elname { font-family: 'IBM Plex Mono', monospace; font-size: 0.56rem; color: var(--ink-soft); display: block; margin-bottom: 0.1rem; }
.tc3-node.is-hit { border-style: solid; border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 3px var(--accent-soft); opacity: 1; color: var(--accent); text-decoration: none; }
.tc3-node.is-skip { opacity: 0.3; }
.tc3-code { background: #1e1e1e; color: #d4d4d4; border-radius: 5px; padding: 0.6rem 0.8rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; line-height: 1.6; margin-bottom: 1rem; white-space: pre; overflow-x: auto; }
.tok-kw { color: #569cd6; }
.tok-fn { color: #dcdcaa; }
.tok-str { color: #ce9178; }
.tok-var { color: #9cdcfe; }
.tok-punc { color: #d4d4d4; }
.tc3-play { display: block; margin: 0 auto; border: none; background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; }
.tc3-play.alt { background: var(--good); }
.tc3-play:active { transform: scale(0.96); }
.tc3-playrow { display: flex; gap: 0.6rem; justify-content: center; flex-wrap: wrap; }
.tc3-console { margin: 1rem auto 0; max-width: 340px; text-align: left; border: 1.5px solid var(--line); background: var(--paper-alt); padding: 0.5rem 0.75rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; min-height: 1.8em; }
.tc3-console .tc3-cline { opacity: 0; transition: opacity 0.25s ease; color: var(--good); display: block; }
.tc3-console .tc3-cline.is-shown { opacity: 1; }
.tc3-note { text-align: center; font-size: 0.78rem; color: var(--ink-soft); padding-top: 1.3rem; }
.tc3-note strong { color: var(--ink); }
/* comparison chips */
.tc3-chiptable { border: 1px solid var(--line); margin-top: 1.1rem; }
.tc3-chiprow { display: grid; grid-template-columns: 1.1fr 1fr 1fr; }
.tc3-chiprow + .tc3-chiprow { border-top: 1px solid var(--line); }
.tc3-chiplabel { background: var(--paper-alt); padding: 0.45rem 0.6rem; font-size: 0.66rem; color: var(--ink-soft); font-weight: 600; }
.tc3-chipval { padding: 0.45rem 0.6rem; font-size: 0.68rem; text-align: center; border-left: 1px solid var(--line); }
.tc3-chipval.good { color: var(--good); font-weight: 700; }
.tc3-chipval.bad { color: var(--bad); font-weight: 700; }
.tc3-colhead { text-align: center; font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem; font-weight: 700; color: var(--ink-soft); padding: 0.4rem 0.4rem; border-left: 1px solid var(--line); background: var(--paper-alt); }
@media (prefers-reduced-motion: reduce) {
.infographic-textcontent *, .infographic-textcontent *::before, .infographic-textcontent *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="tc3-wrap">
<div class="tc3-hero">
<h1>textContent vs innerText</h1>
<p>Same element, two different ideas of "the text."</p>
</div>
<div class="tc3-tabs">
<button class="tc3-tab is-active" data-tab="read" type="button">reading textContent</button>
<button class="tc3-tab" data-tab="vs" type="button">vs innerText</button>
<button class="tc3-tab" data-tab="set" type="button">setting textContent</button>
</div>
<!-- reading -->
<div class="tc3-panel is-active" data-panel="read">
<div class="tc3-page">
<div class="tc3-page-bar"><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div></div>
<div class="tc3-page-body">
<div class="tc3-container">
<span class="tc3-tag"><div id="note"></span>
<div class="tc3-node txtline" id="r-text">JavaScript textContent Demo!</div>
<div class="tc3-node hidden-el" id="r-hidden"><span class="tc3-elname"><span style="display:none"></span>Hidden Text!</div>
<div class="tc3-node comment" id="r-comment"><!-- comment --></div>
</div>
</div>
</div>
<div class="tc3-code"><span class="tok-kw">const</span> <span class="tok-var">note</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">getElementById</span><span class="tok-punc">(</span><span class="tok-str">'note'</span><span class="tok-punc">);</span>
<span class="tok-var">console</span><span class="tok-punc">.</span><span class="tok-fn">log</span><span class="tok-punc">(</span><span class="tok-var">note</span><span class="tok-punc">.</span><span class="tok-var">textContent</span><span class="tok-punc">);</span></div>
<button class="tc3-play" data-run="read" type="button">run</button>
<div class="tc3-console"><span class="tc3-cline" id="outRead">JavaScript textContent Demo! Hidden Text!</span></div>
<p class="tc3-note">Grabs <strong>hidden text too</strong> β it doesn't know or care about CSS. Comments are skipped.</p>
</div>
<!-- vs innerText -->
<div class="tc3-panel" data-panel="vs">
<div class="tc3-page">
<div class="tc3-page-bar"><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div></div>
<div class="tc3-page-body">
<div class="tc3-container">
<span class="tc3-tag"><div id="note"></span>
<div class="tc3-node txtline" id="v-text">JavaScript textContent Demo!</div>
<div class="tc3-node hidden-el" id="v-hidden"><span class="tc3-elname"><span style="display:none"></span>Hidden Text!</div>
<div class="tc3-node comment" id="v-comment"><!-- comment --></div>
</div>
</div>
</div>
<div class="tc3-playrow">
<button class="tc3-play" data-run="vs-text" type="button">note.textContent</button>
<button class="tc3-play alt" data-run="vs-inner" type="button">note.innerText</button>
</div>
<div class="tc3-console"><span class="tc3-cline" id="outVs">JavaScript textContent Demo!</span></div>
<div class="tc3-chiptable">
<div class="tc3-chiprow">
<div class="tc3-chiplabel"> </div>
<div class="tc3-colhead">textContent</div>
<div class="tc3-colhead">innerText</div>
</div>
<div class="tc3-chiprow">
<div class="tc3-chiplabel">hidden text</div>
<div class="tc3-chipval good">β
yes</div>
<div class="tc3-chipval bad">β no</div>
</div>
<div class="tc3-chiprow">
<div class="tc3-chiplabel">CSS aware</div>
<div class="tc3-chipval bad">β no</div>
<div class="tc3-chipval good">β
yes</div>
</div>
<div class="tc3-chiprow">
<div class="tc3-chiplabel">performance</div>
<div class="tc3-chipval good">β‘ fast</div>
<div class="tc3-chipval bad">π’ slower</div>
</div>
</div>
<p class="tc3-note"><strong>innerText</strong> triggers a reflow β it has to check what's actually visible first.</p>
</div>
<!-- setting -->
<div class="tc3-panel" data-panel="set">
<div class="tc3-page">
<div class="tc3-page-bar"><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div><div class="tc3-page-dot"></div></div>
<div class="tc3-page-body">
<div class="tc3-container" id="s-container">
<span class="tc3-tag"><div id="note"></span>
<div class="tc3-node txtline" id="s-text">JavaScript textContent Demo!</div>
<div class="tc3-node hidden-el" id="s-hidden"><span class="tc3-elname"><span style="display:none"></span>Hidden Text!</div>
<div class="tc3-node comment" id="s-comment"><!-- comment --></div>
<div class="tc3-node txtline" id="s-new" style="display:none; opacity: 0;">This is a note</div>
</div>
</div>
</div>
<div class="tc3-code"><span class="tok-var">note</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">'This is a note'</span><span class="tok-punc">;</span></div>
<button class="tc3-play" data-run="set" type="button">run</button>
<p class="tc3-note">Wipes out <strong>every</strong> child β the hidden span, the comment, all of it β and replaces the whole thing with plain text. Nothing gets parsed as HTML, so it's safe from injected markup.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-textcontent')
|| document.querySelector('.infographic-textcontent');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
wrapper.querySelectorAll('.tc3-tab').forEach(tab => {
tab.addEventListener('click', () => {
wrapper.querySelectorAll('.tc3-tab').forEach(t => t.classList.remove('is-active'));
wrapper.querySelectorAll('.tc3-panel').forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
wrapper.querySelector('.tc3-panel[data-panel="' + tab.dataset.tab + '"]').classList.add('is-active');
});
});
/* reading */
wrapper.querySelector('[data-run="read"]').addEventListener('click', () => {
const text = wrapper.querySelector('#r-text');
const hidden = wrapper.querySelector('#r-hidden');
const comment = wrapper.querySelector('#r-comment');
const out = wrapper.querySelector('#outRead');
[text, hidden, comment].forEach(el => el.classList.remove('is-hit', 'is-skip'));
out.classList.remove('is-shown');
void text.offsetWidth;
setTimeout(() => {
text.classList.add('is-hit');
hidden.classList.add('is-hit');
comment.classList.add('is-skip');
}, 60);
setTimeout(() => out.classList.add('is-shown'), 350);
});
/* vs innerText */
const vText = wrapper.querySelector('#v-text');
const vHidden = wrapper.querySelector('#v-hidden');
const vComment = wrapper.querySelector('#v-comment');
const outVs = wrapper.querySelector('#outVs');
function resetVs() {
[vText, vHidden, vComment].forEach(el => el.classList.remove('is-hit', 'is-skip'));
outVs.classList.remove('is-shown');
}
wrapper.querySelector('[data-run="vs-text"]').addEventListener('click', () => {
resetVs();
void vText.offsetWidth;
setTimeout(() => {
vText.classList.add('is-hit');
vHidden.classList.add('is-hit');
vComment.classList.add('is-skip');
}, 60);
setTimeout(() => {
outVs.textContent = 'JavaScript textContent Demo! Hidden Text!';
outVs.classList.add('is-shown');
}, 350);
});
wrapper.querySelector('[data-run="vs-inner"]').addEventListener('click', () => {
resetVs();
void vText.offsetWidth;
setTimeout(() => {
vText.classList.add('is-hit');
vHidden.classList.add('is-skip');
vComment.classList.add('is-skip');
}, 60);
setTimeout(() => {
outVs.textContent = 'JavaScript textContent Demo!';
outVs.classList.add('is-shown');
}, 350);
});
/* setting */
wrapper.querySelector('[data-run="set"]').addEventListener('click', () => {
const text = wrapper.querySelector('#s-text');
const hidden = wrapper.querySelector('#s-hidden');
const comment = wrapper.querySelector('#s-comment');
const newEl = wrapper.querySelector('#s-new');
[text, hidden, comment].forEach(el => { el.style.transition = 'opacity 0.3s ease'; el.style.opacity = '1'; el.style.display = ''; });
newEl.style.display = 'none';
newEl.style.opacity = '0';
void text.offsetWidth;
setTimeout(() => {
[text, hidden, comment].forEach(el => { el.style.opacity = '0'; });
}, 100);
setTimeout(() => {
[text, hidden, comment].forEach(el => { el.style.display = 'none'; });
newEl.style.display = '';
void newEl.offsetWidth;
newEl.style.transition = 'opacity 0.35s ease';
newEl.classList.add('is-hit');
requestAnimationFrame(() => { newEl.style.opacity = '1'; });
}, 450);
});
})();
</script>πΉ What is innerHTML?
innerHTML is used to get or set HTML markup inside an element.
element.innerHTML;
element.innerHTML = newHTML;4οΈβ£ Reading innerHTML
βοΈ HTML
<ul id="menu">
<li>Home</li>
<li>Services</li>
</ul>β JavaScript
const menu = document.getElementById('menu');
console.log(menu.innerHTML);<li>Home</li>
<li>Services</li>5οΈβ£ innerHTML Reflects Live DOM Changes
π§ JavaScript
const li = document.createElement('li');
li.textContent = 'About Us';
menu.appendChild(li);
console.log(menu.innerHTML);<li>Home</li>
<li>Services</li>
<li>About Us</li>β Shows original + dynamically added content
6οΈβ£ Setting innerHTML
document.body.innerHTML = '';Page clearedβοΈ Warning
Replaces entire content
Removes all event listeners
π Security Risk with innerHTML
β Dangerous Example
main.innerHTML = `<img src="1" onerror="alert('Hacked!')">`;π€ Output
π¨ Alert pops up β XSS vulnerability
β Safe Alternative
main.textContent = `<img src="1" onerror="alert('Hacked!')">`;<img src="1" onerror="alert('Hacked!')">β Treated as plain text
<div class="infographic-innerhtml">
<style>
.infographic-innerhtml {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
--bad: #c8452e;
--bad-soft: #f7e3df;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-innerhtml[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
--bad: #e0715a;
--bad-soft: #2e211d;
}
.infographic-innerhtml * { box-sizing: border-box; }
.infographic-innerhtml h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-innerhtml p { margin: 0; }
.infographic-innerhtml button { font-family: inherit; }
.ih-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.ih-hero { text-align: center; margin-bottom: 1.2rem; }
.ih-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.ih-hero p { color: var(--ink-soft); margin-top: 0.4rem; font-size: 0.84rem; }
.ih-tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: center; margin-bottom: 1.2rem; }
.ih-tab { border: 1px solid var(--line); background: var(--paper-alt); color: var(--ink-soft); padding: 0.34rem 0.6rem; font-size: 0.64rem; font-weight: 700; cursor: pointer; font-family: 'IBM Plex Mono', monospace; }
.ih-tab.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.ih-tab:hover:not(.is-active) { border-color: var(--ink-soft); color: var(--ink); }
.ih-panel { display: none; }
.ih-panel.is-active { display: block; }
.ih-page { border: 1.5px solid var(--line); background: var(--paper-alt); border-radius: 6px; overflow: hidden; margin-bottom: 0.9rem; }
.ih-page-bar { background: #2b2b2b; padding: 0.4rem 0.6rem; display: flex; gap: 0.3rem; }
.ih-page-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; }
.ih-page-body { padding: 0.9rem 1rem; background: var(--paper); min-height: 2.4em; }
.ih-item { border: 1.5px solid var(--line); border-radius: 4px; padding: 0.32rem 0.55rem; font-size: 0.8rem; margin-bottom: 0.3rem; transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease, max-height 0.4s ease, padding 0.4s ease, margin 0.4s ease; }
.ih-item.is-new { opacity: 0; transform: translateY(-8px) scale(0.95); }
.ih-item.is-new.is-shown { opacity: 1; transform: translateY(0) scale(1); border-color: var(--good); background: var(--good-soft); }
.ih-item.is-clearing { opacity: 0; transform: scale(0.9); max-height: 0; padding-top: 0; padding-bottom: 0; margin-bottom: 0; border-width: 0; }
.ih-listen-badge { display: inline-block; font-size: 0.58rem; margin-left: 0.4rem; color: var(--accent); }
.ih-code { background: #1e1e1e; color: #d4d4d4; border-radius: 5px; padding: 0.6rem 0.8rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.66rem; line-height: 1.6; margin-bottom: 1rem; white-space: pre; overflow-x: auto; }
.tok-kw { color: #569cd6; }
.tok-fn { color: #dcdcaa; }
.tok-str { color: #ce9178; }
.tok-var { color: #9cdcfe; }
.tok-punc { color: #d4d4d4; }
.ih-play { display: block; margin: 0 auto; border: none; background: var(--accent); color: #fff; padding: 0.5rem 1.2rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; }
.ih-play.alt { background: var(--good); }
.ih-play.danger { background: var(--bad); }
.ih-play:active { transform: scale(0.96); }
.ih-playrow { display: flex; gap: 0.6rem; justify-content: center; flex-wrap: wrap; }
.ih-console { margin: 1rem auto 0; max-width: 360px; text-align: left; border: 1.5px solid var(--line); background: var(--paper-alt); padding: 0.5rem 0.75rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.66rem; min-height: 1.8em; white-space: pre-wrap; }
.ih-console .ih-cline { opacity: 0; transition: opacity 0.25s ease; color: var(--good); display: block; }
.ih-console .ih-cline.is-shown { opacity: 1; }
.ih-note { text-align: center; font-size: 0.78rem; color: var(--ink-soft); padding-top: 1.3rem; }
.ih-note strong { color: var(--ink); }
/* XSS demo */
.ih-xss-stage { min-height: 3.4em; display: flex; align-items: center; justify-content: center; text-align: center; }
.ih-xss-safe { font-family: 'IBM Plex Mono', monospace; font-size: 0.7rem; color: var(--ink-soft); opacity: 0; transition: opacity 0.3s ease; }
.ih-xss-safe.is-shown { opacity: 1; }
.ih-alert-toast {
display: inline-flex; align-items: center; gap: 0.4rem; border: 1.5px solid var(--bad); background: var(--bad-soft); color: var(--bad);
padding: 0.4rem 0.7rem; font-size: 0.72rem; font-weight: 700; border-radius: 5px; opacity: 0; transform: scale(0.85);
transition: opacity 0.25s ease, transform 0.25s ease;
}
.ih-alert-toast.is-shown { opacity: 1; transform: scale(1); animation: ih-shake 0.4s ease; }
@keyframes ih-shake { 20% { transform: translateX(-4px) scale(1); } 40% { transform: translateX(4px) scale(1); } 60% { transform: translateX(-3px) scale(1); } 80% { transform: translateX(3px) scale(1); } 100% { transform: translateX(0) scale(1); } }
.ih-safe-badge { display: inline-flex; align-items: center; gap: 0.4rem; border: 1.5px solid var(--good); background: var(--good-soft); color: var(--good); padding: 0.4rem 0.7rem; font-size: 0.72rem; font-weight: 700; border-radius: 5px; opacity: 0; transition: opacity 0.3s ease; }
.ih-safe-badge.is-shown { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
.infographic-innerhtml *, .infographic-innerhtml *::before, .infographic-innerhtml *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ih-wrap">
<div class="ih-hero">
<h1>innerHTML</h1>
<p>Reads and writes real markup β which is exactly what makes it powerful, and risky.</p>
</div>
<div class="ih-tabs">
<button class="ih-tab is-active" data-tab="read" type="button">reading</button>
<button class="ih-tab" data-tab="live" type="button">reflects live DOM</button>
<button class="ih-tab" data-tab="set" type="button">setting / clearing</button>
<button class="ih-tab" data-tab="xss" type="button">security risk</button>
</div>
<!-- reading -->
<div class="ih-panel is-active" data-panel="read">
<div class="ih-page">
<div class="ih-page-bar"><div class="ih-page-dot"></div><div class="ih-page-dot"></div><div class="ih-page-dot"></div></div>
<div class="ih-page-body">
<span style="font-family:'IBM Plex Mono',monospace;font-size:0.6rem;color:var(--ink-soft);display:block;margin-bottom:0.4rem;"><ul id="menu"></span>
<div class="ih-item">Home</div>
<div class="ih-item">Services</div>
</div>
</div>
<div class="ih-code"><span class="tok-kw">const</span> <span class="tok-var">menu</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">getElementById</span><span class="tok-punc">(</span><span class="tok-str">'menu'</span><span class="tok-punc">);</span>
<span class="tok-var">console</span><span class="tok-punc">.</span><span class="tok-fn">log</span><span class="tok-punc">(</span><span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span><span class="tok-punc">);</span></div>
<button class="ih-play" data-run="read" type="button">run</button>
<div class="ih-console"><span class="ih-cline" id="outRead"><li>Home</li>
<li>Services</li></span></div>
<p class="ih-note">Comes back as a <strong>markup string</strong> β tags and all, not rendered elements.</p>
</div>
<!-- live -->
<div class="ih-panel" data-panel="live">
<div class="ih-page">
<div class="ih-page-bar"><div class="ih-page-dot"></div><div class="ih-page-dot"></div><div class="ih-page-dot"></div></div>
<div class="ih-page-body">
<span style="font-family:'IBM Plex Mono',monospace;font-size:0.6rem;color:var(--ink-soft);display:block;margin-bottom:0.4rem;"><ul id="menu"></span>
<div class="ih-item">Home</div>
<div class="ih-item">Services</div>
<div class="ih-item is-new" id="liveNew">About Us</div>
</div>
</div>
<div class="ih-code"><span class="tok-kw">const</span> <span class="tok-var">li</span> <span class="tok-punc">=</span> <span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-fn">createElement</span><span class="tok-punc">(</span><span class="tok-str">'li'</span><span class="tok-punc">);</span>
<span class="tok-var">li</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">'About Us'</span><span class="tok-punc">;</span>
<span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-fn">appendChild</span><span class="tok-punc">(</span><span class="tok-var">li</span><span class="tok-punc">);</span>
<span class="tok-var">console</span><span class="tok-punc">.</span><span class="tok-fn">log</span><span class="tok-punc">(</span><span class="tok-var">menu</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span><span class="tok-punc">);</span></div>
<button class="ih-play" data-run="live" type="button">run</button>
<div class="ih-console">
<span class="ih-cline" id="outLive"><li>Home</li>
<li>Services</li>
<li>About Us</li></span>
</div>
<p class="ih-note">No separate "refresh" needed β <strong>innerHTML</strong> always reflects whatever's really in the DOM right now.</p>
</div>
<!-- set / clear -->
<div class="ih-panel" data-panel="set">
<div class="ih-page">
<div class="ih-page-bar"><div class="ih-page-dot"></div><div class="ih-page-dot"></div><div class="ih-page-dot"></div></div>
<div class="ih-page-body">
<span style="font-family:'IBM Plex Mono',monospace;font-size:0.6rem;color:var(--ink-soft);display:block;margin-bottom:0.4rem;"><body></span>
<div class="ih-item" id="setItem1">Welcome heading <span class="ih-listen-badge">π has a click listener</span></div>
<div class="ih-item" id="setItem2">Some paragraph text</div>
<div class="ih-item" id="setItem3">A footer note</div>
<div class="ih-item is-new" id="setCleared" style="text-align:center; color: var(--ink-soft);">β empty β</div>
</div>
</div>
<div class="ih-code"><span class="tok-var">document</span><span class="tok-punc">.</span><span class="tok-var">body</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span> <span class="tok-punc">=</span> <span class="tok-str">''</span><span class="tok-punc">;</span></div>
<button class="ih-play" data-run="set" type="button">run</button>
<p class="ih-note">Everything is gone β including the click listener on that heading. It didn't move anywhere; the element it was attached to was destroyed.</p>
</div>
<!-- xss -->
<div class="ih-panel" data-panel="xss">
<div class="ih-code">β <span class="tok-var">main</span><span class="tok-punc">.</span><span class="tok-var">innerHTML</span> <span class="tok-punc">=</span> <span class="tok-str">`<img src="1" onerror="alert('Hacked!')">`</span><span class="tok-punc">;</span>
β
<span class="tok-var">main</span><span class="tok-punc">.</span><span class="tok-var">textContent</span> <span class="tok-punc">=</span> <span class="tok-str">`<img src="1" onerror="alert('Hacked!')">`</span><span class="tok-punc">;</span></div>
<div class="ih-page">
<div class="ih-page-bar"><div class="ih-page-dot"></div><div class="ih-page-dot"></div><div class="ih-page-dot"></div></div>
<div class="ih-page-body ih-xss-stage" id="xssStage">
<span class="ih-xss-safe" id="xssSafeText"><img src="1" onerror="alert('Hacked!')"></span>
<div class="ih-alert-toast" id="xssAlert">π¨ alert("Hacked!")</div>
<div class="ih-safe-badge" id="xssSafeBadge">β treated as plain text β nothing runs</div>
</div>
</div>
<div class="ih-playrow">
<button class="ih-play danger" data-run="xss-bad" type="button">innerHTML (dangerous)</button>
<button class="ih-play alt" data-run="xss-safe" type="button">textContent (safe)</button>
</div>
<p class="ih-note">Any HTML you drop into <strong>innerHTML</strong> gets parsed and can execute β never use it on untrusted input.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-innerhtml')
|| document.querySelector('.infographic-innerhtml');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
wrapper.querySelectorAll('.ih-tab').forEach(tab => {
tab.addEventListener('click', () => {
wrapper.querySelectorAll('.ih-tab').forEach(t => t.classList.remove('is-active'));
wrapper.querySelectorAll('.ih-panel').forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
wrapper.querySelector('.ih-panel[data-panel="' + tab.dataset.tab + '"]').classList.add('is-active');
});
});
/* reading */
wrapper.querySelector('[data-run="read"]').addEventListener('click', () => {
const out = wrapper.querySelector('#outRead');
out.classList.remove('is-shown');
void out.offsetWidth;
setTimeout(() => out.classList.add('is-shown'), 150);
});
/* live */
wrapper.querySelector('[data-run="live"]').addEventListener('click', () => {
const newItem = wrapper.querySelector('#liveNew');
const out = wrapper.querySelector('#outLive');
newItem.classList.remove('is-shown');
out.classList.remove('is-shown');
void newItem.offsetWidth;
setTimeout(() => newItem.classList.add('is-shown'), 100);
setTimeout(() => out.classList.add('is-shown'), 550);
});
/* set / clear */
wrapper.querySelector('[data-run="set"]').addEventListener('click', () => {
const i1 = wrapper.querySelector('#setItem1');
const i2 = wrapper.querySelector('#setItem2');
const i3 = wrapper.querySelector('#setItem3');
const cleared = wrapper.querySelector('#setCleared');
[i1, i2, i3].forEach(el => el.classList.remove('is-clearing'));
cleared.classList.remove('is-shown');
void i1.offsetWidth;
setTimeout(() => { [i1, i2, i3].forEach(el => el.classList.add('is-clearing')); }, 80);
setTimeout(() => cleared.classList.add('is-shown'), 500);
});
/* xss */
const safeText = wrapper.querySelector('#xssSafeText');
const alertToast = wrapper.querySelector('#xssAlert');
const safeBadge = wrapper.querySelector('#xssSafeBadge');
wrapper.querySelector('[data-run="xss-bad"]').addEventListener('click', () => {
safeText.classList.remove('is-shown');
safeBadge.classList.remove('is-shown');
alertToast.classList.remove('is-shown');
void alertToast.offsetWidth;
setTimeout(() => alertToast.classList.add('is-shown'), 150);
});
wrapper.querySelector('[data-run="xss-safe"]').addEventListener('click', () => {
alertToast.classList.remove('is-shown');
safeBadge.classList.remove('is-shown');
safeText.classList.remove('is-shown');
void safeText.offsetWidth;
setTimeout(() => safeText.classList.add('is-shown'), 100);
setTimeout(() => safeBadge.classList.add('is-shown'), 500);
});
})();
</script>π§Ύ Final Comparison
Feature |
|
|
|---|---|---|
Gets text | β Yes | β No |
Gets HTML | β No | β Yes |
Parses HTML | β No | β Yes |
Safe from XSS | β Yes | β No |
Removes children when set | β Yes | β Yes |
π Summary
Use
textContentfor:Plain text
Better performance
Security-safe content
Use
innerHTMLfor:Inserting HTML
Dynamic UI updates
Only with trusted content
β Rule of Thumb:
If itβs text β textContent
If itβs HTML β
innerHTML