“我苦心鍛煉了三年,我變禿了,也變強了。” —— 琦玉老師 0x00 大綱 0x01 前言 四個月前,我在《你是來找茬的吧?對自己的博客進行調優》一文中探討了以博客的使用者而不是開發者身份去進行優化,究竟能做到何種程度的問題。當時以 Edge 瀏覽器的開發者工具里的 lighthouse 評分和載入 ...
最開始想實現一個功能,點擊img圖標後給出購物下拉框CartDropdown,當img及CartDropdown失去焦點時隱藏CartDropdown。
最開始的核心代碼如下:
export default function Cart() {
const [isCartOpen, setIsCartOpen] = useState(false)
function clickHandler() {
setIsCartOpen(!isCartOpen)
}
function closeCartDropdown() {
if(!document.querySelector('#cart').contains(document.activeElement)) {
setIsCartOpen(false)
}
}
return (
<div id="cart" className="relative" onBlur={closeCartDropdown}>
<div tabIndex={0} onClick={clickHandler} className='relative hover:-translate-y-0.5 active:translate-y-0 transition-transform cursor-pointer'>
<img className='h-14' src="/src/assets/images/shopping_bag.png" alt="shopping bag" />
<span className='Z-10 text-2xl font-bold absolute left-1/2 -translate-x-1/2 -translate-y-2/3' style={{top:'70%'}}>4</span>
</div>
{
<CartDropdown isCartOpen={isCartOpen}/>
}
</div>
)
}
這個版本的代碼中在onBlur回調中使用document.activeElement函數想要獲取當前聚焦的元素,之後通過判斷聚焦的元素是cart組件內的來判斷是否需要隱藏cartDropdown,但這裡document.activeElement返回的都是body元素。
後面我加入了onFocus函數,併在其中獲取document.activeElement卻能返回正確的結果,並且是先觸發onBlur再觸發onFocus函數。這樣就可以確定,在之前的元素失去焦點時,onBlur函數被調用,此時沒有焦點因此預設給在body上;之後onFocus函數執行,此時新元素獲得焦點,因此可以正常獲取聚焦結果。
因此在onBlur中想要正確獲取聚焦元素,應該在onFocus函數調用後,所以可以使用非同步函數來完成這一點。我選取setTimeout來進行非同步操作,並且成功在onBlur函數中獲取到了正確的document.activeElement值。
function closeCartDropdown() {
setTimeout(() => {
if(!document.querySelector('#cart').contains(document.activeElement)) {
setIsCartOpen(false)
}
}, 0)
}
備註:傳統html中blur事件是不能冒泡的,但react中進行了特殊的處理成功模擬了冒泡,因此可以實現子組件失去焦點,調用父組件回調函數的效果。