Hai việc hoàn toàn khác nhau: một cái chặn hành vi mặc định của browser, một cái chặn đường đi của sự kiện.
javascript
form.addEventListener('submit', (e) => {
e.preventDefault() // không tải lại trang; sự kiện VẪN bubble lên
submitViaFetch()
})
inner.addEventListener('click', (e) => {
e.stopPropagation() // cha không nhận được click; hành vi mặc định VẪN chạy
})preventDefault() | stopPropagation() | |
|---|---|---|
| Chặn | hành vi mặc định (submit, theo link, tick checkbox) | bubbling / capturing |
| Sự kiện còn đi tiếp | có | không |
| Kiểm tra được | e.defaultPrevented | — |
Còn stopImmediatePropagation() — chặn cả các listener khác trên chính element đó:
javascript
btn.addEventListener('click', (e) => e.stopImmediatePropagation())
btn.addEventListener('click', () => console.log('không bao giờ chạy'))Lưu ý 1: stopPropagation() phá event delegation — listener ở cha không còn nhận được sự kiện, kể cả code của thư viện khác. Đây là nguyên nhân của bug "dropdown không đóng khi bấm ra ngoài".
Lưu ý 2: listener passive không gọi được preventDefault(). Trình duyệt mặc định coi touchstart/wheel trên window là passive để cuộn mượt:
javascript
window.addEventListener('wheel', handler, { passive: false }) // phải khai báo rõChốt: cần chặn hành vi thì preventDefault.
Cần cô lập một vùng thì cân nhắc kỹ trước khi stopPropagation — thường kiểm tra e.target là đủ và ít side effect hơn.