Skillversitas

Event Handling

Event handling is a way to handle user actions such as clicks, hovers, and inputs.

Event Basics

onclick

<button onclick="alert('Click!')">Click Me</button>

addEventListener

<button id="myBtn">Click Me</button>
<script>
    document.getElementById('myBtn').addEventListener('click', function() {
        alert('Click!');
    });
</script>

Event Types

Mouse Events

<div id="box" style="width: 200px; height: 200px; background: #3498db;">
    Hover me
</div>
<script>
    const box = document.getElementById('box');

    box.addEventListener('mouseover', function() {
        this.style.background = '#e74c3c';
    });

    box.addEventListener('mouseout', function() {
        this.style.background = '#3498db';
    });

    box.addEventListener('click', function() {
        this.style.background = '#2ecc71';
    });
</script>

Keyboard Events

<input type="text" id="keyboard" placeholder="Ketik sesuatu">
<p id="keyOutput"></p>
<script>
    const input = document.getElementById('keyboard');
    const output = document.getElementById('keyOutput');

    input.addEventListener('keydown', function(e) {
        output.textContent = `Key: ${e.key}, Code: ${e.code}`;
    });
</script>

Form Events

<form id="myForm">
    <input type="text" name="name" placeholder="Name">
    <button type="submit">Send</button>
</form>
<script>
    document.getElementById('myForm').addEventListener('submit', function(e) {
        e.preventDefault();
        const formData = new FormData(this);
        console.log('Name:', formData.get('name'));
    });
</script>

