先看這個固定例子
固定 sample 事件設為可冒泡、可取消。child 呼叫 preventDefault 後,parent 是否仍收到?此自訂事件沒有導航或送表單的預設動作。
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<section id="parent"><p id="child">本地事件目標</p></section><output id="result"></output>
<script>const trace=[];const child=document.querySelector('#child');child.addEventListener('sample',event=>{trace.push('child');event.preventDefault();});document.querySelector('#parent').addEventListener('sample',event=>trace.push('parent:'+event.defaultPrevented));
const returned=child.dispatchEvent(new Event('sample',{bubbles:true,cancelable:true}));const result={trace,returned};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>{"trace":["child","parent:true"],"returned":false}| 核對項目 | 所給輸入或動作 | 結果 |
|---|---|---|
| child | preventDefault() | defaultPrevented: true |
| parent | 仍冒泡至 parent | parent:true |
| dispatchEvent | cancelable: true | false |
原生機制怎樣處理
可取消事件的 preventDefault 會設定取消旗標,因此 dispatchEvent 傳回 false;它沒有設定停止傳播。stopPropagation 擋住後續祖先,卻不會自行取消。事件若不可取消,preventDefault 也不能改成已取消。
比較另一寫法
只用 stopPropagation,parent 記錄不見,但 dispatch 仍傳回 true。本例自訂事件不示範任何真實瀏覽器預設動作。
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<section id="parent"><p id="child">本地事件目標</p></section><output id="result"></output>
<script>const trace=[];const child=document.querySelector('#child');child.addEventListener('sample',event=>{trace.push('child');event.stopPropagation();});document.querySelector('#parent').addEventListener('sample',event=>trace.push('parent:'+event.defaultPrevented));
const returned=child.dispatchEvent(new Event('sample',{bubbles:true,cancelable:true}));const result={trace,returned};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>{"trace":["child"],"returned":true}試清邊界
cancelable=false、bubbles=true,preventDefault 還能改甚麼?
查看參考答案
defaultPrevented 仍是假;parent 會跑,dispatch 傳回 true。傳播旗標仍是另一回事。
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<section id="parent"><p id="child">本地事件目標</p></section><output id="result"></output>
<script>const trace=[];const child=document.querySelector('#child');child.addEventListener('sample',event=>{event.preventDefault();trace.push('child:'+event.defaultPrevented);});document.querySelector('#parent').addEventListener('sample',event=>trace.push('parent:'+event.defaultPrevented));
const returned=child.dispatchEvent(new Event('sample',{bubbles:true,cancelable:false}));const result={trace,returned};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>{"trace":["child:false","parent:false"],"returned":true}