Creating and Modifying Elements
Objectives
By the end of this chapter, you should be able to:
- Create a new element and add it to the page
- Explain the difference between
.textContentand.innerHTML - Explain how
.innerTextdiffers from both, and why it’s used less often - Read and change an element’s attributes
- Add, remove, and toggle CSS classes with
.classList
💡 Why this matters: Selecting elements only gets you halfway. This is how you actually change what a page shows.
Creating a New Element
document.createElement(tagName) builds a new element. It doesn’t appear on the page until you attach it somewhere.
Given this HTML, already on the page:
<div id="container"></div>
const card = document.createElement("div");
card.textContent = "Hello";
const container = document.getElementById("container");
container.appendChild(card);
console.log(container.innerHTML);
// <div>Hello</div>
.appendChild() adds the new element as the last child of container. A newer method, .append(), does the same thing and additionally accepts plain text and multiple arguments at once:
const card2 = document.createElement("p");
card2.textContent = "World";
container.append(card2);
console.log(container.innerHTML);
// <div>Hello</div><p>World</p>
.textContent vs. .innerHTML
Both set what’s inside an element, but they behave very differently.
const box = document.createElement("div");
box.textContent = "<strong>bold</strong>";
console.log(box.innerHTML);
// <strong>bold</strong>
Setting .textContent treats the string as plain text. The <strong> tag doesn’t become an actual bold element, it shows up as literal text (escaped, so it displays as <strong>bold</strong> on the page rather than rendering as bold).
box.innerHTML = "<strong>bold</strong>";
console.log(box.textContent);
// bold
Setting .innerHTML parses the string as real HTML. Here, <strong> becomes an actual bold element.
Default to .textContent unless you specifically need to insert HTML. Setting .innerHTML with anything that includes user input is a security risk (it’s how cross-site scripting attacks happen), since a malicious string could include a <script> tag or an event handler attribute. .textContent never has this problem, because it never parses its input as markup.
A Third Option: .innerText
There’s a third property that looks like it does the same job as .textContent, but doesn’t: .innerText. It’s worth knowing the difference, since mixing them up leads to confusing bugs.
<div id="box">
Visible text
<span style="display: none">Hidden text</span>
</div>
const box = document.getElementById("box");
console.log(box.textContent);
// "\n Visible text\n Hidden text\n"
console.log(box.innerText);
// "Visible text"
.textContent returns every bit of text in the element exactly as it exists in the DOM, whitespace, line breaks, and all, including text inside elements hidden with CSS. .innerText returns only what a user would actually see rendered on the page: it skips hidden text, and it collapses whitespace the way the browser would actually display it.
That difference comes at a cost. To know what’s “actually visible,” .innerText has to ask the browser to calculate the page’s layout, which is slow if you read it repeatedly (say, inside a loop). .textContent never needs layout information, so it’s faster, and it’s also the older, more universally standardized property of the two.
Default to .textContent for reading or writing text, the same guidance as above. Reach for .innerText only in the rare case where you specifically need “what’s visible to the user,” hidden text excluded.
⚠️ A note on verification: every other example in this course was run against a real DOM and checked against its actual output.
.innerTextdepends on the browser actually calculating layout (which elements are hidden, how text wraps), something the tool used to verify this course’s examples doesn’t simulate. The behavior described above is accurate and well documented, but this is the one property in this module you should double check in an actual browser console rather than take purely on this page’s word.
Reading and Setting Attributes
.setAttribute(name, value) and .getAttribute(name) work on any HTML attribute.
const link = document.createElement("a");
link.setAttribute("href", "https://example.com");
console.log(link.getAttribute("href"));
// https://example.com
console.log(link.hasAttribute("target"));
// false
Many common attributes also have a matching property you can read or set directly, link.href works the same as link.getAttribute("href") for most standard attributes.
Working with Classes: .classList
.classList gives you methods for adding, removing, and checking CSS classes, without manually editing a string of class names yourself.
const box2 = document.createElement("div");
box2.classList.add("card", "highlighted");
console.log(box2.className);
// card highlighted
box2.classList.remove("highlighted");
console.log(box2.className);
// card
console.log(box2.classList.contains("card"));
// true
.toggle() adds a class if it’s missing, and removes it if it’s already there, useful for things like a “show more” button that flips a state back and forth.
box2.classList.toggle("hidden");
console.log(box2.className);
// card hidden
box2.classList.toggle("hidden");
console.log(box2.className);
// card
Changing Styles Directly
.style lets you set individual CSS properties from JavaScript. Property names use camelCase instead of the hyphenated CSS form (fontSize, not font-size).
const box3 = document.createElement("div");
box3.style.color = "blue";
box3.style.fontSize = "20px";
console.log(box3.getAttribute("style"));
// color: blue; font-size: 20px;
Reach for .style for one-off changes. For anything more than a couple of properties, adding or removing a CSS class with .classList and defining the styles in a stylesheet keeps your styling in one place instead of scattered through JavaScript.
Try It
- Create a new
<li>element, set its.textContentto a word of your choice, and append it to an existing<ul>. - Create a
<div>, try setting the same string both ways, once with.textContentand once with.innerHTML, and compare.innerHTMLafter each to see the difference. - Create an
<img>element and use.setAttribute()to give it asrcand analt. Confirm both with.getAttribute(). - Create a
<div>, add two classes with.classList.add(), remove one with.classList.remove(), and log.classNameafter each step. - Given a
<div>containing some visible text and a<span style="display: none">with hidden text inside it, log both.textContentand.innerTextand compare what each one includes.
Recap
document.createElement()makes a new element..appendChild()or.append()attaches it to the page..textContenttreats its value as plain text and is always safe..innerHTMLparses its value as HTML and is a security risk with untrusted input..innerTextlooks similar to.textContentbut only returns visible, rendered text, and is slower since it requires layout information. Default to.textContent..setAttribute()/.getAttribute()read and write any HTML attribute..classList.add(),.remove(),.contains(), and.toggle()manage an element’s CSS classes without string manipulation..stylesets individual CSS properties directly, using camelCase property names.
Next lesson: moving around the page structure with DOM traversal.