转答寒冬的面试题(1)
有關注前端和微博的朋友一定留意到了前一陣寒冬和玉伯關于前端面試的討論,后來老趙和左耳朵耗子也加入其中,討論帶來了非常多的啟發和思考——不久之前自己也經歷了幾輪面試,決心對自己的基礎開始加強,正巧寒冬老師題目由淺入深,非常有代表性,有些概念自己也模糊,決心花時間把這些概念全部弄清楚,梳理一遍
寒冬的微博原文如下
面試的時候問個css的position屬性能刷掉一半的人這是啥情況……其實這問題我本來打算的是可以順著一路扯到normal flow、containing block、bfc、margin collapse,base line,writing mode,bidi,這樣一路問下去的,奈何第一個問題(親我真的只問了position有哪些取值和行為啊)就悲劇了……
并且后來寒冬后來也就這一事件寫了兩篇文章(《談談面試與面試題》,?《阿里巴巴無線前端團隊》)來做了一些說明,我們就根據這些材料對知識點進行一些梳理。本文將談到:
- position
- normal flow
- containing block
- bfc
- margin collapse
- writing mode
- bidi
- 以及各種綜合和邊緣情況
最困難的是當你在談一個概念的同時不得不對其他概念有所涉及——但其實所有的概念和方案都是為了解決問題而生,我打算從最簡單的布局開始,以解決復雜的布局方案為線索,逐漸引入這些概念。本文的資料來源于國外國內的通俗博客和實戰經驗,如果直接引用w3c的概念我感覺會有一些晦澀(重要的是我看的也很頭大),但仍然會做引用。本文只是拋磚引玉,如果有不正確的地方請多多指正。
?
Normal Flow
我傾向把normal flow翻譯為標準流。當你在一個空白的網頁上插入不同的標簽div, p, span等,那么這些標簽形成的元素將以何種標準進行排列?那就是標準流。
談論標準流的前提是,頁面上的所有元素都沒有有關布局的css(float,display,position等)進行修飾,也就是說所有的元素要么是塊狀元素(block),要么是行內元素(inline)
塊狀元素將在頁面上或者包含他們的包含塊(containing block)中,從左上方開始,并且是從垂直自上往下進行填充,不用擔心橫向,因為塊級元素非常霸道,一個元素必定占一整行,即使你給它指定寬度,它所在的那一行余下的寬度也是屬于它的,不會有后面的元素進來,即使后面的元素很窄能剛好插進來。而上下兩個塊之間的距離則根據他們的外邊距和外邊距的折疊規則(margin collapse)而定,這個后面會談到。比如看下面這個例子
div { border: 1px solid black; } div div { height: 100px; } .a { margin: 20px 0; } .b { margin: 30px 0; width: 100px; } .c { margin: -50px 0 0; } <div> <div class="a"></div> <div class="b"></div> <div class="c"></div> </div>?
而行內元素顧名思義,則優先進行橫向排列,從容器的左上開始,后面的元素一個緊挨一個的排列在這一行中,只有當位置不夠了,才會被擠到下一行去,
div { width: 100px; border: 1px solid black;} span.a {background: yellow;} span.b {background: red;} span.c {background: blue;} <div> <span class="a" >This is an span element</span> <span class="b" >This is another span</span> <span class="c" >This is the third span</span> </div> This is an span element?This is another spanThis is the third span在標準流的狀態下,如果兩種元素混合在一起,那么將各自遵循各自的標準,理論上是不會有干擾的。臨時想到一個問題,如果塊狀和行內上下相鄰,并且都配備有外邊距,那么外邊距怎么計算?再復雜一點,行內元素所在的那一行有好幾個行內元素,并且每個的外邊距都不相同,那么與垂直相鄰的塊狀元素的距離怎么算?代碼如下
div { width: 100px; border: 1px solid black; margin: 20px;} span.a {margin: 10px 0 0;} span.b {margin: 20px 0 0;} span.c {margin: 30px 0 0;} <div class="block"></div> <span class="a">This is a span</span> <span class="b">This is b span</span> <span class="c">This is c span</span>這個問題還涉及另一個概念:盒子模型,如果又要展開又大了去了。在這里只說一點,行內元素的margin和padding只對它的左右元素有影響,對上下無影響,具體看下面代碼
.p { width: 200px; height: 50px; border: 1px solid black;} .span {margin: 20px;} <p> <span>this is span</span><span class="span">this is span</span><span>this is span</span><span>this is span</span><span>this is span</span> </p>this is span?this is spanthis is span?this is span?this is span
本節主要參考資料:
- Inline elements and padding
- W3C: Normal flow
- Webdesign:normal flow
- Some definitions:Normal flow
BFC(Block Formatting Context)
好吧,必須承認我也是第一接觸BFC的概念,很慚愧,于是瘋狂的查找資料,對它概念的介紹引用阿里巴巴用戶體驗部的一篇文章的一段話
什么是BFC(Block Formatting Context),簡單講,它是提供了一個獨立布局的環境,每個BFC都遵守同一套布局規則。例如,在同一個BFC內,盒子會一個挨著一個的排,相鄰盒子的間距是由margin決定且垂直方向的margin會重疊。而float和clear float也只對同一個BFC內的元素有效。
在提BFC的同時也不得不提另一個概念,IE的haslayout屬性,一旦元素的這兩個屬性(haslayout在IE下)被觸發,他們都能給自己提供一個獨立的布局環境(我的疑問在于不獨立的布局環境又是什么情況?)。當元素的CSS屬性設置了下列之一時,即可創建一個BFC:
那么如何觸發BFC,繼續引用一淘的文章:
非塊級盒子的浮動元素、絕對定位元素及塊級容器(比如inline-blocks,table-cells和table-captions),以及overflow屬性是visible之外任意值的塊級盒子,都會創建了一個BFC。即當元素CSS屬性設置了下列之一時,即可創建一個BFC:
- float:left | right
- position:absolute | fixed
- display: table-cell | table-caption | inline-block
- display: table-cell | table-caption | inline-block
然后這個獨立布局的規則是這樣的(摘自)
- 在創建了 BFC的元素中,其子元素會一個接一個地放置。垂直方向上他們的起點是一個包含塊的頂部,兩個相鄰的元素之間的垂直距離取決于 ‘margin’ 特性。在BFC中相鄰的塊級元素的垂直邊距會折疊(collapse)。
- 在BFC 中,每一個元素左外邊與包含塊的左邊相接觸(對于從右到左的格式化,右外邊接觸右邊), 即使存在浮動也是如此(盡管一個元素的內容區域會由于浮動而壓縮),除非這個元素也創建了一個新的BFC。
上面的定義是翻譯自W3C的標準。因為自己經驗有限,在這里我直接舉出我找到的的BFC的經典用處,希望大家能從中體會。
在所有的文章中,被列舉最頻繁的例子莫過于阻止文字圍繞浮動元素,請看下面代碼
.item {width: 200px; height: 150px; border: 1px solid black;} .pic{width:80px;height:80px;margin:10px;background-color:#acdae5;float:left;} <div class="item"> <div class="pic">picture</div> <p class="text"> 測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試 </p> </div> picture測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試
此時我們要做一個重要的決定!就是阻止文字圍繞左側的浮動塊——那就開啟右側文字的BFC屬性,用overflow:hidden
.item {width: 200px; height: 150px; border: 1px solid black;} .pic{width:80px;height:80px;margin:10px;background-color:#acdae5;float:left;} .text {overflow: hidden;} picture測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試
用法二:創建了 BFC的元素中,浮動元素參與高度的計算,這個方法也可以用于消除因為子元素浮動引起的折疊
.header {border: 1px solid black;} .logo {width: 50px; height: 50px; background: blue; float: left;} <div class="header"> <div class="logo"></div> </div>?這是一個很常見的問題,在網站有一個標題區域header,其中有一個logo,并且設置logo向左浮動。但問題是一旦logo向左浮動,它的包含塊因為float的一些特性(后面介紹),自動折疊了起來
那么就設置包含塊的BFC的屬性吧,照上面可以使用overflow屬性
.header {border: 1px solid black; overflow:hidden;}?于是成功了,并且此時當計算header的高度時,將把子元素的float元素的高度計算進來。其實解決這一類的問題應該算是清除浮動。這一類解決方案已經非常成熟了,并且一個好的方案知識點的信息量還是比較大的,可以好好介紹一下。
用處三:創建了 BFC的元素不會與它們的子元素發生外邊距折疊
外邊距折疊規則中有一條是,當赤裸裸的子元素(沒有padding,border)與赤裸裸的父元素(還是沒有padding,border)進行接觸時,他們的外邊距會按一定的規則進行折疊。比如
div {margin:30px 0; background: yellow;} p {margin: 20px 0; background: blue;} <div> <p></p> </div>This is text
看到上面因為父元素與子元素的外邊距發生了折疊,父元素已經完全和子元素重合。如果元素是赤裸裸但又不想和父元素發生外邊距折疊怎么辦,那就給父元素設置BFC!
div {margin:30px 0; background: yellow; overflow: hidden;}This is text
以上是三個基本用處的介紹,至少我個人對BFC還不是很理解,比如擁有不同布局的元素究竟應該如何在BFC的元素中排列等。希望有經驗的同學指點一下。
參考文章
- CSS布局中一個簡單的應用BFC的例子
- hasLayout && Block Formatting Contexts
- 更加直觀地了解hasLayout和BFC
- How does the CSS Block Formatting Context work?
上面的文章幾乎都拿BFC與haslayout屬性進行對比,上面的第二篇把異同之處都整理了出來。如果是頁面布局方向的同學可以深入閱讀。
Position: relative
Position有四種可選值,static,relative,absolute,fixed。當你不對元素做任何position屬性時,默認即為static,注意此時你無法對元素的z-index值進行設置,只有當你把position設置為出static以外的值時,z-index才生效。所以最保險的作法是設置相對定位。
CSS2.1中元素是根據三種的位置方案(positioning modes or schemes)被布局的,分別是標準流(normal flow), 浮動(float)和絕對定位(absolute position)
相對定位雖然被改動了position值,但是仍然遵循標準流的規則。CSS中難點之一在于,如何根據當前元素的位置屬性判斷出相鄰,或者后代的位置屬性,而判斷出當前元素的位置屬性并不難。
你可以這么理解,相對定位的元素仍然處于標準流中,只要你沒有設置偏移值(top, left, right, buttom);如果你設置了偏移值,那么元素就會相對于你在標準流中的位置,依照值進行偏移。但你周圍的元素會以為你還在標準流中,有一種靈魂出竅的感覺。見代碼
div { width: 200px; height: 40px; border: 1px solid black; position: relative; } .a {background: yellow; } .b {background: blue; } <div class="a"></div> <div class="b"></div> <div></div>以上是正常情況,而下面將演示非正常情況,我將給前兩個容器添加相偏移
.a {background: yellow; top: 10px; left: 10px;} .b {background: blue; top: 10px; left: 10px;}注意到雖然前兩個元素雖然發生了移動,但是第三個元素并沒有收到影響,并且前兩個元素的移動都是根據自己的相對位移。如果這三個盒子還存在外邊距的話,那么外邊距的折疊效果仍然是與標準流保持一致的。
再留意上面的一個細節,這三個容器我都沒有設置z-index,你也可以通過firebug之類的工具查看到它們的z-index是auto;但卻出現了有疊加的情況。
元素z軸上的層疊關系不僅僅可以通過z-index這個屬性來硬性規定。還可以根據浮動,負外邊距,定位屬性等隱式屬性的隱士式體現。就上面的例子而言,當然是有position為relative的元素的優先級比static高,而同樣都是relative,在代碼中后書寫的元素優先級又比先書寫的優先級高。
z-index不在這次講課之列,有興趣的同學可以參考下列文章:
- The Z-Index CSS Property: A Comprehensive Look
- Z-Index And The CSS Stack: Which Element Displays First?
- Appendix E. Elaborate description of Stacking Contexts
留意一個邊界情況,如果偏移量有矛盾怎么辦,比如
.a { left: 10px; right: 10px; }記住這樣一個原則,在矛盾的情況下left的優先級要比right高,top的優先級要比bottom高,即使right書寫在left后面。這一原則在絕對定位和相對于視窗定位時仍然成立。
Position: absolute
絕對定位與相對定位雖然只差一個字,但差別多了去了。首先絕對定位元素已經徹底脫離了標準流,有一種跳出三界外,不在五行中的感覺,所以它的偏移量不是相對于標準流進行偏移的,而是相對于距離它最近的position不為static的祖先元素。
什么是距離它最近的position不為static的祖先元素?看下面例子
div { border: 1px solid black; } .outer { width: 120px; height: 120px; position: relative;} .inner { width: 80px; height: 80px; margin: 20px 0 0 20px;} .item { width: 50px; height: 50px; background: red; position: absolute; left: 0; top: 0; } <div class="outer"> <div class="inner"> <div class="item"></div> </div> </div>?紅色方塊有兩個父元素,為什么它絕對定位到離他更遠的父元素?因為它的祖父元素(離它更遠的那位)的position屬性不為static,而它的父元素為static。如果想讓它相對于父元素絕對定位怎么辦,那就給父元素設置position不為static就行。
.inner { width: 80px; height: 80px; margin: 20px 0 0 20px; position:relative;}再次強調,絕對定位就是相對于1.離它最近的父元素;2.并且這個最近父元素position值為非static。。如果元素所有的祖先元素都是static怎么辦?那就相對于瀏覽器窗口了。
還有一個在w3c中特別注明的情況,如果父元素是行內元素怎么辦?行內元素不可怕,可怕的是這個行內元素還形成了多行。看下面這個例子(你需要手動把瀏覽器變窄,把下面的行內元素變為多行):
.wrap { position:relative; border: 10px solid black; padding: 10px;line-height: 65px; } .item { background: blue; position: absolute; right: 5px;bottom: 5px;} <span class="wrap"> test test test test test test test test test test test test test test test test test test test test test test test test test test test <span class="item">This is span</span> </span>test test test test test test test test test test test test test test test test test test test test test test test test test test testThis is span
我讓另一個藍色行內元素相對于它父元素(同樣也是行內元素)進行絕對定位,定位在右下角。
注意此時多行元素的“右”是有兩個不同的值,一個是第一行的右,一個是第二行的右,并且第二行的右比第一行的右還要短,那此時相對的“右”應該是哪一個右?如圖所示,當然是最后一行的。
當行內元素形成多行并且左右側不對齊時(形成了許多的line boxes),相對于它絕對定位的元素參考的區域是,以第一行inline box的左邊和上邊為邊界(這里的邊界是內容邊界,content edge而非padding edge),和最后一行inline box的右邊和下邊為邊界的矩形區域。即使中間有某幾行的右側與最后一行沒有對齊,仍然以最后一行為準。所以有了上面的結果。
上面提到了一個edge概念,在盒子模型中有content edge,padding edge,border edge,margin edge。content edge內容邊距指內容(content)與內邊距(padding)的交界處,或者說包圍內容區域的一圈邊緣,另外三個同理。
于是這里我們不得不還要考慮一個問題:比如當我指定絕對定位的left偏移量時,這個量是從這個元素的哪一個邊緣到相對元素的哪一個邊緣?記住了,是從該元素的外邊距邊緣(margin edge)到相對元素的內邊距邊緣(padding edge)。也就是說在絕對定位中,外邊距仍然是工作中的,BTW在相對定位中也是。
阿里巴巴UED有一篇文章是關于使用absolute的一個tip,可以加深對absolute的理解:?《overflow:hidden真的失效了嗎》
Position: fixed
在W3C中把fixed視為absolute的一種情況,這個值可以一句話就帶過了。絕對定位不是把某個祖先元素作為參考對象嗎?那fixed也是把某個祖先元素視為參考對象,只不過這個參考對象是唯一的,那就是瀏覽器。注意,IE6中的position是沒有fixed屬性的
參考文章
- Absolute Positioning
- absolute-positioning
- CSS Positioning
Containing Block
大部分時候一個元素的尺寸和位置并非是由它自己決定的(比如當它沒有硬性規定長寬的時候),而是要參考它所在的那個容器,或者是它需要參考的元素容器。這個容器就叫做containing block,包含塊。
包含塊(containing block)的定義是我覺得w3c文檔中寫的最通俗易懂的,大致談了四點:
- 根元素(我不理解為什么沒有明確指出是body或html)的包含塊被稱為初始包含塊(initial containing block),這個包含塊指的是瀏覽器視口(viewport),就是瀏覽器的窗口大小。
- 對position為relative或static的元素來說,包含塊指1.最近的;2.塊級祖先元素;3.內容邊緣(content edge) 所形成的區域(這一條從正面來說比較好理解,但是我還沒有找到這樣的一個反例)
- position為fixed的元素,包含塊就是視口
- 如果元素是絕對定位,包含塊除了要滿足具有出static之外的定位屬性意外,還要分一下兩種情況:
- 當滿足情況的祖先元素是行內元素時,包含塊應該是上一節絕對定位中最后描述的,行內元素多行時,當右側參差不齊時形成區域的規則。在CSS2.1中,如果行內元素被分為許多行,那么包含塊為undefined
- 如果不是行內元素,包含塊即是那個祖先元素內邊距邊緣所形成的區域。
- 最后,如果該元素都不滿足上述任何條件,那么包含塊即是指“初始包含塊”
參考文章
- Containing Block
- Definition of "containing block"
- overflow:hidden真的失效了嗎
Margin Collapse
關于外邊距折疊,我覺得需要了解兩點
- 什么情況下會折疊
- 什么情況下不會折疊
關于什么情況下會折疊,還要分三種情況
相鄰元素的折疊:當兩個或多個元素垂直排列時(標準流中),相鄰的兩個外邊距會重合為一個外邊距。這個重合外邊距的值為那兩個外邊距的較大值;如果為一正一負的話,則為兩個的和值;如果兩個都是負外邊距的話,最終值為負的更厲害的那個,這個就不舉例了,應該很容易理解吧。
子元素與父元素之間的折疊:在之前那個BFC中提到過這個例子,當父元素與子元素赤裸裸的接觸時(都沒有內邊距,邊框;外邊距的直接接觸),那么父元素的外邊距會與子元素的外邊距產生折疊;如何組織這個折疊?觸發父元素的BFC屬性;具體實例的代碼在那一節也都涉及到了
自己的上邊距與下邊距折疊:這個就有點奇葩了,當你有一個容器,設置了上下外邊距,但是content area為空,那么自己的上外邊距和下外邊距會進行折疊!如果此時兩個外邊距正好一正一負,并且剛好抵消,那么你就看不到這個盒子了!
此時再如果盒子的上方來一個赤裸裸的接觸,那么又觸發外邊距折疊,合體了!上代碼
div {margin: 20px 0;} p {margin: 30px 0;} <div></div> <p></p>?
注意上面的空白處,用瀏覽器工具查看一下代碼,那就是效果,因為沒有任何內容,所以看起來什么都沒有……
那么什么情況下不會折疊?
- 浮動元素
- 絕對定位元素
- display:inline-block元素
- 觸發BFC的元素
- 被清除浮動的元素(cleared)
- 根(root)元素
參考資料
- 《CSS Mastery Advanced Web Standards Solutions Second Edition 》
- Collapsing Margins
寫了這么多感覺把,感覺已經很累了,展現拉的太長了!先最后還剩三個關于書寫排版的概念,這三個就真的沒有接觸過了,要花一段時間消化。最后還有重頭戲,關于float,以及關于float和display和position的疊加。
轉至:http://www.cnblogs.com/hh54188/archive/2013/05/18/3085433.html?
轉載于:https://www.cnblogs.com/xiaohong/p/4200934.html
總結
以上是生活随笔為你收集整理的转答寒冬的面试题(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 央行变相降准祭出又一新手段 引发同业套利
- 下一篇: JS单引号嵌套的问题,怎么改才能对呢!