css魔幻屬性跟進篇

地鐵上的小前端發表於2019-02-15

白話:即上一篇我腦中飄來飄去的css魔幻屬性自己的文章推出之後,這是自己寫的第四篇CSS相關的文章,文章絕大部分是自己工作總結得來,另一部分是平日sf回答的與面試中向面試官交流學到的,都是一些比較基礎,刨根問底的知識分享。

清除浮動的原理

在上一篇我腦中飄來飄去的css魔幻屬性提到浮動不按想要的方式浮,裡面提到清除浮動其實按原理來講,就兩個:

  • clear:both(不準確,後面會講)
  • 觸發BFC

前面因為沒咋搞明白,就沒有說為什麼,最近因為偶然在sf上有人提問,就順著這個問題去搜了相關資料,找了點答案。

clear 清除浮動

clear清除浮動的操作,基本思路是這樣。首先為要清除浮動的盒子引入清除元素,現實表現裡一般為一個空元素或者偽元素(before,after)。設定了clear屬性後,盒子渲染時,會將這個元素的top border(上邊緣)與浮動元素的底部對齊,來達到將盒子撐開的目的。但是這個與浮動元素底部對齊的元素與clear設定的屬性(both,left,top)有關,具體可以看W3C標準。還是很簡單易懂的。如果說的不是很明白,可以拷貝這段程式碼,試一下,然後切換clear的值,就會有種恍然大悟的感覺。 如果你覺得還不夠直觀,你可以將content的“”裡寫兩個字,或則加個margin,border什麼的。

html 程式碼

    <body>
        <div class="float-left">
            這是左邊浮動元素
        </div>
        <div class="clear-box">
            <div class="float-right">這是右側浮動元素</div>
            <div>這是正常佈局文件流元素。</div>
        </div>
    </body>
複製程式碼

css程式碼

    .float-left{
      float: left;
      width: 100px;
      height: 100px;
      background-color: lightpink;
    }
    .clear-box{
      margin-top: 50px; //這個沒有用
      background-color: lightgreen;
      font-size: 16px;
    }
    .clear-box:after{
      content: ``;
      display: block;
      clear: right; //both left
    }
    .float-right{
        width:100px;
        height:75px;
        float:left;
        background-color:red;
        border:1px solid black;
    }
複製程式碼
clipboard.png
clipboard.png

BFC 清除浮動

說BFC清除浮動之前,得知道BFC的概念:
塊格式化上下文(Block Formatting Context,BFC) 是Web頁面的視覺化CSS渲染的一部分,是佈局過程中生成塊級盒子的區域,也是浮動元素與其他元素的互動限定區域。其作用簡單來講就是,保證整個文件流中盒子與盒子之間的佈局不相互干擾,這裡其實已經很顯示的說明BFC一大功效就是清除浮動,觸發BFC的條件詳見MDN
至於BFC為什麼可以清除浮動,就是形成BFC的盒子,其邊框會去查詢盒子裡所有的正常佈局文件流,和浮動的文件流,然後將盒子的底部邊緣與盒子裡最底部元素的盒子邊緣對齊(這麼講會不會被警察關禁閉)。與clear區別的,這種清除浮動由於是盒子自己觸發BFC,所以只能清除盒子裡面的元素,而前面可以清除同一行所有左右兩邊的浮動。

clipboard.png

自此,清除浮動的原理就講完了。但在這次參加面試前,一個問題自己也一直想搞懂,浮動是否脫離了文件流。MDN中是這樣定義的:float CSS屬性指定一個元素應沿其容器的左側或右側放置,允許文字和內聯元素環繞它。該元素從網頁的正常流動中移除,儘管仍然保持部分的流動性(與絕對定位相反)。這相反指的是什麼,想知道。

重點強調: 設定了float屬性的元素,其display是什麼屬性?答案:block。inline元素設定為float後,其width和height都變成了可設定的屬性。其原因就是設定float觸發了BFC。是inline元素變成了一個塊級盒子。其同樣適用於設定position屬性為絕對定位或固定定位的內聯元素。

重新認識盒子之padding

學前端的,出去面試,浮動和盒模型是必問。這裡也不再說正常盒模型(W3C)與怪異盒模型(IE)的區別,重點談padding。

設定相對單位時,其參照值是誰

首先是說說單位。一般來說設定盒子某一屬性,有如下幾種單位可以設定:

  • padding :20%;

  • padding :2em;

  • padding :5px;(最常見的)

  • padding :2vh;

  • padding :2rem;
    px,vh,vw,rem這些絕對單位很好理解,但如果是em和%,你是否足夠留意,其是根據誰來作為參照來計算的。首先%,直接看程式碼和效果圖:

      <div class="big">
          <div class="small">這是子元素</div>
      </div>
    
    
     .big{
       height:200px;
       width:1000px;
       background-color: yellowgreen;
     }
     .big .small{
       width:50%;
       height:50%;
       margin-top:5%;
       margin-left:5%;
       border-top: 5px solid red;
       border-left: 5px solid red;
       padding-top: 5%;
       padding-left: 5%;
       background-color: #0e8cf6;
     }
    複製程式碼
