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

วิธีซ่อน DIV เมื่อคลิกนอกพื้นที่ด้วย JavaScript

ใช้ Event Delegation บน document ตรวจสอบว่า Click target ไม่ใช่ Element ที่ต้องการ แล้วซ่อนมัน

ตัวอย่างจากบทความ

<!DOCTYPE html>
<html>
  <body>
    <script>
      window.onload = function(){
        var hideMe = document.getElementById('hideMe');
        document.onclick = function(e){
          if(e.target.id !== 'hideMe'){
            hideMe.style.display = 'none';
          }
        };
      };
    </script>
    <div id="hideMe">คลิกนอกพื้นที่นี้เพื่อซ่อน</div>
  </body>
</html>

ปัญหาของตัวอย่างต้นฉบับ

  • ใช้ document.onclick ทับ handler อื่นได้
  • ไม่ตรวจสอบ child elements (คลิกลูกก็ซ่อน)
  • ไม่มีการ cleanup

วิธีที่ดีกว่า (Modern Approach)

1. ใช้ addEventListener + contains()

const dropdown = document.getElementById('dropdown');

document.addEventListener('click', function(event) {
  // ถ้าคลิกนอก dropdown และไม่ใช่ dropdown เอง
  if (!dropdown.contains(event.target)) {
    dropdown.style.display = 'none';
  }
});

2. รองรับหลาย Element (Dropdown หลายตัว)

function setupClickOutside(selector, callback) {
  document.addEventListener('click', function(event) {
    const elements = document.querySelectorAll(selector);
    let clickedInside = false;
    
    elements.forEach(el => {
      if (el.contains(event.target)) {
        clickedInside = true;
      }
    });
    
    if (!clickedInside) {
      callback(event);
    }
  });
}

// ใช้งาน
setupClickOutside('.dropdown-menu', () => {
  document.querySelectorAll('.dropdown-menu').forEach(el => {
    el.style.display = 'none';
  });
});

3. ใช้ Custom Hook (React)

// useClickOutside.js
import { useEffect } from 'react';
import { useRef } from 'react';

export function useClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    
    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);
    
    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
    };
  }, [ref, handler]);
}

// ใช้งาน
function Dropdown() {
  const ref = useRef(null);
  const [open, setOpen] = useState(false);
  
  useClickOutside(ref, () => setOpen(false));
  
  return (
    
{open &&
Content
}
); }

4. ใช้ Focus Management (Accessibility)

// สำหรับ Modal/Dialog - ใช้ focus trap
function trapFocus(element) {
  const focusableElements = element.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusableElements[0];
  const last = focusableElements[focusableElements.length - 1];
  
  element.addEventListener('keydown', (e) => {
    if (e.key !== 'Tab') return;
    
    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first.focus();
    }
  });
}

Event Target vs CurrentTarget

document.addEventListener('click', (e) => {
  e.target;        // Element ที่ถูกคลิกจริง (ลึกที่สุด)
  e.currentTarget; // Element ที่ attach listener (document)
  this;            // เหมือน currentTarget (ใน regular function)
});

สรุป Best Practices

  • ใช้ addEventListener แทน onclick property
  • ใช้ element.contains(event.target) ตรวจสอบการคลิกภายใน
  • Cleanup listener เมื่อไม่ต้องการ (return function ใน useEffect/componentWillUnmount)
  • พิจารณา Accessibility (Focus trap, ARIA attributes)
  • ใช้ mousedown หรือ touchstart นอกจาก click สำหรับ mobile