Skillversitas

DOM Manipulation

The DOM (Document Object Model) is a tree representation of the HTML that can be manipulated with JavaScript.

Selecting Elements

getElementById

<div id="header">Hello</div>
<script>
    const element = document.getElementById('header');
    element.textContent = 'World';
</script>

querySelector

<div class="container">
    <p class="text">First</p>
    <p class="text">Second</p>
</div>
<script>
    const first = document.querySelector('.text');
    const all = document.querySelectorAll('.text');
</script>

Changing Content

textContent vs innerHTML

<div id="content"></div>
<script>
    const el = document.getElementById('content');

    // textContent - safe, text only
    el.textContent = '<b>Bold</b>'; // Displays literal text

    // innerHTML - prone to XSS
    el.innerHTML = '<b>Bold</b>'; // Displays bold
</script>

insertAdjacentHTML

<div id="list">
    <h2>Student List</h2>
</div>
<script>
    const list = document.getElementById('list');
    list.insertAdjacentHTML('beforeend', 
        '<p>John Smith</p><p>Sarah Lee</p>');
</script>

Changing Styles

<div id="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
    const box = document.getElementById('box');

    // Direct style
    box.style.backgroundColor = 'blue';
    box.style.borderRadius = '8px';

    // ClassList
    box.classList.add('active');
    box.classList.remove('active');
    box.classList.toggle('active');
    box.classList.contains('active'); // true/false
</script>

Changing Attributes

<a id="link" href="https://google.com">Google</a>
<script>
    const link = document.getElementById('link');

    link.setAttribute('target', '_blank');
    link.getAttribute('href');
    link.removeAttribute('target');
</script>

Creating and Removing Elements

<ul id="list"></ul>
<button onclick="addItem()">Add</button>
<script>
    function addItem() {
        const li = document.createElement('li');
        li.textContent = 'New item';
        document.getElementById('list').appendChild(li);
    }

    function removeItem() {
        const li = document.querySelector('li');
        li.remove();
    }
</script>