clipboard.png
clipboard.png

從上面的圖可以看出,margin,padding無論是top還是left設定為5%都是50,算下來就是以父級元素的寬度來作為參照的。不要問為啥border不能設定百分比,我不知道,我也沒這種需求。
再看一下以em為單位是以誰做參照,HTML與上面一致,上css程式碼:

.big{
  height:200px;
  width:1000px;
  font-size: 14px;
  background-color: yellowgreen;
}

.big .small{
  width:50%;
  height:50%;
  margin-top:2em;
  margin-left:2em;
  border-top: 5px solid red;
  border-left: 5px solid red;
  padding-top: 2em;
  padding-left: 2em;
  font-size: 20px;
  background-color: #0e8cf6;
}
複製程式碼
clipboard.png

從計算樣式盒子可以看出,margin,padding無論是top還是left設定為2em都是以元素自身的font-size來計算的,所以和百分比又不一樣。

如果知道這些,UI需求是讓你在一個盒子裡畫一個正方形盒子,你就很自然的會想到。用padding的百分位元性來做。
如果再多想一些,當我們用css3特性來做位移比如:transform:translate(50%,50%),其又是相對誰來計算呢?這裡直接給出答案:其參照值是元素本身的長和寬,和前面padding又不一樣。

奇葩的內聯元素padding

sf上面有這樣一個提問:為什麼設定display:inline後,padding-bottom仍然起作用?如果一般看過css基礎知識的人,都知道內聯元素設定margin-top、bottom,padding-top、bottom是不起作用的,所以日常開發,我們一般不會用這兩個屬性,要用時更多也是把內聯元素轉換為inline-box。重現問題:

  <div id="fu">
    <p>1505</p>
    <p>計科</p>
</div>

#fu{
  //  margin-top: 20px;
  //  background-color: yellowgreen;
}
#fu p{
  display: inline;
  margin: 20px;
  padding: 20px;
  border: 5px solid transparent;
  background-color: #0e8cf6;
}
複製程式碼
clipboard.png

上面是三幅圖,分別代表三種狀態。通過不斷遞進,我們就可以回答上面那個問題了。其實不是padding-bottom仍然起作用,準確來說是padding-bottom與padding-top都會起作用,只是起作用只是從表現上起作用,但並不佔據文件流。怎麼理解?第一:父元素黃綠色背景區域的高度和子元素內容高度一致,說明padding高度並沒有被計算在內;第二:父元素沒有加margin-top來佔位時,padding-top那塊區域是不可見的,所以內聯元素padding是沒有在正常文件流的。至於為什麼,可以理解為內聯元素沒有盒模型,其高度由內容決定。由於其沒有盒模型,所以沒法控制padding-top和padding-bottom。

盒子裡面的絕對定位,其零點在哪裡

自己寫了一年css,其實一直只關注了設定border-box與content-box盒子模型時padding表現的差別。但這個盒子的零點,及子元素固定定位相對的零點在哪裡呢?還是先看程式碼和效果圖:

<div class="big">
    <div class="small">
        <!--<div class="normal">這是正常文件流子元素</div>-->
        <div class="position">這是絕對定位子元素</div>
    </div>
</div>


.small .normal{
  height:40px;
  background-color: #999;
}
.small .position{
  position: absolute;
  width: 90%;
  height:30px;
  //top:0;
  //left:0;
  background-color: white;
}
複製程式碼
clipboard.png
clipboard.png

上面四張圖片,分別展示了絕對定位時,設定top,left與不設定的差別。不設定時,其文件流開始的起點是正常文件流的位置,而設定了top,left的地方,其起點是父元素(padding+content)區域零點的位置。以上效果和父元素設定不設定box-sizing: border-box屬性無關,表現一致。

縱向上的margin:auto 用於垂直居中

這一波面試,談css的技術面試官,基本都會提怎麼垂直水平居中。這確實是一個老生常談的問題,以致於我越往後,回答的越含糊,如果你還不知道,可以看看這篇文章。基本就四種:table,flex,translate,定位加margin:auto。最後這一種很少人聽說,但在居中盒子長寬值確定時,適用性確實很高。具體怎麼操作呢:

 <div class="item">
        <div class="items-center">
            這是一個居中
        </div>
 </div>

