千萬別小瞧九宮格 一道題就能讓候選人原形畢露!
前言
據(jù)不完全統(tǒng)計(其實就統(tǒng)計了自己身邊的朋友和同事),在刨除抖音或快手這一類短視頻 APP 后,每天在手機上花費時間最長的就是刷微博和逛朋友圈。
在刷微博和逛朋友圈的時候經(jīng)常會看到這種東西:
它有一個高大上的名字:九宮格。
顧名思義,九宮格通常為如圖這種三行三列的布局。
微信客戶端就用到了這種布局方式:
大家最熟悉的朋友圈也采用了九宮格:
還有微博:
它在移動端的運用十分的廣泛,而且不僅僅是在移動端的運用,它甚至還運用到了一些面試題中,因為九宮格可以很好的考察面試者的 CSS 功底。
邊距九宮格
九宮格通常分為兩種,一種是邊距九宮格,另一種是邊框九宮格。
邊距九宮格就是朋友圈那種每張圖都帶有一定邊距的那種:
這種其實反而更簡單一些,因為不涉及到邊框問題,像這種幾行幾列的布局用網(wǎng)格布局(grid)簡直再合適不過了。
但考慮到大家普遍對網(wǎng)格不太熟悉,所以咱們用同樣適合幾行幾列的表格布局來實現(xiàn),為什么不用萬能的彈性盒子(flex)來做呢?因為下面那道面試題就是用 flex 實現(xiàn)的,不想用兩個一樣的布局來實現(xiàn),來看代碼:
運行結(jié)果:
可以看到在 DOM 結(jié)構(gòu)上我們并沒有用到 <table\>、<tr\>、<td\> 這類傳統(tǒng)表格元素,因為在這種情況下只是用到了表格的那種幾行幾列而已。
但實際上九宮格并不是表格,所以為了符合 W3C 的語義化標準,我們采用了其他的 DOM 元素。
在有些適合使用表格布局但又不是表格的情況下,可以利用 display 屬性來模仿表格的行為:
display: table;相當于把元素的行為變成
<table></table>
display: inline-table;相當于把元素的行為變成行內(nèi)元素版的
<table></table>
display: table-header-group;相當于把元素的行為變成
<thead></thead>
display: table-row-group;相當于把元素的行為變成
<tbody></tbody>
display: table-footer-group;相當于把元素的行為變成
<tfoot></tfoot>
display: table-row;相當于把元素的行為變成
<tr></tr>
display: table-column-group;相當于把元素的行為變成
<colgroup></colgroup>
display: table-column;相當于把元素的行為變成
<col></col>
display: table-cell;相當于把元素的行為變成
<td></td>或<th></th>
display: table-caption;相當于把元素的行為變成
<caption></caption>
邊框九宮格
可能大家看了前面的內(nèi)容覺得:就這?這么簡單還想讓人原形畢露?
那咱們來看這么一道題:
要求如下:
邊框九宮格的每個格子中的數(shù)字都要居中
鼠標經(jīng)過時邊框和數(shù)字都要變紅
點擊九宮格會彈出對應(yīng)的數(shù)字
看起來還是沒什么大不了對不對?是不是覺得就是把九宮格加個邊框就行了?如果你是這么想的話,那么你寫出來的九宮格將會變成這樣:
是不是跟想象中的好像不太一樣?為什么會這樣呢?
因為給每個盒子加入了邊框以后,在有邊距的情況下看起來都挺正常的,但要將他們合并在一起的話相鄰的兩個邊框就會貼合在一起,肉眼看起來就是一個兩倍粗的邊框:
那么怎么解決這個問題呢?
解法1
不是相鄰的兩個邊框合并在一起會變粗嗎?那么最簡單粗暴的辦法就是讓兩個相鄰的盒子的其中一個的相鄰邊不顯示邊框不就完了!就像這樣:
這么做完全可以實現(xiàn),絕對沒毛病。但這種屬于笨方法,如果給換成四宮格、六宮格、十二宮格,那么又要重新去想一下該怎么實現(xiàn),而且寫出來的代碼也比較冗余,幾乎每個盒子都要給它定義一個不同的樣式。
如果去參加面試的時候這么實現(xiàn)出來,面試官也不會給你滿分,甚至可能連個及格分都不會給。但畢竟算是實現(xiàn)出來了,總比那些沒實現(xiàn)出來的強點,不會給零分的。
解法2
上面那種實現(xiàn)方式要給每一個盒子都寫一套不同的樣式,而且還不適合別的像六宮格、十二宮格這類,代碼冗余、可復(fù)用性差。
那么怎么才能每個盒子只用到一個樣式,并且同樣還適用于別的宮格呢?來看看這個思路:
但是仔細一看經(jīng)不起推敲啊:整個九宮格最右邊和最下邊的邊框都沒有了!其實只要咱們在父元素上再加上右側(cè)和下側(cè)的邊框即可:
而且并不一定非得是這個方向的,別的方向也可以實現(xiàn)啊,比如醬嬸兒的:
醬嬸兒的:
還有醬嬸兒的:
這種方式不管你是4、6、9還是12宮格,只需在子元素上加一個樣式即可,然后再在父元素上加一個互補的邊框樣式。
解法3
上面那種解法其實已經(jīng)可以了,但還不是最完美的,那么它都有哪些問題呢?
首先,雖然換成別的宮格也可以復(fù)用,但都只適合"滿"的情況。比如像朋友圈,最大就是九宮格對吧?但用戶可以不是每次都發(fā)滿九張照片,有可能發(fā)7張、有可能發(fā)五張,這樣的話就會露餡(所以朋友圈采用的是邊距九宮格而不是邊框九宮格)。
其次,它并不適合這道面試題,因為這道面試題的要求是在鼠標移入時邊框變紅,而上面那種解法會導(dǎo)致每個盒子的邊框都不完整,所以當鼠標移入時效果會變成這樣:
那么怎么樣才能完美的解出這道題呢?首先每個盒子的邊框不能再給它缺斤少兩了,但那又會回到最初的那個問題上去:
有的面試題就是這樣,在你苦思冥想的時候怎么也想不出來,但是稍微給點思路立馬就能明白!
其實就是每個盒子都給它一個負邊距,邊距的距離恰巧就是邊框的粗細,這樣后面一個盒子就會"疊加"在前面那個盒子的邊框上,我們來寫一個粗點的半透明邊框演示一下:
中間那些顏色變深了的就是疊在一起的邊框,由于是半透明,所以疊在一起時顏色會變深。
不過一些比較細心的朋友可能會納悶:既然所有盒子都用負邊距向左上角移動了,豈不是九宮格不會處在原來的位置上了,沒錯是這樣的!所以我們需要讓最左邊那一排和最上面那一排不要有負邊距,這時候就要考察候選人的CSS水平了,看看他/她能不能夠靈活運用偽類選擇器:每一行的第一個,應(yīng)該怎么寫?
:nth-child(1), :nth-child(4), :nth-child(7) ?
這樣也能實現(xiàn),不過更好的方式是寫成這樣:
:nth-child(3n+1)
最上面那一排負邊距可以不用管,因為如果頁面上的九宮格往左邊移動了,哪怕只有一兩像素,也會導(dǎo)致和頁面上的版面無法對齊,而往上移動個一兩像素的話誰也看不出來。
每個宮格內(nèi)的數(shù)字要居中,這里推薦用 grid,因為九宮格可以用 flex 去實現(xiàn),但里面的內(nèi)容還繼續(xù)用它去實現(xiàn)的話就體現(xiàn)不出你技術(shù)的全面性了,而且在居中這一方面 grid 可以做到比 flex 代碼更少,即使你對 grid 不感興趣,那么只需記住這一固定用法即可:
里面的內(nèi)容解決了,外面的九宮格咱們來用萬能的 flex 去實現(xiàn),flex 默認是一維布局,但如果僅支持一維的話就不會稱之為萬能的 flex 了,思路是這樣的,假如每一個宮格寬高為 100 x 100,九宮格加起來是 300 x 300,每三個就讓它換行,這樣就可以考察到候選人對 flex 的靈活運用的程度了:
看起來沒毛病對不對?實際上確是每行只有兩個宮格就會換行,因為加了邊框以后子元素的寬高就變成了`102 x 102`了,三個的話就已經(jīng)超過了`300`,所以還沒到三個就開始換行了,這時候就考察到候選人的盒模型了:
這樣即使加了邊框,寬高也還是`100`,剛好能滿3個就換行,想象一下如果你是面試官,直接問盒模型是不是顯得很low,但是就這一個小小的九宮格立馬就能區(qū)分出這個候選人的水平如何。
再接下來就是鼠標移入時邊框和里面的內(nèi)容一起變紅,這有啥難的,不就是:
還是那句話,這樣確實能實現(xiàn),但如果在咱們寫js的過程中像red這種多處地方使用的值是不是一般都會給它設(shè)置成變量啊?那么這里要寫CSS變量?也可以,但有一個更好的變量叫做 currentColor,這個屬性可以把它理解成一個內(nèi)置變量,就像 js 里的 innerWidth(window.innerWidth) 一樣,不用定義自然就是一個變量。
和CSS變量不同的是它取的是自身或父元素上的color值,而且它的兼容性還更好,可以一直兼容到 IE9。
如果你覺得納悶:這單詞這么長,還不如直接寫個red多方便啊,那么請別忘了color是可以繼承的!如果在一個外層元素中定義了一個顏色,里面的子元素都可以繼承,用JS來控制的話只需要獲取外層DOM元素然后修改它的color樣式即可。
currentColor作為一個變量,可以用在 border、box-shadow、background、linear-gradient() 等一大堆的 CSS 屬性上…甚至連svg中的 fill 和 stroke 都可以使用這個變量,它能做的事情很多,這里為了不跑題就先不展開講,有興趣的可以去搜一下。
修改后的代碼如上,為什么沒有 currentColor?那是因為如果你不寫的話,默認就是 currentColor,這個關(guān)鍵字代表的就是你當前的color值。
大多數(shù)的候選人可能都不會寫成這樣,如果你作為面試官的話最好是適當?shù)奶崾疽幌拢此懿荒苷f出 currentColor 這個變量或者 CSS 變量。
然后就是點擊每個宮格彈出對應(yīng)的數(shù)字,這個考察的是事件冒泡和事件代理:
你可以觀察一下候選人是把事件綁定在父元素上還是一個個的綁定在子元素上,這個問題按理說基本上都不會錯。但如果發(fā)現(xiàn)候選人一個個把事件綁定在子元素上了,那就可以到此為止了,也不用浪費時間再去問別的問題了,可以十分裝B的來一句:行,你的情況我已基本了解了,回去等通知吧!
接下來我們再來寫一下完整一點的代碼,以便引出下一個問題:
運行結(jié)果:
想知道為什么會這樣嗎?因為當前這個邊框被后面的宮格壓住了嘛!那么只需要當鼠標經(jīng)過時不讓后面的壓住就好了(調(diào)高層級)。
說到調(diào)高層級,大家首先想到的可能就是 z-index 了,這個屬性用的最多的地方可能就是絕對定位和固定定位了。但其實很少有人知道, z-index 不是只能用在 position: xxx 的,萬能的彈性盒子(display:flex)也是支持 z-index 的:
運行結(jié)果:
結(jié)語
沒想到這么一個看似不起眼的九宮格一下子就能考察這么多內(nèi)容吧!如果面試的時候直接問:
你對 flex 了解的怎么樣
當元素的外邊距為負值時會有什么樣的行為
請實現(xiàn)一下水平垂直居中
了解過 grid 嗎
談一下你對盒模型的理解
說一下事件綁定和事件冒泡
CSS3的偽類選擇器用的怎么樣
當頁面元素重疊時如何控制哪個在上哪個在下
在CSS中如何運用變量
直接這么問的話既浪費口舌,又顯得很low,而且還不能篩選出真正能夠靈活運用技術(shù)的候選人。
因為這些問題都不難,一般來說都能答出來,但具體能不能靈活運用就不一定了,而這一道九宮格,就像一面照妖鏡一樣,瞬間讓人原形畢露!
如果你是候選人的話,那么一定要好好練習(xí)一下這道題。
如果是面試官的話,那么也推薦你用這道題來考察候選者的技術(shù)水平,如果能非常完美的做出來,那么基本上就不用再問其他的CSS題目了,日常開發(fā)所用到的樣式基本難不倒他/她了,可以直接上JS面試題了。
但如果沒做出來也不一定就代表這個人水平不行,可以試著提示一下候選者,然后再問一下其他的CSS題來確定一下此人的水平。