Case Study: Interactive Campus Dashboard

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Campus Dashboard</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', sans-serif;
            background: #f0f2f5;
            color: #333;
        }

        .header {
            background: #1e3a5f;
            color: white;
            padding: 1rem 2rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .main {
            padding: 2rem;
            max-width: 1200px;
            margin: 0 auto;
        }

        .stats {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 1.5rem;
            margin-bottom: 2rem;
        }

        .stat-card {
            background: white;
            padding: 1.5rem;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            text-align: center;
        }

        .stat-card h3 {
            color: #666;
            font-size: 0.9rem;
            margin-bottom: 0.5rem;
        }

        .stat-card .value {
            font-size: 2rem;
            font-weight: bold;
            color: #1e3a5f;
        }

        .content {
            display: grid;
            grid-template-columns: 2fr 1fr;
            gap: 1.5rem;
        }

        .card {
            background: white;
            padding: 1.5rem;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        .card h2 {
            color: #1e3a5f;
            margin-bottom: 1rem;
            padding-bottom: 0.5rem;
            border-bottom: 2px solid #eee;
        }

        .btn {
            padding: 0.5rem 1rem;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 0.5rem;
            margin-bottom: 0.5rem;
        }

        .btn:hover {
            background: #2980b9;
        }

        .btn-danger {
            background: #e74c3c;
        }

        .btn-danger:hover {
            background: #c0392b;
        }

        .btn-success {
            background: #2ecc71;
        }

        .btn-success:hover {
            background: #27ae60;
        }

        .student-list {
            list-style: none;
        }

        .student-item {
            padding: 0.8rem;
            border-bottom: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .student-item:last-child {
            border-bottom: none;
        }

        .student-item .name {
            font-weight: 500;
        }

        .student-item .studentId {
            color: #666;
            font-size: 0.85rem;
        }

        .student-item .actions {
            display: flex;
            gap: 0.5rem;
        }

        .student-item .btn {
            padding: 0.3rem 0.6rem;
            font-size: 0.8rem;
        }

        .add-form {
            margin-top: 1rem;
            padding-top: 1rem;
            border-top: 2px solid #eee;
        }

        .form-row {
            display: flex;
            gap: 0.5rem;
            margin-bottom: 0.5rem;
        }

        .form-row input {
            flex: 1;
            padding: 0.5rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        .activity-list {
            list-style: none;
        }

        .activity-item {
            padding: 0.8rem;
            border-left: 3px solid #3498db;
            margin-bottom: 0.8rem;
            background: #f8f9fa;
            border-radius: 0 4px 4px 0;
        }

        .activity-item .time {
            font-size: 0.75rem;
            color: #666;
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            background: #2ecc71;
            color: white;
            padding: 1rem 1.5rem;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            transform: translateX(120%);
            transition: transform 0.3s;
        }

        .notification.show {
            transform: translateX(0);
        }

        .notification.error {
            background: #e74c3c;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>Campus Dashboard</h1>
        <div id="clock"></div>
    </div>

    <div class="main">
        <div class="stats">
            <div class="stat-card">
                <h3>Total Student</h3>
                <div class="value" id="totalStudents">0</div>
            </div>
            <div class="stat-card">
                <h3>Lecturer</h3>
                <div class="value" id="totalLecturers">0</div>
            </div>
            <div class="stat-card">
                <h3>Active Class</h3>
                <div class="value" id="totalClasses">0</div>
            </div>
            <div class="stat-card">
                <h3>Average GPA</h3>
                <div class="value" id="avgGPA">0.00</div>
            </div>
        </div>

        <div class="content">
            <div class="card">
                <h2>Student List</h2>
                <div class="actions" style="margin-bottom: 1rem;">
                    <button class="btn btn-success" onclick="addRandomStudent()">Add Random</button>
                    <button class="btn btn-danger" onclick="clearStudents()">Delete All</button>
                </div>

                <ul class="student-list" id="studentList"></ul>

                <div class="add-form">
                    <h3 style="margin-bottom: 0.5rem; font-size: 0.9rem;">Manual Add</h3>
                    <div class="form-row">
                        <input type="text" id="inputName" placeholder="Name">
                        <input type="text" id="inputStudentId" placeholder="StudentID">
                        <button class="btn" onclick="addManualStudent()">Add</button>
                    </div>
                </div>
            </div>

            <div class="card">
                <h2>Latest Activity</h2>
                <ul class="activity-list" id="activityList"></ul>
            </div>
        </div>
    </div>

    <div class="notification" id="notification"></div>

    <script>
        // Data
        let students = [
            { name: 'John Smith', studentId: '1234567890' },
            { name: 'Sarah Lee', studentId: '0987654321' },
            { name: 'Robert Brown', studentId: '1122334455' }
        ];

        let activities = [
            { text: 'John enrolled in a new class', time: '2 minutes ago' },
            { text: 'Sarah submitted an assignment', time: '5 minutes ago' }
        ];

        // Render functions
        function renderStudents() {
            const list = document.getElementById('studentList');
            list.innerHTML = '';

            students.forEach((student, index) => {
                const li = document.createElement('li');
                li.className = 'student-item';
                li.innerHTML = `
                    <div>
                        <div class="name">${student.name}</div>
                        <div class="studentId">${student.studentId}</div>
                    </div>
                    <div class="actions">
                        <button class="btn" onclick="editStudent(${index})">Edit</button>
                        <button class="btn btn-danger" onclick="deleteStudent(${index})">Delete</button>
                    </div>
                `;
                list.appendChild(li);
            });

            updateStats();
        }

        function renderActivities() {
            const list = document.getElementById('activityList');
            list.innerHTML = '';

            activities.slice(0, 5).forEach(activity => {
                const li = document.createElement('li');
                li.className = 'activity-item';
                li.innerHTML = `
                    <div>${activity.text}</div>
                    <div class="time">${activity.time}</div>
                `;
                list.appendChild(li);
            });
        }

        function updateStats() {
            document.getElementById('totalStudents').textContent = students.length;
            document.getElementById('totalLecturers').textContent = '25';
            document.getElementById('totalClasses').textContent = '12';
            document.getElementById('avgGPA').textContent = '3.45';
        }

        // CRUD Operations
        function addRandomStudent() {
            const names = ['Mike Johnson', 'Dana Lewis', 'Frank Nelson', 'Grace Parker'];
            const randomName = names[Math.floor(Math.random() * names.length)];
            const randomStudentId = Math.floor(1000000000 + Math.random() * 9000000000).toString();

            students.push({ name: randomName, studentId: randomStudentId });
            addActivity(`${randomName} added`);
            renderStudents();
            showNotification(`${randomName} successfully added!`);
        }

        function addManualStudent() {
            const name = document.getElementById('inputName').value.trim();
            const studentId = document.getElementById('inputStudentId').value.trim();

            if (name && studentId) {
                students.push({ name, studentId });
                addActivity(`${name} added manually`);
                renderStudents();
                showNotification(`${name} successfully added!`);

                document.getElementById('inputName').value = '';
                document.getElementById('inputStudentId').value = '';
            } else {
                showNotification('Name and StudentID are required!', true);
            }
        }

        function editStudent(index) {
            const student = students[index];
            const newName = prompt('New name:', student.name);
            const newNim = prompt('New StudentID:', student.studentId);

            if (newName && newNim) {
                students[index] = { name: newName, studentId: newNim };
                addActivity(`${student.name} updated`);
                renderStudents();
                showNotification('Data updated successfully!');
            }
        }

        function deleteStudent(index) {
            const student = students[index];
            if (confirm(`Delete ${student.name}?`)) {
                students.splice(index, 1);
                addActivity(`${student.name} removed`);
                renderStudents();
                showNotification(`${student.name} successfully deleted!`);
            }
        }

        function clearStudents() {
            if (confirm('Delete all students?')) {
                students = [];
                addActivity('All students removed');
                renderStudents();
                showNotification('All data deleted successfully!');
            }
        }

        function addActivity(text) {
            activities.unshift({ text, time: 'Just now' });
            renderActivities();
        }

        function showNotification(message, isError = false) {
            const notif = document.getElementById('notification');
            notif.textContent = message;
            notif.className = isError ? 'notification error show' : 'notification show';

            setTimeout(() => {
                notif.classList.remove('show');
            }, 3000);
        }

        // Clock
        function updateClock() {
            const now = new Date();
            document.getElementById('clock').textContent = 
                now.toLocaleTimeString('id-ID') + ' - ' + 
                now.toLocaleDateString('id-ID', { weekday: 'long' });
        }

        // Initialize
        renderStudents();
        renderActivities();
        updateClock();
        setInterval(updateClock, 1000);
    </script>
</body>
</html>

Everyday Case Study

Case: A developer needs to toggle section visibility.

<button onclick="toggleSection()">Toggle Section</button>
<div id="section" style="display: none;">
    <p>This is content that can be toggled.</p>
</div>

<script>
    function toggleSection() {
        const section = document.getElementById('section');
        if (section.style.display === 'none') {
            section.style.display = 'block';
        } else {
            section.style.display = 'none';
        }
    }
</script>

Best Practices

  1. Batch DOM updates: Reduce reflow
  2. DocumentFragment: For multiple inserts
  3. Event delegation: More efficient
  4. querySelector vs getElementById: Pick the one that fits your needs
  5. Don't access the DOM repeatedly: Cache selectors

Summary

DOM manipulation enables dynamic changes to HTML pages. Use it efficiently for optimal performance.