CodingNic

The DOM

Creating and Modifying Elements

The DOM 25 min read

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 .textContent and .innerHTML
  • Explain how .innerText differs 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:

text
<div id="container"></div>
javascript
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:

javascript
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.

javascript
const box = document.createElement("div");
box.textContent = "<strong>bold</strong>";
console.log(box.innerHTML);
// &lt;strong&gt;bold&lt;/strong&gt;

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).

javascript
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.

text
<div id="box">
  Visible text
  <span style="display: none">Hidden text</span>
</div>
javascript
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. .innerText depends 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.

javascript
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.

javascript
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.

javascript
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).

javascript
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

  1. Create a new <li> element, set its .textContent to a word of your choice, and append it to an existing <ul>.
  2. Create a <div>, try setting the same string both ways, once with .textContent and once with .innerHTML, and compare .innerHTML after each to see the difference.
  3. Create an <img> element and use .setAttribute() to give it a src and an alt. Confirm both with .getAttribute().
  4. Create a <div>, add two classes with .classList.add(), remove one with .classList.remove(), and log .className after each step.
  5. Given a <div> containing some visible text and a <span style="display: none"> with hidden text inside it, log both .textContent and .innerText and compare what each one includes.

Recap

  • document.createElement() makes a new element. .appendChild() or .append() attaches it to the page.
  • .textContent treats its value as plain text and is always safe. .innerHTML parses its value as HTML and is a security risk with untrusted input.
  • .innerText looks similar to .textContent but 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.
  • .style sets individual CSS properties directly, using camelCase property names.

Next lesson: moving around the page structure with DOM traversal.