Skillversitas

31. Changing HTML Content

After selecting a DOM element, you can read and modify its content. JavaScript provides several ways to change HTML content, text, and element structure.

Code Example

Reading Content

const paragraph = document.querySelector("p");

// textContent — raw text, including text inside script/style
console.log(paragraph.textContent);

// innerText — visible text only (affected by CSS)
console.log(paragraph.innerText);

// innerHTML — the full HTML including tags
console.log(paragraph.innerHTML);

// Difference between textContent and innerText:
// textContent: fast, grabs all text (including display:none)
// innerText: slower, only visible text

Changing Content

const info = document.getElementById("info-student");

// textContent — safe from XSS, text only
info.textContent = "GPA: 3.75";

// innerHTML — you can insert HTML, but WATCH OUT FOR XSS!
info.innerHTML = "<strong>GPA: 3.75</strong> (Cum Laude)";

// Never do this with user input!
const userInput = '<script>alert("XSS")</script>';
// info.innerHTML = userInput; // DANGEROUS!
info.textContent = userInput; // Safe — displayed as plain text

insertAdjacentHTML

const container = document.getElementById("list");

// beforebegin — before the container (previous sibling)
container.insertAdjacentHTML("beforebegin", "<p>Before the container</p>");

// afterbegin — inside the container, before the first child
container.insertAdjacentHTML("afterbegin", "<li>New Item (Top)</li>");

// beforeend — inside the container, after the last child
container.insertAdjacentHTML("beforeend", "<li>New Item (Bottom)</li>");

// afterend — after the container (next sibling)
container.insertAdjacentHTML("afterend", "<p>Sesudah container</p>");
beforebegin  ┌─────────────────────┐
              │     container       │
afterbegin   │  ┌───────────────┐  │
              │  │ first child   │  │
              │  └───────────────┘  │
              │  ┌───────────────┐  │
              │  │ last child    │  │
beforeend    │  └───────────────┘  │
              └─────────────────────┘
afterend     ┌─────────────────────┐
              │  next element      │
              └─────────────────────┘

insertAdjacentElement

const container = document.getElementById("studentList");

// Create a new element
const item = document.createElement("div");
item.className = "student-item";
item.textContent = "Mike Johnson";

// Insert at a specific position
container.insertAdjacentElement("afterbegin", item);

// Keep a reference for further manipulation
const firstItem = container.querySelector(".student-item");
firstItem.style.backgroundColor = "#e8f5e9";

outerHTML

const card = document.querySelector(".card");

// Read outerHTML — the entire element including opening/closing tags
console.log(card.outerHTML);
// '<div class="card"><h3>Title</h3><p>Content</p></div>'

// Replace the entire element with new HTML
card.outerHTML = `
  <div class="card highlight">
    <h3>New Title</h3>
    <p>New content</p>
  </div>
`;
// the card reference is now INVALID because the element has been replaced

Creating New Elements

// createElement — create a new element
const card = document.createElement("div");
card.className = "card";
card.id = "card-1";
card.innerHTML = `
  <h3>Card title</h3>
  <p>Card content</p>
`;

// Add to the DOM with various methods
const container = document.getElementById("content");

// appendChild — add at the end
container.appendChild(card);

// prepend — add at the beginning
const header = document.createElement("h2");
header.textContent = "Student List";
container.prepend(header);

// insertBefore — add before a specific element
const ref = container.querySelector(".card");
const newCard = document.createElement("div");
newCard.className = "card";
newCard.textContent = "Card New";
container.insertBefore(newCard, ref);

// replaceChildren — replace all children
container.replaceChildren(
  document.createElement("h2"),
  document.createElement("p")
);

// remove — delete an element
card.remove();

// cloneNode — duplicate an element
const original = document.querySelector(".card");
const clone = original.cloneNode(true); // true = deep clone
container.appendChild(clone);

Example: Dynamic Table for Student Data

