本文將介紹一個不太實用的小技巧,使用 tabindex
配合 :focus-within
巧妙實現父選擇器。
CSS 中是否存在父選擇器?
這是一個非常經典的問題,到目前為止,CSS 沒有真正意義上被廣泛實現的父選擇器,這和瀏覽器的渲染機制有關。
如果你對 CSS 中是否存在父選擇器有疑惑,可以去看看 知乎 -- CSS 中能否選取父元素?
當然,這不代表 CSS 完全無法通過子元素去控制父元素,通過 :focus-within
偽類可以近似的達到類似的目的。
:focus-within
偽類
首先需要複習一下 :focus-within
,它是一個偽類。
它表示一個元素獲得焦點,或,該元素的後代元素獲得焦點。劃重點,它或它的後代獲得焦點。
關於 :focus-within
,不算太瞭解的可以先看看這篇文章:《神奇的選擇器 :focus-within》
利用它,我們可以實現類似這樣的功能,通過元素的子元素的獲焦(focus事件),觸發該偽元素,從而實現一個狹義的父選擇器,類似這樣:
CodePen -- CSS focus-within INPUT
:focus-within
偽類實現父選擇的缺陷
藉助 :focus-within
實現父類選擇器最大的問題是,元素必須要有 focus
事件,才能觸發它或者它的父元素的 :focus-within
。
所以,這就導致了在之前我認為 :focus-within
只能配合 <button>
、<input >
元素一起使用。
諸如
<button>
,<input>
,<select>
,<a>
這類可互動元素,預設是存在 focus 事件的,而類似<div>
,<span>
和<table>
這類非互動元素,預設是不能被聚焦的。
也是因為這個原因,大大限制了它的使用場景。基於此,我們引入本文的另外一個主角 -- tabindex
。
使用 tabindex
使元素獲得 focus
事件
tabindex: HTML 標籤的屬性,指示其元素是否可以聚焦,以及它是否/在何處參與順序鍵盤導航(通常使用Tab鍵,因此得名)。
也就是說,一個單純的 div 標籤,他是沒有 focus 事件的,然而,我們給它加上一個 tabindex 屬性,這個時候他就會獲得類似 input
框一樣的表現,擁有了 focus
事件,再配合 :focus-within
,能夠使用的場景就大大提升了。
看看虛擬碼:
<div class="g-father"> <!-- 沒有 focus 事件的 .g-children 元素 --> <div class="g-children">Click</div> </div>
<div class="g-father"> <!-- 擁有 focus 事件的 .g-children 元素 --> <div class="g-children" tabindex="-1">Click</div> </div>
這裡為什麼是
tabindex="-1"
呢,tabindex 負值表示元素是可聚焦的,但是不能通過鍵盤導航來訪問到該元素。因為我們只需要讓元素能夠獲得 focus 事件,而不需要他真的能夠被鍵盤導航來訪問。
這樣,配合 :focus-within
,就能做到當點選子元素的時候,去改變父元素的樣式了。
並且,我們可以在任意元素上搭配 tabindex
,脫離了 <input>, <a>, <button>
等元素才有 focus 事件的束縛。
.g-father:focus-within { background: #fc0; }
CodePen -- tabindex 配合 focus-within 實現div的父選擇器
一個小細節,button 的 focus 事件在 Safari 和 firefox 的上冒泡問題
由於 input 元素(或者任意元素 +tabindex
) 配合 :focus-within
的方案依賴 focus 事件的冒泡。
而對於 <button>
元素,稍微有點特殊,存在這樣兩個問題,即:
- 在 MacOS 的 Safari 和 Firefox 中, **點選
<button>
元素,不會觸發<button>
的 focus 事件,也沒有 focus 事件冒泡。 - 在 Windows 的 Safari 和 Firefox 中, 點選
<button>
元素,會觸發<button>
的 focus 事件,但在被目標元素捕捉到之後,不會繼續向上冒泡。
什麼意思呢?我們來驗證一下,使用類似這樣的結構:
<div class="g-father"> <input type="button" value="Button"> </div>
input:focus { background: #00bcd4; } body:focus-within { background: blue; } .g-father:focus-within { background: red; }
看看,在 Chrome 下的表現:
在 Windows 的 Safari,Firefox 下的表現:
在 MacOS 的 Safari,Firefox 下的表現:
在 Chrome 上的表現是正常,而在 Windows 的 Safari、Firefox 上,會觸發 button 的 focus 事件,但不會觸發父元素的 :focus-within
事件,也就是上面說的,focus 事件,在被目標元素捕捉到之後,不會繼續向上冒泡。而在 Mac 上,則連 focus 都不會觸發。
這一點,在使用的時候務必需要留意。
CodePen -- button 的 focus 事件冒泡性驗證(Chorme / Safari / Firefox)
最後
當然,本文介紹的小技巧,只能算是一個非常簡陋,特定條件(點選目標元素改變父元素樣式)下的父選擇器,真正意義上的父選擇器仍需等待未來規範的實現。
好了,本文到此結束,希望對你有幫助 :)
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。