跳至內容
Astera Technology

按鈕內的文字被按到,仍要找到正確動作

按鈕內層收到點擊,仍要找回所屬操作按鈕。

下載完整個案資料及參考答案 ↓

先看這個固定例子

固定點擊落在預覽按鈕內的 span,監聽器卻掛在 tools。先找最近的操作按鈕,再讀 data-action。

完整頁面內容與固定程式
<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="tools"><button type="button" data-action="preview"><span id="label">預覽原始檔</span></button><p id="plain">一般筆記</p></section><output id="result"></output>
<script>const tools=document.querySelector('#tools');
tools.addEventListener('click',event=>{const button=event.target.closest('button[data-action]');if(button&&tools.contains(button))document.querySelector('#result').textContent=button.dataset.action;});
document.querySelector('#label').click();console.log(document.querySelector('#result').textContent);
</script>
確實的 console 輸出
preview
輸入與原生結果
核對項目所給輸入或動作結果
target#label span沒有 data-action
currentTarget#tools section監聽器所屬
closestbutton[data-action]preview

原生機制怎樣處理

target 是最初接收事件的節點,回呼內的 currentTarget 是掛監聽器的容器。closest 由自己向祖先找,contains 再核按鈕仍在 tools。本例目標確定是 Element;一般回呼應先核這個條件。

比較另一寫法

直接讀 target.dataset.action,span 沒有該屬性,得到 undefined;preview 在外層按鈕。

另開的反例原始碼
<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="tools"><button type="button" data-action="preview"><span id="label">預覽原始檔</span></button></section><output id="result"></output>
<script>let observed;document.querySelector('#tools').addEventListener('click',event=>{observed=event.target.dataset.action;});document.querySelector('#label').click();
const result={actionType:typeof observed};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
反例輸出
{"actionType":"undefined"}

試清邊界

按一般段落會否寫入操作?直接按按鈕又是否有效?

查看參考答案

一般段落被略過。按鈕自身也符合 closest,會取到 preview;記錄可分清兩個目標及共用容器。

另開的邊界原始碼
<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="tools"><button id="action" type="button" data-action="preview"><span id="label">預覽原始檔</span></button><p id="plain">一般筆記</p></section><output id="result"></output>
<script>const tools=document.querySelector('#tools');const trace=[];tools.addEventListener('click',event=>{const button=event.target.closest('button[data-action]');trace.push({target:event.target.id,currentTarget:event.currentTarget.id,action:button&&tools.contains(button)?button.dataset.action:null});});
document.querySelector('#plain').click();document.querySelector('#action').click();const result=trace;
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
邊界輸出
[{"target":"plain","currentTarget":"tools","action":null},{"target":"action","currentTarget":"tools","action":"preview"}]

原始例子檔案