function createStudentTable(students) {
  const container = document.getElementById("tableContainer");

  // Create the table element
  const table = document.createElement("table");
  table.className = "table-student";

  // Create thead
  const thead = document.createElement("thead");
  thead.innerHTML = `
    <tr>
      <th>No</th>
      <th>StudentID</th>
      <th>Name</th>
      <th>Major</th>
      <th>GPA</th>
      <th>Classification</th>
      <th>Action</th>
    </tr>
  `;
  table.appendChild(thead);

  // Create tbody
  const tbody = document.createElement("tbody");

  students.forEach((student, index) => {
    const tr = document.createElement("tr");

    // Calculate the classification
    let classification = "Satisfactory";
    if (student.gpa >= 3.5) classification = "Cum Laude";
    else if (student.gpa >= 3.0) classification = "Excellent";
    else if (student.gpa >= 2.5) classification = "Good";

    // Color based on classification
    let color = "#f44336";
    if (student.gpa >= 3.5) color = "#4caf50";
    else if (student.gpa >= 3.0) color = "#2196f3";
    else if (student.gpa >= 2.5) color = "#ff9800";

    tr.innerHTML = `
      <td>${index + 1}</td>
      <td>${student.studentId}</td>
      <td>${student.name}</td>
      <td>${student.major}</td>
      <td>${student.gpa.toFixed(2)}</td>
      <td style="color: ${color}; font-weight: bold">${classification}</td>
      <td>
        <button class="btn-detail" data-studentId="${student.studentId}">Details</button>
        <button class="btn-delete" data-studentId="${student.studentId}">Delete</button>
      </td>
    `;

    tbody.appendChild(tr);
  });

  table.appendChild(tbody);

  // Add a summary row
  const total = students.length;
  const average = total > 0
    ? (students.reduce((sum, m) => sum + m.gpa, 0) / total).toFixed(2)
    : "0.00";

  const tfoot = document.createElement("tfoot");
  tfoot.innerHTML = `
    <tr>
      <td colspan="4"><strong>Total: ${total} student</strong></td>
      <td><strong>${average}</strong></td>
      <td colspan="2"></td>
    </tr>
  `;
  table.appendChild(tfoot);

  // Render
  container.innerHTML = "";
  container.appendChild(table);

  // Event delegation for the buttons
  container.addEventListener("click", (event) => {
    const btnDetail = event.target.closest(".btn-detail");
    const btnDelete = event.target.closest(".btn-delete");

    if (btnDetail) {
      const studentId = btnDetail.dataset.studentId;
      const student = students.find(m => m.studentId === studentId);
      alert(`Details: ${student.name} - GPA ${student.gpa}`);
    }

    if (btnDelete) {
      const studentId = btnDelete.dataset.studentId;
      const index = students.findIndex(m => m.studentId === studentId);
      if (index > -1) {
        students.splice(index, 1);
        createStudentTable(students); // re-render
      }
    }
  });
}

// Data
const data = [
  { studentId: "2024001", name: "Mike Johnson", major: "CS", gpa: 3.75 },
  { studentId: "2024002", name: "Anna Reed", major: "IS", gpa: 3.20 },
  { studentId: "2024003", name: "John Smith", major: "CS", gpa: 2.85 },
  { studentId: "2024004", name: "Amy Lee", major: "SOC", gpa: 3.92 }
];

document.addEventListener("DOMContentLoaded", () => {
  createStudentTable(data);
});

replaceChildren — Modern Replacement

const list = document.getElementById("courseList");

// Replace all content at once (more efficient than innerHTML)
list.replaceChildren(
  createCourseItem("Algorithms", 3),
  createCourseItem("Database", 3),
  createCourseItem("Web Programming", 3)
);

function createCourseItem(name, credits) {
  const li = document.createElement("li");
  li.textContent = `${name} (${credits} credits)`;
  return li;
}

Code Explanation

