Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

เหตุการณ์โฟกัส (Focus Events) ใน JavaScript

Focus Events เกิดขึ้นเมื่อองค์ประกอบได้รับหรือสูญเสียโฟกัส (Focus) มี 4 เหตุการณ์หลัก ซึ่งแตกต่างกันที่ "การกระจาย" (Propagation) และ "ลำดับการยิง"

ประเภท Focus Events

เหตุการณ์คำอธิบายBubbles
focusเกิดเมื่อองค์ประกอบได้รับโฟกัส❌ ไม่
blurเกิดเมื่อองค์ประกอบสูญเสียโฟกัส❌ ไม่
focusinเกิดก่อน focus (เมื่อกำลังจะได้รับโฟกัส)✅ ใช่
focusoutเกิดก่อน blur (เมื่อกำลังจะสูญเสียโฟกัส)✅ ใช่

ลำดับการยิง (Event Order)

// เมื่อคลิกจาก Input A ไป Input B:
1. focusout (บน A) - Bubbles
2. blur (บน A) - No bubble
3. focusin (บน B) - Bubbles
4. focus (บน B) - No bubble

ตัวอย่าง: เปลี่ยนสีพื้นหลังเมื่อ Focus

<!DOCTYPE html>
<html lang="th">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Focus Events</title>
    <style>
        body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
        input { padding: 10px; margin: 10px 0; display: block; width: 300px; font-size: 16px; }
        .info { background: #e7f3ff; padding: 15px; border-radius: 4px; margin: 10px 0; }
    </style>
</head>
<body>
    <h1>Focus Events ใน JavaScript</h1>
    <form id="form">
        <input type="text" placeholder="Username" autocomplete="username">
        <input class="passW" type="password" placeholder="Password" autocomplete="current-password">
    </form>
    <div class="info">ลองคลิกที่ช่อง Password เพื่อดู focus event ทำงาน</div>

    <script>
        let passEle = document.querySelector(".passW");
        
        // focus - ไม่ Bubble
        passEle.addEventListener("focus", () => {
            passEle.style.backgroundColor = "#90EE90"; // เขียวอ่อน
            console.log("focus event fired");
        });
        
        // blur - ไม่ Bubble
        passEle.addEventListener("blur", () => {
            passEle.style.backgroundColor = ""; // คืนค่าเดิม
            console.log("blur event fired");
        });
        
        // focusin - Bubble (จับได้ที่ Parent)
        document.getElementById("form").addEventListener("focusin", (e) => {
            console.log("focusin on:", e.target.tagName, e.target.type);
        }, true); // ใช้ Capture phase เพราะ focusin ไม่ bubble ปกติในบาง Browser
        
        // focusout - Bubble
        document.getElementById("form").addEventListener("focusout", (e) => {
            console.log("focusout from:", e.target.tagName, e.target.type);
        });
    </script>
</body>
</html>

ผลลัพธ์

คลิกช่อง Password → พื้นหลังสีเขียวอ่อน (focus)
คลิกนอกพื้นที่/Tab ไปช่องอื่น → พื้นหลังคืนค่าเดิม (blur)

การใช้งานจริง (Use Cases)

1. Validation แบบ Real-time (on blur)

emailInput.addEventListener("blur", () => {
    if (emailInput.value && !isValidEmail(emailInput.value)) {
        showError(emailInput, "รูปแบบอีเมลไม่ถูกต้อง");
    } else {
        clearError(emailInput);
    }
});

2. Floating Label Pattern

input.addEventListener("focus", () => label.classList.add("active"));
input.addEventListener("blur", () => {
    if (!input.value) label.classList.remove("active");
});

3. Keyboard Navigation (Focus Management)

// กด Escape ปิด Modal และคืน Focus กลับ Trigger Button
modal.addEventListener("keydown", (e) => {
    if (e.key === "Escape") {
        closeModal();
        triggerButton.focus(); // คืน Focus
    }
});

// Trap Focus ภายใน Modal
modal.addEventListener("focusin", (e) => {
    if (!modal.contains(e.target)) {
        firstFocusableElement.focus();
    }
});

4. ใช้ focusin/focusout สำหรับ Event Delegation

// จับ Focus ทุก Input ใน Form ได้ที่ Form Element
form.addEventListener("focusin", (e) => {
    if (e.target.matches("input, textarea, select")) {
        e.target.style.borderColor = "#667eea";
    }
});

form.addEventListener("focusout", (e) => {
    if (e.target.matches("input, textarea, select")) {
        e.target.style.borderColor = "#ccc";
    }
});

Focus และ Accessibility

  • ใช้ tabindex ควบคุมลำดับ Tab Order
  • อย่าเอา Outline เริ่มต้นออก (outline: none) ถ้าไม่มี Style 替代
  • ใช้ :focus-visible CSS สำหรับแสดง Focus เฉพาะ Keyboard Navigation
  • autofocus attribute - โฟกัสอัตโนมัติตอนโหลด (ใช้แค่ 1 ตัวต่อหน้า)