Case Study: Interactive Registration Form

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

        body {
            font-family: 'Segoe UI', sans-serif;
            background: #f0f2f5;
            padding: 2rem;
        }

        .container {
            max-width: 600px;
            margin: 0 auto;
        }

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

        h1 {
            color: #1e3a5f;
            margin-bottom: 1.5rem;
            text-align: center;
        }

        .form-group {
            margin-bottom: 1.5rem;
        }

        label {
            display: block;
            margin-bottom: 0.5rem;
            color: #333;
            font-weight: 500;
        }

        input, select {
            width: 100%;
            padding: 0.8rem;
            border: 2px solid #ddd;
            border-radius: 4px;
            font-size: 1rem;
            transition: border-color 0.3s;
        }

        input:focus, select:focus {
            outline: none;
            border-color: #3498db;
        }

        input.valid {
            border-color: #2ecc71;
        }

        input.invalid {
            border-color: #e74c3c;
        }

        .error-message {
            color: #e74c3c;
            font-size: 0.85rem;
            margin-top: 0.5rem;
            display: none;
        }

        .error-message.active {
            display: block;
        }

        .char-count {
            text-align: right;
            font-size: 0.8rem;
            color: #666;
            margin-top: 0.3rem;
        }

        .btn {
            width: 100%;
            padding: 0.8rem;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 1rem;
            cursor: pointer;
        }

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

        .btn:disabled {
            background: #bdc3c7;
            cursor: not-allowed;
        }

        .preview {
            margin-top: 1.5rem;
            padding: 1rem;
            background: #f8f9fa;
            border-radius: 4px;
            display: none;
        }

        .preview.active {
            display: block;
        }

        .preview h3 {
            color: #1e3a5f;
            margin-bottom: 0.5rem;
            font-size: 1rem;
        }

        .preview p {
            margin-bottom: 0.3rem;
        }

        .strength-bar {
            height: 5px;
            background: #e9ecef;
            border-radius: 3px;
            margin-top: 0.5rem;
            overflow: hidden;
        }

        .strength-fill {
            height: 100%;
            width: 0%;
            transition: width 0.3s, background 0.3s;
        }

        .events-log {
            margin-top: 1.5rem;
            padding: 1rem;
            background: #1e3a5f;
            color: #2ecc71;
            border-radius: 4px;
            font-family: monospace;
            font-size: 0.8rem;
            max-height: 150px;
            overflow-y: auto;
        }

        .events-log p {
            margin-bottom: 0.3rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="card">
            <h1>Interactive Registration Form</h1>

            <form id="registrationForm">
                <div class="form-group">
                    <label for="name">Full name</label>
                    <input type="text" id="name" name="name" 
                           placeholder="Enter your full name" required>
                    <div class="char-count"><span id="nameCount">0</span>/50</div>
                    <div class="error-message" id="nameError">Name must be at least 3 characters</div>
                </div>

                <div class="form-group">
                    <label for="email">Email</label>
                    <input type="email" id="email" name="email" 
                           placeholder="[email protected]" required>
                    <div class="error-message" id="emailError">Invalid email</div>
                </div>

                <div class="form-group">
                    <label for="password">Password</label>
                    <input type="password" id="password" name="password" 
                           placeholder="At least 8 characters" required>
                    <div class="strength-bar">
                        <div class="strength-fill" id="strengthFill"></div>
                    </div>
                    <div class="error-message" id="passwordError">Password at least 8 characters</div>
                </div>

                <div class="form-group">
                    <label for="major">Major</label>
                    <select id="major" name="major" required>
                        <option value="">Select Major</option>
                        <option value="cs">Computer Science</option>
                        <option value="si">Information Systems</option>
                        <option value="management">Management/option>
                        <option value="accounting">Accounting/option>
                    </select>
                </div>

                <button type="submit" id="submitBtn" disabled>Register</button>
            </form>

            <div class="preview" id="preview">
                <h3>Preview Data</h3>
                <p><strong>Name:</strong> <span id="previewName">-</span></p>
                <p><strong>Email:</strong> <span id="previewEmail">-</span></p>
                <p><strong>Major:</strong> <span id="previewMajor">-</span></p>
            </div>

            <div class="events-log" id="eventsLog">
                <p>Events Log:</p>
            </div>
        </div>
    </div>

    <script>
        const form = document.getElementById('registrationForm');
        const eventsLog = document.getElementById('eventsLog');
        const submitBtn = document.getElementById('submitBtn');

        // Log events
        function logEvent(message) {
            const time = new Date().toLocaleTimeString('id-ID');
            const p = document.createElement('p');
            p.textContent = `[${time}] ${message}`;
            eventsLog.prepend(p);
        }

        // Name validation
        const nameInput = document.getElementById('name');
        const nameError = document.getElementById('nameError');
        const nameCount = document.getElementById('nameCount');

        nameInput.addEventListener('input', function() {
            const value = this.value;
            nameCount.textContent = value.length;

            if (value.length >= 3) {
                this.classList.remove('invalid');
                this.classList.add('valid');
                nameError.classList.remove('active');
            } else if (value.length > 0) {
                this.classList.remove('valid');
                this.classList.add('invalid');
                nameError.classList.add('active');
            } else {
                this.classList.remove('valid', 'invalid');
                nameError.classList.remove('active');
            }

            updatePreview();
            validateForm();
            logEvent(`Input name: "${value}"`);
        });

        nameInput.addEventListener('focus', function() {
            logEvent('Focus on name input');
        });

        nameInput.addEventListener('blur', function() {
            logEvent('Blur from name input');
        });

        // Email validation
        const emailInput = document.getElementById('email');
        const emailError = document.getElementById('emailError');

        emailInput.addEventListener('input', function() {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

            if (emailRegex.test(this.value)) {
                this.classList.remove('invalid');
                this.classList.add('valid');
                emailError.classList.remove('active');
            } else if (this.value.length > 0) {
                this.classList.remove('valid');
                this.classList.add('invalid');
                emailError.classList.add('active');
            } else {
                this.classList.remove('valid', 'invalid');
                emailError.classList.remove('active');
            }

            updatePreview();
            validateForm();
        });

        // Password strength
        const passwordInput = document.getElementById('password');
        const passwordError = document.getElementById('passwordError');
        const strengthFill = document.getElementById('strengthFill');

        passwordInput.addEventListener('input', function() {
            const password = this.value;
            let strength = 0;

            if (password.length >= 8) strength += 25;
            if (password.match(/[a-z]/)) strength += 25;
            if (password.match(/[A-Z]/)) strength += 25;
            if (password.match(/[0-9]/)) strength += 25;

            strengthFill.style.width = strength + '%';

            if (strength <= 25) {
                strengthFill.style.background = '#e74c3c';
            } else if (strength <= 50) {
                strengthFill.style.background = '#f39c12';
            } else if (strength <= 75) {
                strengthFill.style.background = '#3498db';
            } else {
                strengthFill.style.background = '#2ecc71';
            }

            if (password.length >= 8) {
                this.classList.remove('invalid');
                this.classList.add('valid');
                passwordError.classList.remove('active');
            } else if (password.length > 0) {
                this.classList.remove('valid');
                this.classList.add('invalid');
                passwordError.classList.add('active');
            } else {
                this.classList.remove('valid', 'invalid');
                passwordError.classList.remove('active');
            }

            validateForm();
        });

        // Program change
        const majorSelect = document.getElementById('major');

        majorSelect.addEventListener('change', function() {
            logEvent(`Major selected: ${this.options[this.selectedIndex].text}`);
            updatePreview();
            validateForm();
        });

        // Form submit
        form.addEventListener('submit', function(e) {
            e.preventDefault();
            logEvent('Form disubmit!');

            const formData = new FormData(this);
            let data = {};
            formData.forEach((value, key) => data[key] = value);

            alert('Registration successful!\n\n' + JSON.stringify(data, null, 2));
        });

        // Keyboard events
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                logEvent('Escape key pressed');
            }
        });

        // Helper functions
        function validateForm() {
            const isNameValid = nameInput.value.length >= 3;
            const isEmailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput.value);
            const isPasswordValid = passwordInput.value.length >= 8;
            const isMajorValid = majorSelect.value !== '';

            submitBtn.disabled = !(isNameValid && isEmailValid && isPasswordValid && isMajorValid);
        }

        function updatePreview() {
            const preview = document.getElementById('preview');

            if (nameInput.value || emailInput.value || majorSelect.value) {
                preview.classList.add('active');

                document.getElementById('previewName').textContent = nameInput.value || '-';
                document.getElementById('previewEmail').textContent = emailInput.value || '-';
                document.getElementById('previewMajor').textContent = 
                    majorSelect.value ? majorSelect.options[majorSelect.selectedIndex].text : '-';
            } else {
                preview.classList.remove('active');
            }
        }
    </script>
</body>
</html>

Everyday Case Study

Case: A developer needs to add hover and click effects.

<div class="card" id="card">
    <h3>Hover and Click</h3>
    <p>Seret mouse ke sini</p>
</div>

<script>
    const card = document.getElementById('card');

    card.addEventListener('mouseenter', function() {
        this.style.transform = 'scale(1.05)';
    });

    card.addEventListener('mouseleave', function() {
        this.style.transform = 'scale(1)';
    });

    card.addEventListener('click', function() {
        this.style.background = '#3498db';
    });
</script>

Best Practices

  1. addEventListener: More flexible than onclick
  2. Event delegation: For dynamic elements
  3. preventDefault: To prevent default behavior
  4. stopPropagation: To stop event bubbling
  5. Remove listener: Remove event listeners that are no longer needed

Summary

Event handling allows web applications to respond to user actions. Use addEventListener for more maintainable code.