Method Explanation
textContent Raw text — safe from XSS, fast
innerHTML HTML markup — can be injected, use carefully
innerText Visible text only (slow, affected by CSS)
insertAdjacentHTML Insert HTML at 4 relative positions
insertAdjacentElement Insert an element at 4 relative positions
outerHTML Read/replace the entire element (including its own tags)
createElement Create a new element (not yet in the DOM)
appendChild Add a child at the end of the parent
prepend Add a child at the beginning of the parent
insertBefore Add a child before a reference node
remove Remove an element from the DOM
cloneNode(deep) Duplicate an element (true = deep clone)
replaceChildren Replace all children at once

Performance Tips

✅ Use documentFragment for batch inserts
❌ Avoid repeated innerHTML (causes re-render)
✅ Use textContent when only changing text
❌ Don't append elements one by one in a large loop
// Use a fragment for performance
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const li = document.createElement("li");
  li.textContent = `Item ${i + 1}`;
  fragment.appendChild(li);
}
document.getElementById("list").appendChild(fragment);
// Only 1 reflow, not 100!

Real-World Examples

A student dashboard that updates in real time:

class CampusDashboard {
  constructor(container) {
    this.container = document.querySelector(container);
    this.data = [];
  }

  render() {
    const fragment = document.createDocumentFragment();

    // Header
    const header = document.createElement("div");
    header.className = "dashboard-header";
    header.innerHTML = `
      <h2>Dashboard Academic</h2>
      <p>Update: ${new Date().toLocaleTimeString("en-US")}</p>
    `;
    fragment.appendChild(header);

    // Stats cards
    const stats = this.calculateStatistics();
    const statsHTML = document.createElement("div");
    statsHTML.className = "stats-grid";
    statsHTML.innerHTML = `
      <div class="stat-card">
        <h3>${stats.total}</h3>
        <p>Total Student</p>
      </div>
      <div class="stat-card">
        <h3>${stats.averageGPA}</h3>
        <p>Average GPA</p>
      </div>
      <div class="stat-card">
        <h3>${stats.cumLaude}</h3>
        <p>Cum Laude</p>
      </div>
    `;
    fragment.appendChild(statsHTML);

    // Table
    const table = document.createElement("table");
    table.className = "data-table";
    table.innerHTML = `
      <thead>
        <tr>
          <th>StudentID</th><th>Name</th><th>GPA</th><th>Status</th>
        </tr>
      </thead>
      <tbody>
        ${this.data.map(student => `
          <tr>
            <td>${student.studentId}</td>
            <td>${student.name}</td>
            <td>${student.gpa.toFixed(2)}</td>
            <td>${student.gpa >= 3.5 ? "🏆 Cum Laude" : "Active"}</td>
          </tr>
        `).join("")}
      </tbody>
    `;
    fragment.appendChild(table);

    // Render at once
    this.container.replaceChildren(fragment);
  }

  calculateStatistics() {
    const total = this.data.length;
    const averageGPA = total > 0
      ? (this.data.reduce((sum, m) => sum + m.gpa, 0) / total).toFixed(2)
      : "0.00";
    const cumLaude = this.data.filter(m => m.gpa >= 3.5).length;
    return { total, averageGPA, cumLaude };
  }

  updateData(newData) {
    this.data = newData;
    this.render();
  }
}

// Usage
document.addEventListener("DOMContentLoaded", () => {
  const dashboard = new CampusDashboard("#dashboard");

  dashboard.updateData([
    { studentId: "2024001", name: "Mike", gpa: 3.75 },
    { studentId: "2024002", name: "Anna", gpa: 3.20 },
    { studentId: "2024003", name: "John", gpa: 2.85 },
    { studentId: "2024004", name: "Amy", gpa: 3.92 }
  ]);
});

Conclusion

  • Use textContent for safe text, and innerHTML for HTML (watch out for XSS).
  • insertAdjacentHTML lets you insert HTML at 4 relative positions without removing existing content.
  • createElement + appendChild is the safest way to build elements.
  • Use documentFragment for batch inserts to boost performance.
  • replaceChildren is the modern way to replace all children at once.