為什麼CSS中的calc函數可能會不生效?

2022-10-31 12:01:09

前言

在早期如果想要對某一些樣式進行動態計算,絕大多數的做法都是使用JavaScript來進行,當時的CSS在面對這種場景顯得有點無能為力。但是,當CSS3中新增了calc函數時,面對這種場景,JavaScript不再是我們的第一選擇,我們只用 CSS 就可以進行相當複雜的計算了。在使用calc的過程中,相信大家或多或少都遇到過下面這些「坑」。

如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新文章~

常見的「坑」

先來介紹css使用calc無效的兩種常見情況:

運運算元之間沒加空格

/*錯誤寫法*/
div{
      width: calc(100%-50px);
}
/*正確寫法*/
div{
      width: calc(100% - 50px);
}

這裡錯誤寫法中-兩邊沒加空格,導致width不生效。但並不是所有運運算元間都需要加空格,只有 +- 需要加空格,因為運算允許負數的出現,如:

div{
      width: calc(100% + -50px);
}

所以,為了統一,建議所有運運算元都加上空格,防止記憶混淆。

運算值沒帶單位

我們都知道在寫css時,如果數值為0我們一般會省略它的單位,比如:0px我們一般會直接寫成0。但是在calc()函數中如果0不帶單位,也會導致不生效。

/*錯誤寫法*/
div{
      width: calc(0 + 100px);
}
/*正確寫法*/
div{
      width: calc(0px + 100px);
}

這裡上面的不帶單位的寫法也是不生效的。這裡我相信很多人都會有疑問,為什麼0還需要帶個單位?

這是因為calc() 函數傳入的是一個數學表示式,而表示式的值可以有多種型別,如長度、百分比、角度等。那如果你傳個 0 進去,沒單位的話,怎麼知道這個 0 是屬於什麼型別?

低版本less處理calc衝突

以下程式碼在低版本less中會被編譯成你意想不到的結果:

.box {
  width: calc(100% - 50px)
}

編譯後:

.box {
  width: calc(50%)
}

導致這個結果的原因在於less中有一套自己的運算規則:

less運算(Operations)

算術運運算元 +-*/ 可以對任何數位、顏色或變數進行運算。如果可能的話,算術運運算元在加、減或比較之前會進行單位換算。計算的結果以最左側運算元的單位型別為準。如果單位換算無效或失去意義,則忽略單位。無效的單位換算例如:px 到 cm 或 rad 到 % 的轉換。

// 所有運算元被轉換成相同的單位
@conversion-1: 5cm + 10mm; // 結果是 6cm
@conversion-2: 2 - 3cm - 5mm; // 結果是 -1.5cm

// conversion is impossible
@incompatible-units: 2 + 5px - 3cm; // 結果是 4px

// example with variables
@base: 5%;
@filler: @base * 2; // 結果是 10%
@other: @base + @filler; // 結果是 15%

乘法和除法不作轉換。因為這兩種運算在大多數情況下都沒有意義,一個長度乘以一個長度就得到一個區域,而 CSS 是不支援指定區域的。Less 將按數位的原樣進行操作,並將為計算結果指定明確的單位型別。

所以上面的less會被進行如下處理:

  • 由於100%與50px單位不同,會被轉換成相同的單位%(以最左側運算元的單位型別為準)
  • 再進行減法運算得到50%

解決方案

  • 跳脫

跳脫(Escaping)允許你使用任意字串作為屬性或變數值。任何 ~"anything"~'anything' 形式的內容都將按原樣輸出,除非 interpolation

我們希望less不要幫我們處理,所以這裡我們可以使用less的跳脫語法讓calc函數原樣輸出就好了

.box {
  width: calc(~"100% - 50px")
}
  • 升級lessless-loader

瞭解calc函數

CSS calc 函數用於在指定 CSS 屬性值時執行計算。它可以用於可以使用任何數值的地方。它將表示式作為引數,並將結果用作使用它的 CSS 屬性的值。我們可以用它進行加法+、減法-、乘法*和除法/

語法

/* property: calc(expression) */
width: calc(100% - 80px);

calc() 函數用一個表示式作為它的引數,用這個表示式的結果作為值。這個表示式可以是任何如下操作符的組合,採用標準操作符處理法則的簡單表示式。

  • + 加法
  • - 減法
  • * 乘法,乘數中至少有一個是 number
  • / 除法,除數必須是number

規則

  • +- 運運算元的兩邊必須要有空白字元。 比如,calc(50% -8px) 會被解析成為一個無效的表示式,解析結果是:一個百分比 後跟一個負數長度值。而加有空白字元的、有效的表示式 calc(8px + -50%) 會被解析成為:一個長度 後跟一個加號 再跟一個負百分比。
  • */ 這兩個運運算元前後不需要空白字元,但如果考慮到統一性,仍然推薦加上空白符。
  • 用 0 作除數會使 HTML 解析器丟擲異常。
  • 涉及自動佈局和固定佈局的表格中的表列、表列組、錶行、錶行組和表單元格的寬度和高度百分比的數學表示式,auto 可視為已指定。
  • calc() 函數支援巢狀,但支援的方式是:把被巢狀的 calc() 函數全當成普通的括號。

解惑

想要了解前面那些坑產生的原因,我們得先了解CSS中的基礎語法與資料型別:

DIMENSION語法

DIMENSION    {num}{ident}

這裡的num值的是數位,那麼ident代表什麼呢,這個我們也可以在CSS規範中找到答案

ident

ident    [-]?{nmstart}{nmchar}*

nmstart和nmchar

nmstart [_a-z]|{nonascii}|{escape}

nmchar [_a-z0-9-]|{nonascii}|{escape}

解惑calc(100%-50px)

瞭解完CSS的基礎語法與資料結構,我們現在可以來看看解析器是如何解析calc(100%-50px)的。

  • 首先DIMENSION語法,{num}{ident}會將其分割為num:100ident:%和-100px
  • %是單位,這個應該沒有疑問
  • -100px這個符合nmchar語法,所以沒有將其拆開,而是保留作為單位解析,但CSS中沒有-100px這個單位,所以這個表示式不會生效

相容性

calc 函數具有驚人的瀏覽器支援,一直到 IE9。如果你用舊版瀏覽器或 Opera Mini 編寫程式碼,請考慮使用數值作為後備。

最後

喜歡的同學歡迎點個贊呀,想要檢視原始碼的同學快來公眾號回覆碎片吧~

原文首發地址點這裡,歡迎大家關注公眾號 「前端南玖」,如果你想進前端交流群一起學習,請點這裡

我是南玖,我們下期見!!!