.item{
  width: 500px;
  height: 500px;
  position: relative;/*關鍵設定*/
  background-color: #999;
}
.item-center{
    position: absolute; /*關鍵設定,也可fixed*/
    top:0; /*關鍵設定*/
    bottom: 0;/*關鍵設定*/
    left:0;/*關鍵設定*/
    right:0;/*關鍵設定*/
    height:300px;/*關鍵設定,也可其他單位*/
    width: 300px;/*關鍵設定,也可其他單位*/
    margin: auto;/*關鍵設定*/
    background-color: yellowgreen;
    border: 5px solid darkgray;
}
複製程式碼
clipboard.png

上面的效果圖,可以看到這種水平垂直居中方案也是666啊,前提是width,height必須顯示設定,只相容IE8+,其同樣也適用於position:fixed的情況,具體視UI需求。我們通常只知道針對於塊級元素,如果其定寬,可以使用margin:0 auto;來水平居中的,那這裡又用auto實現了垂直居中,怎麼實現的?本來想好好寫的,可又看見我張老師已經做了一次剖析,自己只能仰望,獻上地址。基本上從兩個方面解釋,能稍微解釋同:

1:left,right,top,bottom設定為0,那麼就說明item-center這個盒子,是會填滿整個父級容器item的;

2:margin:auto 預設只會計算左右邊距,所以上下如果設定為auto時預設是0;但對於脫離了正常文件流的定位元素,這個auto對於上下也是有效的,會自動均分左右兩邊的距離。所以這個盒子已經顯示設定寬高,那麼margin就會自動計算均分,達到居中的效果。

一些零碎的知識

下面是一些零碎的經驗分享,寫出來共勉。

字型圖示的使用

clipboard.png

字型圖示出現以後,其實精靈圖的很多實用場景就被取代了,前端切圖仔又可以好好安心寫程式碼了。但使用字型圖示圖示還是有需要注意的地方,比如上方那張圖,從正常到不正常(字型邊框模糊),其實也就是font-weight設定的問題,由於font的繼承性關係,所以很容易出現問題,所以字型圖示樣式初始化的時候將font-style與weight置為important還是很有必要的。

  .ued-components{
    font-family:"fe-components" !important;  //引用字型圖示庫
    font-size:16px;
    font-style:normal !important;  //設定字型樣式
    font-weight:normal !important;  //設定字型加粗程度
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

    <div class="ued-components send-img">&#xe6ae;</div>
複製程式碼

CSS動畫丟幀

clipboard.png

以上是一個用css3 animation 做的一個演示動畫,如果仔細看,可以感覺到那種車速和檔位不匹配的那種感覺,就是抖、抖、抖,看一下css程式碼的實現:

.logo-animate {
    position: relative;
    line-height: 0;
    width: 240px;
    animation: move-float 8s linear 0s infinite;
}

    @keyframes move-float {
        0% {
            margin-top:0;
        }
        50% {
            margin-top:-22px;
        }
        100% {
            margin-top:0;
        }
    }
複製程式碼

在過往依賴jQuery的animate做圖片輪播和列表輪播時,習慣於用margin來做位移。但是用純css來做得時候,發現實現有明顯的卡頓。後面一檢視了一篇文章,發現css的animation實現最好依賴於transform來做,避免使用height,width,margin,padding等,具體原因在前面文章中有提到。所以程式碼優化一下,就是下面這樣:

  @keyframes move-float {
    0% {
        transform: translate(95px, 0);
    }
    50% {
        transform: translate(95px, -22px);
    }
    100% {
        transform: translate(95px, 0);
    }
}
複製程式碼

字型溢位省略號的使用

clipboard.png

如上圖展示的那樣,當我文字過多時,需要截斷文字,使用省略號來保證正常的顯示效果。用css的實現基本都是下面這段程式碼:

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
複製程式碼

但是遇到table的情況,儘管你設定了td的width屬性,但還是不起作用。這是因為table佈局的流體屬性,其會根據內容再重新分配空間,所以還需要加上一個table-layout:fixed 這樣的屬性設定。其實除了單行可以用css做文字擷取,多行也可以,只是在相容性上和效果上還不足以線上上環境來使用。但是實現思路還是可以看一看:

display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
複製程式碼
clipboard.png

因使用了WebKit的CSS擴充套件屬性,該方法適用於WebKit瀏覽器及移動端,但是要想做到相容及顯示效果完美,還是用css配合js來做,單行css來做已足夠,但記得設定title屬性,保證hover能讀到完整的資訊。

作為一個寫CSS不到兩年的前端,在工作中吃了很多基礎不紮實的虧。學CSS也不如JS那樣簡單-知識成體系,所以除了看完一本CSS基礎知識的書以外,更多的還是寫、寫、寫,然後思考,嘗試用不同的思路來解決。