MoreRSS

site iconJerry Wong

热门Hexo 主题 Butterfly 的作者。Base HK.
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Jerry Wong的 RSS 预览

CrazyWong AI LOGO

2024-07-09 01:20:14

🎧 Anson Seabra - Keep Your Head Up Princess

2021-11-28 14:27:36

My Princess 別低頭 皇冠會掉


When she was younger she would pretend
在她小的時候 她會假裝

That her bedroom was a castle she was fairest in the land
她的卧室是一座城堡 她是這個國度裏最美麗的女孩

And she got older and it all changed
她漸漸長大 一切都物是人非

There was no time for make believe and all the magic slipped away
沒有時間徜徉在幻想裏 所有魔力悄然流逝

Until the light in her eyes it was all but gone
直到她眼中的光芒消失殆盡

‘Cause all the dreams that she had turned out to be wrong
因為她擁有的所有夢想不過都是幼稚可笑的錯誤

So keep your head up princess ‘fore your crown falls
昂首闊步 別讓頭頂的王冠掉落

Know these voices in your head will be your downfall
縈繞腦海裏那些質疑聲音 只會讓你的心不堪一擊

I know it gets so hard but you don’t got far to go
我知道這萬分艱難 而前路並非遙不可及

Yeah keep your head up princess it’s a long road
昂首闊步 這是條漫長的道路

And the path leads right to where they won’t go
是他們都不會選擇去走的一條路

I know it hurts right now but I know you’ll make it home
路程艱辛 但我相信你定會到達終點 載譽歸來

So keep your head up
堅定腳步

Yeah keep your head up
不忘初心

And now she’s grown up works at a bar
現在她長大成人 在酒吧找到一份工作

She traded makeshift gowns for serving rounds from sunrise ‘til it’s dark
她穿着簡單質樸的衣服 披星戴月地賣力工作

And all her friends got someone to hold
她所有的朋友都已找到歸宿

And she’s got no one else still not prepared to make it on her own
可她孑然一身 依然沒有準備好自力更生

And now the light in her eyes it’s now all but gone
現在她眼中的光芒消失殆盡

‘Cause all the dreams that she had turned out to be wrong
因為她擁有的所有夢想不過都是幼稚可笑的錯誤

So keep your head up princess ‘fore your crown falls
昂首闊步 別讓頭頂的王冠掉落

Know these voices in your head will be your downfall
縈繞腦海裏那些質疑聲音 只會讓你的心不堪一擊

I know it gets so hard but you don’t got far to go
我知道這萬分艱難 而前路並非遙不可及

Yeah keep your head up princess it’s a long road
昂首闊步 這是條漫長的道路

And the path leads right to where they won’t go
是他們都不會選擇去走的一條路

I know it hurts right now but I know you’ll make it home
路程艱辛 但我相信你定會到達終點 載譽歸來

So keep your head up
堅定腳步

Yeah keep your head up
不忘初心

One day you’ll find your way back to the start
總有一天 你會找到方向 回到原點

One day you’ll live in your dreams
總有一天 你會沉浸在你的美夢裏

One day you’ll wake up and girl you’ll be a queen
總有一天 你會醒來 你將成為無與倫比的女王

So keep your head up princess ‘fore your crown falls
昂首闊步 別讓頭頂的王冠掉落

Know these voices in your head will be your downfall
縈繞腦海裏那些質疑聲音 只會讓你的心不堪一擊

I know it gets so hard but you don’t got far to go
我知道這萬分艱難 而前路並非遙不可及

Yeah keep your head up princess it’s a long road
昂首闊步 這是條漫長的道路

And the path leads right to where they won’t go
是他們都不會選擇去走的一條路

I know it hurts right now but I know you’ll make it home
路程艱辛 但我相信你定會到達終點 載譽歸來

So keep your head up
堅定腳步

Yeah keep your head up
不忘初心

是的,我又更換了域名

2021-11-10 19:25:24

年初才把域名換到了 immyw.com, 還沒到一年,又把域名換到了 crazywong.com

之前爲了安全起見,給 Godaddy 開了兩步認證,選擇了 Microsoft Authenticator 儲存驗證。然後倒霉的事情來了,一次不小心的操作,把 Microsoft Authenticator 給刪了,本想著有備份,重新下載軟件再恢復就行。結果可想而知,備份丟了。無奈之下,只能一個個去申訴回來。

Godaddy 的要求比較嚴格,需要提供由政府發行之含照身份證明卡片的彩色影本。能提供的不多,無非就是身份證和護照。不明白現在的網絡,動不動就需要實名認證,上傳身份證。甚至有些還要人臉識別才能使用。本著能不提交就不提交的態度,就算是大公司,也有可能泄露個人資料的時候。

似乎沒有別的方法恢復我的賬號了,只能按要求傳了護照過去。等待了 3.4 天之後,收到了 godaddy 的回信。要求上傳一張手持護照的照片。

手持照片這事就超過我的承受範圍。早知道會這樣,一開始就不會傳護照照片過去了。最終還是考慮放棄這個域名,還好當初有把 renew 給取消了。

心儀的域名都被注冊了,最後選擇了 crazywong.com.

Crazy 是形容詞

Wong 是黃(姓)

爲了方便域名記憶,我把博客名字也改成 CrazyWong.

以後設置兩步認證時,真要把二維碼也保存一份,不然就真的 Game Over 了。

1.5 萬字 CSS 基礎拾遺(核心知識、常見需求)

2021-03-21 21:18:44

本文轉自微信公衆號 - 大海我來了
原文地址 1.5 萬字 CSS 基礎拾遺(核心知識、常見需求)

本篇文章圍繞了 CSS 的核心知識點和項目中常見的需求來展開。雖然行文偏長,但偏基礎,適合初級中級前端閲讀,閲讀的時候請適當跳過已經掌握的部分。

這篇文章斷斷續續寫了比較久,也參考了許多優秀的文章,但或許文章裏還是存在不好或不對的地方,請多多指教,可以評論裏直接提出來哈。

小 tip:後續內容更精彩哦。

核心概念和知識點

語法

CSS 的核心功能是將 CSS 屬性設定為特定的值。一個屬性與值的鍵值對被稱為聲明(declaration)。

1
color: red;

而如果將一個或者多個聲明用 {} 包裹起來後,那就組成了一個聲明塊(declaration block)。

1
2
3
4
{
    color: red;
    text-align: center;
}

聲明塊如果需要作用到對應的 HTML 元素,那還需要加上選擇器。選擇器和聲明塊組成了 CSS 規則集(CSS ruleset),常簡稱為 CSS 規則。

1
2
3
4
span {
    color: red;
    text-align: center;
}

規則集中最後一條聲明可以省略分號,但是並不建議這麼做,因為容易出錯。

CSS 中的註釋

1
2
3
4
5
6
/* 單行註釋 */

/*
    多行
    註釋
*/

在 CSS 文件中,除了註釋、CSS 規則集以及 @規則 外,定義的一些別的東西都將被瀏覽器忽略。

@規則

CSS 規則是樣式表的主體,通常樣式表會包括大量的規則列表。但有時候也需要在樣式表中包括其他的一些信息,比如字符集,導入其它的外部樣式表,字體等,這些需要專門的語句表示。

而 @規則 就是這樣的語句。CSS 裏包含了以下 @規則:

  • @namespace 告訴 CSS 引擎必須考慮 XML 命名空間。

  • @media, 如果滿足媒體查詢的條件則條件規則組裏的規則生效。

  • @page, 描述打印文檔時佈局的變化.

  • @font-face, 描述將下載的外部的字體。

  • @keyframes, 描述 CSS 動畫的關鍵幀。

  • @document, 如果文檔樣式表滿足給定條件則條件規則組裏的規則生效。 (推延至 CSS Level 4 規範)

除了以上這幾個之外,下面還將對幾個比較生澀的 @規則 進行介紹。

@charset

@charset 用於定義樣式表使用的字符集。它必須是樣式表中的第一個元素。如果有多個 @charset 被聲明,只有第一個會被使用,而且不能在 HTML 元素或 HTML 頁面的 <style> 元素內使用。

注意:值必須是雙引號包裹,且和

1
@charset "UTF-8";

平時寫樣式文件都沒寫 @charset 規則,那這個 CSS 文件到底是用的什麼字符編碼的呢?

某個樣式表文件到底用的是什麼字符編碼,瀏覽器有一套識別順序(優先級由高到低):

  • 文件開頭的 Byte order mark 字符值,不過一般編輯器並不能看到文件頭裏的 BOM 值;

  • HTTP 響應頭裏的 content-type 字段包含的 charset 所指定的值,比如:

    1
    Content-Type: text/css; charset=utf-8
  • CSS 文件頭裏定義的 @charset 規則裏指定的字符編碼;

  • <link> 標籤裏的 charset 屬性,該條已在 HTML5 中廢除;

  • 默認是 UTF-8

@import

@import 用於告訴 CSS 引擎引入一個外部樣式表。

link 和 @import 都能導入一個樣式文件,它們有什麼區別嘛?

  • link 是 HTML 標籤,除了能導入 CSS 外,還能導入別的資源,比如圖片、腳本和字體等;而 @import 是 CSS 的語法,只能用來導入 CSS;

  • link 導入的樣式會在頁面加載時同時加載,@import 導入的樣式需等頁面加載完成後再加載;

  • link 沒有兼容性問題,@import 不兼容 ie5 以下;

  • link 可以通過 JS 操作 DOM 動態引入樣式表改變樣式,而 @import 不可以。

@supports

@supports 用於查詢特定的 CSS 是否生效,可以結合 not、and 和 or 操作符進行後續的操作。

1
2
3
4
5
6
/* 如果支持自定義屬性,則把 body 顏色設置為變量 varName 指定的顏色 */
@supports (--foo: green) {
    body {
        colorvar(--varName);
    }
}

層疊性

層疊樣式表,這裏的層疊怎麼理解呢?其實它是 CSS 中的核心特性之一,用於合併來自多個源的屬性值的算法。比如説針對某個 HTML 標籤,有許多的 CSS 聲明都能作用到的時候,那最後誰應該起作用呢?層疊性説的大概就是這個。

針對不同源的樣式,將按照如下的順序進行層疊,越往下優先級越高:

  • 用户代理樣式表中的聲明 (例如,瀏覽器的默認樣式,在沒有設置其他樣式時使用)。

  • 用户樣式表中的常規聲明 (由用户設置的自定義樣式。由於 Chrome 在很早的時候就放棄了用户樣式表的功能,所以這裏將不再考慮它的排序。)。

  • 作者樣式表中的常規聲明 (這些是我們 Web 開發人員設置的樣式)。

  • 作者樣式表中的 !important 聲明。

  • 用户樣式表中的 !important 聲明 S。

理解層疊性的時候需要結合 CSS 選擇器的優先級以及繼承性來理解。比如針對同一個選擇器,定義在後面的聲明會覆蓋前面的;作者定義的樣式會比默認繼承的樣式優先級更高。

選擇器

CSS 選擇器無疑是其核心之一,對於基礎選擇器以及一些常用偽類必須掌握。下面列出了常用的選擇器。 想要獲取更多選擇器的用法可以看 MDN CSS Selectors

基礎選擇器

  • 標籤選擇器:h1

  • 類選擇器:.checked

  • ID 選擇器:#picker

  • 通配選擇器:*

屬性選擇器

  • [attr]:指定屬性的元素;

  • [attr=val]:屬性等於指定值的元素;

  • [attr*=val]:屬性包含指定值的元素;

  • [attr^=val] :屬性以指定值開頭的元素;

  • [attr$=val]:屬性以指定值結尾的元素;

  • [attr~=val]:屬性包含指定值 (完整單詞) 的元素(不推薦使用);

  • [attr|=val]:屬性以指定值 (完整單詞) 開頭的元素(不推薦使用);

組合選擇器

  • 相鄰兄弟選擇器:A + B

  • 普通兄弟選擇器:A ~ B

  • 子選擇器:A > B

  • 後代選擇器:A B

偽類

條件偽類

  • :lang():基於元素語言來匹配頁面元素;

  • :dir():匹配特定文字書寫方向的元素;

  • :has():匹配包含指定元素的元素;

  • :is():匹配指定選擇器列表裏的元素;

  • :not():用來匹配不符合一組選擇器的元素;

行為偽類

  • :active:鼠標激活的元素;

  • :hover: 鼠標懸浮的元素;

  • ::selection:鼠標選中的元素;

狀態偽類

  • :target:當前錨點的元素;

  • :link:未訪問的鏈接元素;

  • :visited:已訪問的鏈接元素;

  • :focus:輸入聚焦的表單元素;

  • :required:輸入必填的表單元素;

  • :valid:輸入合法的表單元素;

  • :invalid:輸入非法的表單元素;

  • :in-range:輸入範圍以內的表單元素;

  • :out-of-range:輸入範圍以外的表單元素;

  • :checked:選項選中的表單元素;

  • :optional:選項可選的表單元素;

  • :enabled:事件啟用的表單元素;

  • :disabled:事件禁用的表單元素;

  • :read-only:只讀的表單元素;

  • :read-write:可讀可寫的表單元素;

  • :blank:輸入為空的表單元素;

  • :current():瀏覽中的元素;

  • :past():已瀏覽的元素;

  • :future():未瀏覽的元素;

結構偽類

  • :root:文檔的根元素;

  • :empty:無子元素的元素;

  • :first-letter:元素的首字母;

  • :first-line:元素的首行;

  • :nth-child(n):元素中指定順序索引的元素;

  • :nth-last-child(n):元素中指定逆序索引的元素;;

  • :first-child:元素中為首的元素;

  • :last-child :元素中為尾的元素;

  • :only-child:父元素僅有該元素的元素;

  • :nth-of-type(n):標籤中指定順序索引的標籤;

  • :nth-last-of-type(n):標籤中指定逆序索引的標籤;

  • :first-of-type :標籤中為首的標籤;

  • :last-of-type:標籤中為尾標籤;

  • :only-of-type:父元素僅有該標籤的標籤;

偽元素

  • ::before:在元素前插入內容;

  • ::after:在元素後插入內容;

優先級

優先級就是分配給指定的 CSS 聲明的一個權重,它由匹配的選擇器中的每一種選擇器類型的數值決定。為了記憶,可以把權重分成如下幾個等級,數值越大的權重越高:

  • 10000:!important;

  • 01000:內聯樣式;

  • 00100:ID 選擇器;

  • 00010:類選擇器、偽類選擇器、屬性選擇器;

  • 00001:元素選擇器、偽元素選擇器;

  • 00000:通配選擇器、後代選擇器、兄弟選擇器;

可以看到內聯樣式(通過元素中 style 屬性定義的樣式)的優先級大於任何選擇器;而給屬性值加上 !important 又可以把優先級提至最高,就是因為它的優先級最高,所以需要謹慎使用它,以下有些使用注意事項:

  • 一定要優先考慮使用樣式規則的優先級來解決問題而不是 !important;

  • 只有在需要覆蓋全站或外部 CSS 的特定頁面中使用 !important;

  • 永遠不要在你的插件中使用 !important;

  • 永遠不要在全站範圍的 CSS 代碼中使用 !important;

繼承性

在 CSS 中有一個很重要的特性就是子元素會繼承父元素對應屬性計算後的值。比如頁面根元素 html 的文本顏色默認是黑色的,頁面中的所有其他元素都將繼承這個顏色,當申明瞭如下樣式後,H1 文本將變成橙色。

1
2
3
4
5
6
body {
    color: orange;
}
h1 {
    color: inherit;
}

設想一下,如果 CSS 中不存在繼承性,那麼我們就需要為不同文本的標籤都設置一下 color,這樣一來的後果就是 CSS 的文件大小就會無限增大。

CSS 屬性很多,但並不是所有的屬性默認都是能繼承父元素對應屬性的,那哪些屬性存在默認繼承的行為呢?一定是那些不會影響到頁面佈局的屬性,可以分為如下幾類:

  • 字體相關:font-familyfont-stylefont-sizefont-weight 等;

  • 文本相關:text-aligntext-indenttext-decorationtext-shadowletter-spacingword-spacingwhite-spaceline-heightcolor 等;

  • 列表相關:list-stylelist-style-imagelist-style-typelist-style-position 等;

  • 其他屬性:visibilitycursor 等;

對於其他默認不繼承的屬性也可以通過以下幾個屬性值來控制繼承行為:

  • inherit:繼承父元素對應屬性的計算值;

  • initial:應用該屬性的默認值,比如 color 的默認值是 #000

  • unset:如果屬性是默認可以繼承的,則取 inherit 的效果,否則同 initial

  • revert:效果等同於 unset,兼容性差。

文檔流

在 CSS 的世界中,會把內容按照從左到右、從上到下的順序進行排列顯示。正常情況下會把頁面分割成一行一行的顯示,而每行又可能由多列組成,所以從視覺上看起來就是從上到下從左到右,而這就是 CSS 中的流式佈局,又叫文檔流。文檔流就像水一樣,能夠自適應所在的容器,一般它有如下幾個特性:

  • 塊級元素默認會佔滿整行,所以多個塊級盒子之間是從上到下排列的;

  • 內聯元素默認會在一行裏一列一列的排布,當一行放不下的時候,會自動切換到下一行繼續按照列排布;

如何脱離文檔流呢?

脱流文檔流指節點脱流正常文檔流後,在正常文檔流中的其他節點將忽略該節點並填補其原先空間。文檔一旦脱流,計算其父節點高度時不會將其高度納入,脱流節點不佔據空間。有兩種方式可以讓元素脱離文檔流:浮動和定位。

  • 使用浮動(float)會將元素脱離文檔流,移動到容器左 / 右側邊界或者是另一個浮動元素旁邊,該浮動元素之前佔用的空間將被別的元素填補,另外浮動之後所佔用的區域不會和別的元素之間發生重疊;

  • 使用絕對定位(position: absolute;)或者固定定位(position: fixed;)也會使得元素脱離文檔流,且空出來的位置將自動被後續節點填補。

盒模型

在 CSS 中任何元素都可以看成是一個盒子,而一個盒子是由 4 部分組成的:內容(content)、內邊距(padding)、邊框(border)和外邊距(margin)。

盒模型有 2 種:標準盒模型和 IE 盒模型,本別是由 W3C 和 IExplore 制定的標準。

如果給某個元素設置如下樣式:

1
2
3
4
5
6
7
.box {
    width200px;
    height200px;
    padding10px;
    border1px solid #eee;
    margin10px;
}

標準盒模型認為:盒子的實際尺寸 = 內容(設置的寬 / 高) + 內邊距 + 邊框

所以 .box 元素內容的寬度就為 200px,而實際的寬度則是 width + padding-left + padding-right + border-left-width + border-right-width = 200 + 10 + 10 + 1 + 1 = 222。

IE 盒模型認為:盒子的實際尺寸 = 設置的寬 / 高 = 內容 + 內邊距 + 邊框

.box 元素所佔用的實際寬度為 200px,而內容的真實寬度則是 width - padding-left - padding-right - border-left-width - border-right-width = 200 - 10 - 10 - 1 - 1 = 178。

現在高版本的瀏覽器基本上默認都是使用標準盒模型,而像 IE6 這種老古董才是默認使用 IE 盒模型的。

在  CSS3 中新增了一個屬性 box-sizing,允許開發者來指定盒子使用什麼標準,它有 2 個值:

  • content-box:標準盒模型;

  • border-box:IE 盒模型;

視覺格式化模型

視覺格式化模型(Visual formatting model)是用來處理和在視覺媒體上顯示文檔時使用的計算規則。CSS 中一切皆盒子,而視覺格式化模型簡單來理解就是規定這些盒子應該怎麼樣放置到頁面中去,這個模型在計算的時候會依賴到很多的因素,比如:盒子尺寸、盒子類型、定位方案(是浮動還是定位)、兄弟元素或者子元素以及一些別的因素。

從上圖中可以看到視覺格式化模型涉及到的內容很多,有興趣深入研究的可以結合上圖看這個 W3C 的文檔 Visual formatting model。所以這裏就簡單介紹下盒子類型。

盒子類型由 display 決定,同時給一個元素設置 display 後,將會決定這個盒子的 2 個顯示類型(display type):

  • outer display type(對外顯示):決定了該元素本身是如何佈局的,即參與何種格式化上下文;

  • inner display type(對內顯示):其實就相當於把該元素當成了容器,規定了其內部子元素是如何佈局的,參與何種格式化上下文;

outer display type

對外顯示方面,盒子類型可以分成 2 類:block-level box(塊級盒子) 和 inline-level box(行內級盒子)。

依據上圖可以列出都有哪些塊級和行內級盒子:

  • 塊級盒子:display 為 block、list-item、table、flex、grid、flow-root 等;

  • 行內級盒子:display 為 inline、inline-block、inline-table 等;

所有塊級盒子都會參與 BFC,呈現垂直排列;而所有行內級盒子都參會 IFC,呈現水平排列。

除此之外,block、inline 和 inline-block 還有什麼更具體的區別呢?

block

  • 佔滿一行,默認繼承父元素的寬度;多個塊元素將從上到下進行排列;

  • 設置 width/height 將會生效;

  • 設置 padding 和 margin 將會生效;

inline

  • 不會佔滿一行,寬度隨着內容而變化;多個 inline 元素將按照從左到右的順序在一行裏排列顯示,如果一行顯示不下,則自動換行;

  • 設置 width/height 將不會生效;

  • 設置豎直方向上的 padding 和 margin 將不會生效;

inline-block

  • 是行內塊元素,不單獨佔滿一行,可以看成是能夠在一行裏進行左右排列的塊元素;

  • 設置 width/height 將會生效;

  • 設置 padding 和 margin 將會生效;

inner display type

對內方面,其實就是把元素當成了容器,裏面包裹着文本或者其他子元素。container box 的類型依據 display 的值不同,分為 4 種:

  • block container:建立 BFC 或者 IFC;

  • flex container:建立 FFC;

  • grid container:建立 GFC;

  • ruby container:接觸不多,不做介紹。

值得一提的是如果把 img 這種替換元素(replaced element)申明為 block 是不會產生 container box 的,因為替換元素比如 img 設計的初衷就僅僅是通過 src 把內容替換成圖片,完全沒考慮過會把它當成容器。

參考:

格式化上下文

格式化上下文(Formatting Context)是 CSS2.1 規範中的一個概念,大概説的是頁面中的一塊渲染區域,規定了渲染區域內部的子元素是如何排版以及相互作用的。

不同類型的盒子有不同格式化上下文,大概有這 4 類:

  • BFC (Block Formatting Context) 塊級格式化上下文;

  • IFC (Inline Formatting Context) 行內格式化上下文;

  • FFC (Flex Formatting Context) 彈性格式化上下文;

  • GFC (Grid Formatting Context) 格柵格式化上下文;

其中 BFC 和 IFC 在 CSS 中扮演着非常重要的角色,因為它們直接影響了網頁佈局,所以需要深入理解其原理。

BFC

塊格式化上下文,它是一個獨立的渲染區域,只有塊級盒子參與,它規定了內部的塊級盒子如何佈局,並且與這個區域外部毫不相干。

BFC 渲染規則

  • 內部的盒子會在垂直方向,一個接一個地放置;

  • 盒子垂直方向的距離由 margin 決定,屬於同一個 BFC 的兩個相鄰盒子的 margin 會發生重疊;

  • 每個元素的 margin 的左邊,與包含塊 border 的左邊相接觸 (對於從左往右的格式化,否則相反),即使存在浮動也是如此;

  • BFC 的區域不會與 float 盒子重疊;

  • BFC 就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。

  • 計算 BFC 的高度時,浮動元素也參與計算。

如何創建 BFC?

  • 根元素:html

  • 非溢出的可見元素:overflow 不為 visible

  • 設置浮動:float 屬性不為 none

  • 設置定位:position 為 absolute 或 fixed

  • 定義成塊級的非塊級元素:display: inline-block/table-cell/table-caption/flex/inline-flex/grid/inline-grid

BFC 應用場景

1、 自適應兩欄佈局

應用原理:BFC 的區域不會和浮動區域重疊,所以就可以把側邊欄固定寬度且左浮動,而對右側內容觸發 BFC,使得它的寬度自適應該行剩餘寬度。

1
2
3
4
<div class="layout">
    <div class="aside">aside</div>
    <div class="main">main</div>
</div>
1
2
3
4
5
6
7
8
.aside {
    float: left;
    width100px;
}
.main {
    <!-- 觸發 BFC -->
    overflow: auto;
}

2、清除內部浮動

浮動造成的問題就是父元素高度坍塌,所以清除浮動需要解決的問題就是讓父元素的高度恢復正常。而用     BFC 清除浮動的原理就是:計算 BFC 的高度時,浮動元素也參與計算。只要觸發父元素的 BFC 即可。

1
2
3
.parent {
    overflow: hidden;
}

3、 防止垂直 margin 合併

BFC 渲染原理之一:同一個 BFC 下的垂直 margin 會發生合併。所以如果讓 2 個元素不在同一個 BFC 中即可阻止垂直 margin 合併。那如何讓 2 個相鄰的兄弟元素不在同一個 BFC 中呢?可以給其中一個元素外面包裹一層,然後觸發其包裹層的 BFC,這樣一來 2 個元素就不會在同一個 BFC 中了。

1
2
3
4
5
6
<div class="layout">
    <div class="a">a</div>
    <div class="contain-b">
        <div class="b">b</div>
    </div>
</div>
1
2
3
4
5
6
7
8
.demo3 .a,
.demo3 .b {
    border1px solid #999;
    margin10px;
}
.contain-b {
    overflow: hidden;
}

針對以上 3 個 示例 ,可以結合這個 BFC 應用示例 配合觀看更佳。

參考:CSS 原理 - Formatting Context

IFC

IFC 的形成條件非常簡單,塊級元素中僅包含內聯級別元素,需要注意的是當 IFC 中有塊級元素插入時,會產生兩個匿名塊將父元素分割開來,產生兩個 IFC。

IFC 渲染規則

  • 子元素在水平方向上一個接一個排列,在垂直方向上將以容器頂部開始向下排列;

  • 節點無法聲明寬高,其中 margin 和 padding 在水平方向有效在垂直方向無效;

  • 節點在垂直方向上以不同形式對齊;

  • 能把在一行上的框都完全包含進去的一個矩形區域,被稱為該行的線盒(line box)。線盒的寬度是由包含塊(containing box)和與其中的浮動來決定;

  • IFC 中的 line box 一般左右邊貼緊其包含塊,但 float 元素會優先排列。

  • IFC 中的 line box 高度由 line-height 計算規則來確定,同個 IFC 下的多個 line box 高度可能會不同;

  • 當內聯級盒子的總寬度少於包含它們的 line box 時,其水平渲染規則由 text-align 屬性值來決定;

  • 當一個內聯盒子超過父元素的寬度時,它會被分割成多盒子,這些盒子分佈在多個 line box 中。如果子元素未設置強制換行的情況下,inline box 將不可被分割,將會溢出父元素。

針對如上的 IFC 渲染規則,你是不是可以分析下下面這段代碼的 IFC 環境是怎麼樣的呢?

1
<p>It can get <strong>very complicated</storng> once you start looking into it.</p>

對應上面這樣一串 HTML 分析如下:

  • p 標籤是一個 block container,對內將產生一個 IFC;

  • 由於一行沒辦法顯示完全,所以產生了 2 個線盒(line box);線盒的寬度就繼承了 p 的寬度;高度是由裏面的內聯盒子的 line-height 決定;

  • It can get:匿名的內聯盒子;

  • very complicated:strong 標籤產生的內聯盒子;

  • once you start:匿名的內聯盒子;

  • looking into it.:匿名的內聯盒子。

參考:Inline formatting contexts

IFC 應用場景

  • 水平居中:當一個塊要在環境中水平居中時,設置其為 inline-block 則會在外層產生 IFC,通過 text-align 則可以使其水平居中。

  • 垂直居中:創建一個 IFC,用其中一個元素撐開父元素的高度,然後設置其 vertical-align: middle,其他行內元素則可以在此父元素下垂直居中。

偷個懶,demo 和圖我就不做了。

層疊上下文

在電腦顯示屏幕上的顯示的頁面其實是一個三維的空間,水平方向是 X 軸,豎直方向是 Y 軸,而屏幕到眼睛的方向可以看成是 Z 軸。眾 HTML 元素依據自己定義的屬性的優先級在 Z 軸上按照一定的順序排開,而這其實就是層疊上下文所要描述的東西。

我們對層疊上下文的第一印象可能要來源於 z-index,認為它的值越大,距離屏幕觀察者就越近,那麼層疊等級就越高,事實確實是這樣的,但層疊上下文的內容遠非僅僅如此:

  • z-index 能夠在層疊上下文中對元素的堆疊順序其作用是必須配合定位才可以;

  • 除了 z-index 之外,一個元素在 Z 軸上的顯示順序還受層疊等級和層疊順序影響;

在看層疊等級和層疊順序之前,我們先來看下如何產生一個層疊上下文,特定的 HTML 元素或者 CSS 屬性產生層疊上下文,MDN 中給出了這麼一個列表,符合以下任一條件的元素都會產生層疊上下文:

  • html 文檔根元素

  • 聲明 position: absolute/relative 且 z-index 值不為 auto 的元素;

  • 聲明 position: fixed/sticky 的元素;

  • flex 容器的子元素,且 z-index 值不為 auto;

  • grid 容器的子元素,且 z-index 值不為 auto;

  • opacity 屬性值小於 1 的元素;

  • mix-blend-mode 屬性值不為 normal 的元素;

  • 以下任意屬性值不為 none 的元素:

    • transform

    • filter

    • perspective

    • clip-path

    • mask / mask-image / mask-border

  • isolation 屬性值為 isolate 的元素;

  • -webkit-overflow-scrolling 屬性值為 touch 的元素;

  • will-change 值設定了任一屬性而該屬性在 non-initial 值時會創建層疊上下文的元素;

  • contain 屬性值為 layout、paint 或包含它們其中之一的合成值(比如 contain: strict、contain: content)的元素。

層疊等級

層疊等級指節點在三維空間 Z 軸上的上下順序。它分兩種情況:

  • 在同一個層疊上下文中,它描述定義的是該層疊上下文中的層疊上下文元素在 Z 軸上的上下順序;

  • 在其他普通元素中,它描述定義的是這些普通元素在 Z 軸上的上下順序;

普通節點的層疊等級優先由其所在的層疊上下文決定,層疊等級的比較只有在當前層疊上下文中才有意義,脱離當前層疊上下文的比較就變得無意義了。

層疊順序

在同一個層疊上下文中如果有多個元素,那麼他們之間的層疊順序是怎麼樣的呢?

以下這個列表越往下層疊優先級越高,視覺上的效果就是越容易被用户看到(不會被其他元素覆蓋):

  • 層疊上下文的 border 和 background

  • z-index < 0 的子節點

  • 標準流內塊級非定位的子節點

  • 浮動非定位的子節點

  • 標準流內行內非定位的子節點

  • z-index: auto/0 的子節點

  • z-index > 0 的子節點

如何比較兩個元素的層疊等級?

  • 在同一個層疊上下文中,比較兩個元素就是按照上圖的介紹的層疊順序進行比較。

  • 如果不在同一個層疊上下文中的時候,那就需要比較兩個元素分別所處的層疊上下文的等級。

  • 如果兩個元素都在同一個層疊上下文,且層疊順序相同,則在 HTML 中定義越後面的層疊等級越高。

參考:徹底搞懂 CSS 層疊上下文、層疊等級、層疊順序、z-index

值和單位

CSS 的聲明是由屬性和值組成的,而值的類型有許多種:

  • 數值:長度值 ,用於指定例如元素 width、border-width、font-size 等屬性的值;

  • 百分比:可以用於指定尺寸或長度,例如取決於父容器的 width、height 或默認的 font-size;

  • 顏色:用於指定 background-color、color 等;

  • 座標位置:以屏幕的左上角為座標原點定位元素的位置,比如常見的 background-position、top、right、bottom 和 left 等屬性;

  • 函數:用於指定資源路徑或背景圖片的漸變,比如 url()、linear-gradient() 等;

而還有些值是需要帶單位的,比如 width: 100px,這裏的 px 就是表示長度的單位,長度單位除了 px 外,比較常用的還有 em、rem、vw/vh 等。那他們有什麼區別呢?又應該在什麼時候使用它們呢?

px

屏幕分辨率是指在屏幕的橫縱方向上的像素點數量,比如分辨率 1920×1080 意味着水平方向含有 1920 個像素數,垂直方向含有 1080 個像素數。

而 px 表示的是 CSS 中的像素,在 CSS 中它是絕對的長度單位,也是最基礎的單位,其他長度單位會自動被瀏覽器換算成 px。但是對於設備而言,它其實又是相對的長度單位,比如寬高都為 2px,在正常的屏幕下,其實就是 4 個像素點,而在設備像素比 (devicePixelRatio) 為 2 的 Retina 屏幕下,它就有 16 個像素點。所以屏幕尺寸一致的情況下,屏幕分辨率越高,顯示效果就越細膩。

講到這裏,還有一些相關的概念需要理清下:

設備像素(Device pixels)

設備屏幕的物理像素,表示的是屏幕的橫縱有多少像素點;和屏幕分辨率是差不多的意思。

設備像素比(DPR)

設備像素比表示 1 個 CSS 像素等於幾個物理像素。

計算公式:DPR = 物理像素數 / 邏輯像素數;

在瀏覽器中可以通過 window.devicePixelRatio 來獲取當前屏幕的 DPR。

像素密度(DPI/PPI)

像素密度也叫顯示密度或者屏幕密度,縮寫為 DPI(Dots Per Inch) 或者 PPI(Pixel Per Inch)。從技術角度説,PPI 只存在於計算機顯示領域,而 DPI 只出現於打印或印刷領域。

計算公式:像素密度 = 屏幕對角線的像素尺寸 / 物理尺寸

比如,對於分辨率為 750 * 1334 的 iPhone 6 來説,它的像素密度為:

1
Math.sqrt(750 * 750 + 1334 * 1334) / 4.7 = 326ppi

設備獨立像素(DIP)

DIP 是特別針對 Android 設備而衍生出來的,原因是安卓屏幕的尺寸繁多,因此為了顯示能儘量和設備無關,而提出的這個概念。它是基於屏幕密度而計算的,認為當屏幕密度是 160 的時候,px = DIP。

計算公式:dip = px * 160 / dpi

em

em 是 CSS 中的相對長度單位中的一個。居然是相對的,那它到底是相對的誰呢?它有 2 層意思:

  • 在 font-size 中使用是相對於父元素的 font-size 大小,比如父元素 font-size: 16px,當給子元素指定 font-size: 2em 的時候,經過計算後它的字體大小會是 32px;

  • 在其他屬性中使用是相對於自身的字體大小,如 width/height/padding/margin 等;

我們都知道每個瀏覽器都會給 HTML 根元素 html 設置一個默認的 font-size,而這個值通常是 16px。這也就是為什麼 1em = 16px 的原因所在了。

em 在計算的時候是會層層計算的,比如:

1
2
3
<div>
    <p></p>
</div>
1
2
div { font-size2em; }
p { font-size2em; }

對於如上一個結構的 HTML,由於根元素 html 的字體大小是 16px,所以 p 標籤最終計算出來後的字體大小會是 16 * 2 * 2 = 64px

rem

rem(root em) 和 em 一樣,也是一個相對長度單位,不過 rem 相對的是 HTML 的根元素 html。

rem 由於是基於 html 的 font-size 來計算,所以通常用於自適應網站或者 H5 中。

比如在做 H5 的時候,前端通常會讓 UI 給 750px 寬的設計圖,而在開發的時候可以基於 iPhone X 的尺寸 375px * 812px 來寫頁面,這樣一來的話,就可以用下面的 JS 依據當前頁面的視口寬度自動計算出根元素 html 的基準 font-size 是多少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(function (doc, win) {
    var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
        psdWidth = 750,  // 設計圖寬度
        recalc = function () {
            var clientWidth = docEl.clientWidth;
            if ( !clientWidth ) return;
            if ( clientWidth >= 640 ) {
                docEl.style.fontSize = 200 * ( 640 / psdWidth ) + 'px';
            } else {
                docEl.style.fontSize = 200 * ( clientWidth / psdWidth ) + 'px';
            }
        };

    if ( !doc.addEventListener ) return;
    // 綁定事件的時候最好配合防抖函數
    win.addEventListener( resizeEvt, debounce(recalc, 1000), false );
    doc.addEventListener'DOMContentLoaded', recalc, false );
    
    function debounce(func, wait) {
        var timeout;
        return function () {
            var context = this;
            var args = arguments;
            clearTimeout(timeout)
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
})(documentwindow);

比如當視口是 375px 的時候,經過計算 html 的 font-size 會是 100px,這樣有什麼好處呢?好處就是方便寫樣式,比如從設計圖量出來的 header 高度是 50px 的,那我們寫樣式的時候就可以直接寫:

1
2
3
header {
    height0.5rem;
}

每個從設計圖量出來的尺寸只要除於 100 即可得到當前元素的 rem 值,都不用經過計算,非常方便。偷偷告訴你,如果你把上面那串計算 html 標籤 font-size 的 JS 代碼中的 200 替換成 2,那在計算 rem 的時候就不需要除於 100 了,從設計圖量出多大 px,就直接寫多少個 rem。

vw/vh

vw 和 vh 分別是相對於屏幕視口寬度和高度而言的長度單位:

  • 1vw = 視口寬度均分成 100 份中 1 份的長度;

  • 1vh = 視口高度均分成 100 份中 1 份的長度;

在 JS 中 100vw = window.innerWidth,100vh = window.innerHeight。

vw/vh 的出現使得多了一種寫自適應佈局的方案,開發者不再侷限於 rem 了。

相對視口的單位,除了 vw/vh 外,還有 vmin 和 vmax:

  • vmin:取 vw 和 vh 中值較小的;

  • vmax:取 vw 和 vh 中值較大的;

顏色體系

CSS 中用於表示顏色的值種類繁多,足夠構成一個體系,所以這裏就專門拿出一個小節來講解它。

根據 CSS 顏色草案 中提到的顏色值類型,大概可以把它們分為這幾類:

  • 顏色關鍵字

  • transparent 關鍵字

  • currentColor 關鍵字

  • RGB 顏色

  • HSL 顏色

顏色關鍵字

顏色關鍵字(color keywords)是不區分大小寫的標識符,它表示一個具體的顏色,比如 white(白),黑(black)等;

可接受的關鍵字列表在 CSS 的演變過程中發生了改變:

  • CSS 標準 1 只接受 16 個基本顏色,稱為 VGA 顏色,因為它們來源於 VGA 顯卡所顯示的顏色集合而被稱為 VGA colors (視頻圖形陣列色彩)。

  • CSS 標準 2 增加了 orange 關鍵字。

  • 從一開始,瀏覽器接受其它的顏色,由於一些早期瀏覽器是 X11 應用程序,這些顏色大多數是 X11 命名的顏色列表,雖然有一點不同。SVG 1.0 是首個正式定義這些關鍵字的標準;CSS 色彩標準 3 也正式定義了這些關鍵字。它們經常被稱作擴展的顏色關鍵字, X11 顏色或 SVG 顏色 。

  • CSS 顏色標準 4 添加可 rebeccapurple 關鍵字來紀念 web 先鋒 Eric Meyer。

如下這張圖是 16 個基礎色,又叫 VGA 顏色。截止到目前為止 CSS 顏色關鍵字總共有 146 個,這裏可以查看 完整的色彩關鍵字列表

需要注意的是如果聲明的時候的顏色關鍵字是錯誤的,瀏覽器會忽略它。

transparent 關鍵字

transparent 關鍵字表示一個完全透明的顏色,即該顏色看上去將是背景色。從技術上説,它是帶有 alpha 通道為最小值的黑色,是 rgba(0,0,0,0) 的簡寫。

透明關鍵字有什麼應用場景呢?

實現三角形

下面這個圖是用 4 條邊框填充的正方形,看懂了它你大概就知道該如何用 CSS 寫三角形了。

1
2
3
4
5
6
7
8
div {
    border-top-color#ffc107;
    border-right-color#00bcd4;
    border-bottom-color#e26b6b;
    border-left-color#cc7cda;
    border-width50px;
    border-style: solid;
}

用 transparent 實現三角形的原理:

  • 首先寬高必須是 0px,通過邊框的粗細來填充內容;

  • 那條邊需要就要加上顏色,而不需要的邊則用 transparent;

  • 想要什麼樣姿勢的三角形,完全由上下左右 4 條邊的中有顏色的邊和透明的邊的位置決定;

  • 等腰三角形:設置一條邊有顏色,然後緊挨着的 2 邊是透明,且寬度是有顏色邊的一半;直角三角形:設置一條邊有顏色,然後緊挨着的任何一邊透明即可。

看下示例:

增大點擊區域

常常在移動端的時候點擊的按鈕的區域特別小,但是由於現實效果又不太好把它做大,所以常用的一個手段就是通過透明的邊框來增大按鈕的點擊區域:

1
2
3
.btn {
    border5px solid transparent;
}

currentColor 關鍵字

currentColor 會取當前元素繼承父級元素的文本顏色值或聲明的文本顏色值,即 computed 後的 color 值。

比如,對於如下 CSS,該元素的邊框顏色會是 red:

1
2
3
4
.btn {
    color: red;
    border1px solid currentColor;
}

RGB[A] 顏色

RGB[A] 顏色是由 R(red)-G(green)-B(blue)-A(alpha) 組成的色彩空間。

在 CSS 中,它有兩種表示形式:

  • 十六進制符號;

  • 函數符;

十六進制符號

RGB 中的每種顏色的值範圍是 00~ff,值越大表示顏色越深。所以一個顏色正常是 6 個十六進制字符加上 # 組成,比如紅色就是 #ff0000。

如果 RGB 顏色需要加上不透明度,那就需要加上 alpha 通道的值,它的範圍也是 00~ff,比如一個帶不透明度為 67% 的紅色可以這樣寫 #ff0000aa。

使用十六進制符號表示顏色的時候,都是用 2 個十六進制表示一個顏色,如果這 2 個字符相同,還可以縮減成只寫 1 個,比如,紅色 #f00;帶 67% 不透明度的紅色 #f00a。

函數符

當 RGB 用函數表示的時候,每個值的範圍是 0255 或者 0%100%,所以紅色是 rgb(255, 0, 0), 或者 rgb(100%, 0, 0)。

如果需要使用函數來表示帶不透明度的顏色值,值的範圍是 01 及其之間的小數或者 0%100%,比如帶 67% 不透明度的紅色是 rgba(255, 0, 0, 0.67) 或者 rgba(100%, 0%, 0%, 67%)

需要注意的是 RGB 這 3 個顏色值需要保持一致的寫法,要嘛用數字要嘛用百分比,而不透明度的值的可以不用和 RGB 保持一致寫法。比如 rgb(100%, 0, 0) 這個寫法是無效的;而 rgb(100%, 0%, 0%, 0.67) 是有效的。

在第 4 代 CSS 顏色標準中,新增了一種新的函數寫法,即可以把 RGB 中值的分隔逗號改成空格,而把 RGB 和 alpha 中的逗號改成 /,比如帶 67% 不透明度的紅色可以這樣寫 rgba(255 0 0 / 0.67)。另外還把 rgba 的寫法合併到 rgb 函數中了,即 rgb 可以直接寫帶不透明度的顏色。

HSL[A] 顏色

HSL[A] 顏色是由色相 (hue)- 飽和度 (saturation)- 亮度 (lightness)- 不透明度組成的顏色體系。

  • 色相(H)是色彩的基本屬性,值範圍是 0360 或者 0deg360deg, 0 (或 360) 為紅色, 120 為綠色, 240 為藍色;

  • 飽和度(S)是指色彩的純度,越高色彩越純,低則逐漸變灰,取 0~100% 的數值;0% 為灰色, 100% 全色;

  • 亮度(L),取 0~100%,0% 為暗,100% 為白;

  • 不透明度(A),取 0100%,或者 01 及之間的小數;

寫法上可以參考 RGB 的寫法,只是參數的值不一樣。

給一個按鈕設置不透明度為 67% 的紅色的 color 的寫法,以下全部寫法效果一致:

1
2
3
4
5
6
7
8
button {
    color#ff0000aa;
    color#f00a;
    colorrgba(255000.67);
    colorrgb(100% 0% 0% / 67%);
    colorhsla(0100%50%67%);
    colorhsl(0deg 100% 50% / 67%);
}

小提示:在 Chrome DevTools 中可以按住 shift + 鼠標左鍵可以切換顏色的表示方式。

媒體查詢

媒體查詢是指針對不同的設備、特定的設備特徵或者參數進行定製化的修改網站的樣式。

你可以通過給 <link> 加上 media 屬性來指定該樣式文件只能對什麼設備生效,不指定的話默認是 all,即對所有設備都生效:

1
2
<link rel="stylesheet" src="styles.css" media="screen" />
<link rel="stylesheet" src="styles.css" media="print" />

都支持哪些設備類型?

  • all:適用於所有設備;

  • print:適用於在打印預覽模式下在屏幕上查看的分頁材料和文檔;

  • screen:主要用於屏幕;

  • speech:主要用於語音合成器。

需要注意的是:通過 media 指定的  資源儘管不匹配它的設備類型,但是瀏覽器依然會加載它。

除了通過 <link> 讓指定設備生效外,還可以通過 @media 讓 CSS 規則在特定的條件下才能生效。響應式頁面就是使用了 @media 才讓一個頁面能夠同時適配 PC、Pad 和手機端。

1
@media (min-width1000px) {}

媒體查詢支持邏輯操作符:

  • and:查詢條件都滿足的時候才生效;

  • not:查詢條件取反;

  • only:整個查詢匹配的時候才生效,常用語兼容舊瀏覽器,使用時候必須指定媒體類型;

  • 逗號或者 or:查詢條件滿足一項即可匹配;

媒體查詢還支持眾多的媒體特性,使得它可以寫出很複雜的查詢條件:

1
2
/* 用户設備的最小高度為680px或為縱向模式的屏幕設備 */
@media (min-height680px), screen and (orientation: portrait) {}

常見需求

自定義屬性

之前我們通常是在預處理器裏才可以使用變量,而現在 CSS 裏也支持了變量的用法。通過自定義屬性就可以在想要使用的地方引用它。

自定義屬性也和普通屬性一樣具有級聯性,申明在 :root 下的時候,在全文檔範圍內可用,而如果是在某個元素下申明自定義屬性,則只能在它及它的子元素下才可以使用。

自定義屬性必須通過 --x 的格式申明,比如:–theme-color: red; 使用自定義屬性的時候,需要用 var 函數。比如:

1
2
3
4
5
6
7
8
9
<!-- 定義自定義屬性 -->
:root {
    --theme-color: red;
}

<!-- 使用變量 -->
h1 {
    colorvar(--theme-color);
}

上圖這個是使用 CSS 自定義屬性配合 JS 實現的動態調整元素的 box-shadow,具體可以看這個 codepen demo

1px 邊框解決方案

Retina 顯示屏比普通的屏幕有着更高的分辨率,所以在移動端的 1px 邊框就會看起來比較粗,為了美觀通常需要把這個線條細化處理。這裏有篇文章列舉了 7 中方案可以參考一下:7 種方法解決移動端 Retina 屏幕 1px 邊框問題

而這裏附上最後一種通過偽類和 transform 實現的相對完美的解決方案:

只設置單條底部邊框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.scale-1px-bottom {
    position: relative;
    border:none;
}
.scale-1px-bottom::after {
    content'';
    position: absolute;
    left0;
    bottom0;
    background#000;
    width100%;
    height1px;
    -webkit-transformscaleY(0.5);
    transformscaleY(0.5);
    -webkit-transform-origin0 0;
    transform-origin0 0;
}

同時設置 4 條邊框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.scale-1px {
    position: relative;
    margin-bottom20px;
    border:none;
}
.scale-1px::after {
    content'';
    position: absolute;
    top0;
    left0;
    border1px solid #000;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    width200%;
    height200%;
    -webkit-transformscale(0.5);
    transformscale(0.5);
    -webkit-transform-origin: left top;
    transform-origin: left top;
}

清除浮動

什麼是浮動:浮動元素會脱離文檔流並向左 / 向右浮動,直到碰到父元素或者另一個浮動元素。

為什麼要清楚浮動,它造成了什麼問題?

因為浮動元素會脱離正常的文檔流,並不會佔據文檔流的位置,所以如果一個父元素下面都是浮動元素,那麼這個父元素就無法被浮動元素所撐開,這樣一來父元素就丟失了高度,這就是所謂的浮動造成的父元素高度坍塌問題。

父元素高度一旦坍塌將對後面的元素佈局造成影響,為了解決這個問題,所以需要清除浮動,讓父元素恢復高度,那該如何做呢?

這裏介紹兩種方法:通過 BFC 來清除、通過 clear 來清除。

BFC 清除浮動

前面介紹 BFC 的時候提到過,計算 BFC 高度的時候浮動子元素的高度也將計算在內,利用這條規則就可以清楚浮動。

假設一個父元素 parent 內部只有 2 個子元素 child,且它們都是左浮動的,這個時候 parent 如果沒有設置高度的話,因為浮動造成了高度坍塌,所以 parent 的高度會是 0,此時只要給 parent 創造一個 BFC,那它的高度就能恢復了。

而產生 BFC 的方式很多,我們可以給父元素設置 overflow: auto 來簡單的實現 BFC 清除浮動,但是為了兼容 IE 最好用 overflow: hidden。

1
2
3
.parent {
    overflow: hidden;
}

通過 overflow: hidden 來清除浮動並不完美,當元素有陰影或存在下拉菜單的時候會被截斷,所以該方法使用比較侷限。

通過 clear 清除浮動

我先把結論貼出來:

1
2
3
4
5
6
7
8
.clearfix {
    zoom: 1;
}
.clearfix::after {
    content"";
    display: block;
    clear: both;
}

這種寫法的核心原理就是通過 ::after 偽元素為在父元素的最後一個子元素後面生成一個內容為空的塊級元素,然後通過 clear 將這個偽元素移動到所有它之前的浮動元素的後面,畫個圖來理解一下。

可以結合這個 codepen demo 一起理解上圖的 clear 清楚浮動原理。

上面這個 demo 或者圖裏為了展示需要所以給偽元素的內容設置為了 ::after,實際使用的時候需要設置為空字符串,讓它的高度為 0,從而父元素的高度都是由實際的子元素撐開。

該方式基本上是現在人人都在用的清除浮動的方案,非常通用。

參考:CSS 中的浮動和清除浮動,梳理一下

消除瀏覽器默認樣式

針對同一個類型的 HTML 標籤,不同的瀏覽器往往有不同的表現,所以在網站製作的時候,開發者通常都是需要將這些瀏覽器的默認樣式清除,讓網頁在不同的瀏覽器上能夠保持一致。

針對清除瀏覽器默認樣式這件事,在很早之前 CSS 大師 Eric A. Meyer 就幹過。它就是寫一堆通用的樣式用來重置瀏覽器默認樣式,這些樣式通常會放到一個命名為 reset.css 文件中。比如大師的 reset.css 是這麼寫的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
htmlbodydivspan, applet, objectiframe,
h1h2h3h4h5h6pblockquote, pre,
aabbr, acronym, address, big, citecode,
deldfnemimginskbdq, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dldtddolulli,
fieldsetformlabellegend,
tablecaptiontbodytfoottheadtrthtd,
articleasidecanvasdetails, embed, 
figurefigcaptionfooterheaderhgroup
menunav, output, ruby, sectionsummary,
timemarkaudiovideo {
    margin0;
    padding0;
    border0;
    font-size100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
articleasidedetailsfigcaptionfigure
footerheaderhgroupmenunavsection {
    display: block;
}
body {
    line-height1;
}
olul {
    list-style: none;
}
blockquoteq {
    quotes: none;
}
blockquote:beforeblockquote:after,
q:beforeq:after {
    content'';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing0;
}

他的這份 reset.css 據説是被使用最廣泛的重設樣式的方案了。

除了 reset.css 外,後來又出現了 Normalize.css 。關於 Normalize.css, 其作者 necolas 專門寫了一篇文章介紹了它,並談到了它和 reset.css 的區別。這個是他寫那篇文章的翻譯版:讓我們談一談 Normalize.css

文章介紹到:Normalize.css 只是一個很小的 CSS 文件,但它在默認的 HTML 元素樣式上提供了跨瀏覽器的高度一致性。相比於傳統的 CSS reset,Normalize.css 是一種現代的、為 HTML5 準備的優質替代方案,現在已經有很多知名的框架和網站在使用它了。

Normalize.css 的具體樣式可以看這裏 Normalize.css

區別於 reset.css,Normalize.css 有如下特點:

  • reset.css 幾乎為所有標籤都設置了默認樣式,而 Normalize.css 則是有選擇性的保護了部分有價值的默認值;

  • 修復了很多瀏覽器的 bug,而這是 reset.css 沒做到的;

  • 不會讓你的調試工具變的雜亂,相反 reset.css 由於設置了很多默認值,所以在瀏覽器調試工具中往往會看到一大堆的繼承樣式,顯得很雜亂;

  • Normalize.css 是模塊化的,所以可以選擇性的去掉永遠不會用到的部分,比如表單的一般化;

  • Normalize.css 有詳細的説明文檔;

長文本處理

默認:字符太長溢出了容器

字符超出部分換行

字符超出位置使用連字符

單行文本超出省略

多行文本超出省略

查看以上這些方案的示例: codepen demo

有意思的是剛好前兩天看到 chokcoco 針對文本溢出也寫了一篇文章,主要突出的是對整塊的文本溢出處理。啥叫整塊文本?比如,下面這種技術標籤就是屬於整塊文本:

另外他還對 iOS/Safari 做了兼容處理,感興趣的可以去閲讀下:CSS 整塊文本溢出省略特性探究

水平垂直居中

讓元素在父元素中呈現出水平垂直居中的形態,無非就 2 種情況:

  • 單行的文本、inline 或者 inline-block 元素;

  • 固定寬高的塊級盒子;

  • 不固定寬高的塊級盒子;

以下列到的所有水平垂直居中方案這裏寫了個 codepen demo,配合示例閲讀效果更佳。

單行的文本、inline 或 inline-block 元素

水平居中

此類元素需要水平居中,則父級元素必須是塊級元素 (block level),且父級元素上需要這樣設置樣式:

1
2
3
.parent {
    text-align: center;
}

垂直居中

方法一:通過設置上下內間距一致達到垂直居中的效果:

1
2
3
4
.single-line {
    padding-top10px;
    padding-bottom10px;
}

方法二:通過設置 heightline-height 一致達到垂直居中:

1
2
3
4
.single-line {
    height100px;
    line-height100px;
}

固定寬高的塊級盒子

方法一:absolute + 負 margin

方法二:absolute + margin auto

方法三:absolute + calc

不固定寬高的塊級盒子

這裏列了 6 種方法,參考了顏海鏡 寫的文章 ,其中的兩種 line-height 和 writing-mode 方案看後讓我驚呼:還有這種操作?學到了學到了。

方法一:absolute + transform

方法二:line-height + vertical-align

640

方法三:writing-mode

方法四:table-cell

方法五:flex

方法六:grid

常用佈局

兩欄佈局(邊欄定寬主欄自適應)

針對以下這些方案寫了幾個示例: codepen demo

方法一:float + overflow(BFC 原理)

方法二:float + margin

方法三:flex

方法四:grid

三欄佈局(兩側欄定寬主欄自適應)

針對以下這些方案寫了幾個示例: codepen demo

方法一:聖盃佈局

方法二:雙飛翼佈局

方法三:float + overflow(BFC 原理)

方法四:flex

方法五:grid

多列等高佈局

結合示例閲讀更佳:codepen demo

方法一:padding + 負 margin

方法二:設置父級背景圖片

三行佈局(頭尾定高主欄自適應)

列了 4 種方法,都是基於如下的 HTML 和 CSS 的,結合示例閲讀效果更佳:codepen demo

1
2
3
4
5
6
7
<div class="layout">
    <header></header>
    <main>
        <div class="inner"></div>
    </main>
    <footer></footer>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
html,
body,
.layout {
    height100%;
}
body {
    margin0;
}
header
footer {
    height50px;
}
main {
    overflow-y: auto;
}

方法一:calc

方法二:absolute

方法三:flex

方法四:grid

圖片

結了個尾

這是我斷斷續續寫了 2 周完成的文章,算是自己對 CSS 的一個總結,雖然寫得很長,但不足以覆蓋所有 CSS 的知識,比如動畫和一些 CSS3 的新特性就完全沒涉及,因為這要寫下來估計得有大幾萬字(其實就是懶 😝 )。

碼字作圖不易,如果喜歡或者對你有絲毫幫助的話,幫忙點個👍 哈,點贊就是我的動力。同時也希望自己能堅持認真的寫下去,因為在總結提升自己的同時如果也能幫助更多的前端 er,那將會讓我感覺很開心。

JavaScript - 日期 Date

2021-03-11 01:10:28

Date

Date 類型使用自 UTC 世界協調時間 1970 年 1 月 1 日午夜零時開始經過的毫秒數來保存日期

創建

通過使用 new 操作符和 Date 構造函數來創建日期對象

var now = new Date()

Date() 可以選擇傳入參數,如果不傳入參數

  1. 不傳入參數

    會依據系統設置的當前時間來創建一個Date對象,返回當前的日期和時間

  2. 傳入參數 Unix 时间戳

    即 从1970-1-1 00:00:00 UTC 到该日期對象(UTC時間)的毫秒数

  3. 傳入 时间戳字符串

    與下文的 Date.parse()所需的參數一樣

  4. 傳入 日期成員

    與 下文的 Date.UTC() 所需的參數一樣,但是 Date() 會以 本地時間 來處理參數,而不是 UTC。

    1
    2
    3
    // 可見最終的結果 兩個不一樣, 原因是 Date() 會以本地時間來處理參數
    console.log(new Date(Date.UTC(2021,1,2,12,12,12))) // Tue Feb 02 2021 20:12:12 GMT+0800 (香港標準時間)
    console.log(new Date(2021,1,2,12,12,12)) // Tue Feb 02 2021 12:12:12 GMT+0800 (香港標準時間)

以一個函數的形式來調用 Date 對象(即不使用 new 操作符)會返回一個代表當前日期和時間的字符串

1
2
3
4
5
6
var d1 = new Date()
var d2 = Date()
console.log(d1) // Wed Mar 10 2021 22:46:21 GMT+0800 (香港標準時間)
console.log(d2) // Wed Mar 10 2021 22:46:21 GMT+0800 (香港標準時間)
console.log(typeof d1) // object
console.log(typeof d2) // string

獲取毫秒數方法

JavaScript 提供了兩種方法可以快速獲取毫秒數

  1. Date.parse()

    該方法接受一個表示日期的字符串參數(符合RFC 2822ISO 8601 的日期格式,其他格式也支持,但是結果可能會與預期不同),然後根據字符串返回相應的日期的毫秒數(从1970-1-1 00:00:00 UTC 到该日期對象(UTC時間)的毫秒数)。

    如果傳入的參數無法識別,則會返回 NaN

    如果參數沒有指定時區,默認使用本地時區。

    在 ISO 8601 格式中

    1. ‘2011-02-22’ 這種僅日期格式的,將會使用 UTC 時區來處理解析這個參數
    2. 如果是日期加時間的,則使用本地時間處理

    這個方法可能在不同瀏覽器和地區,有不一樣的結果

    1
    2
    3
    4
    5
    6
    7
    8
    console.log(Date.parse('2012-02-02')) // 1328140800000 (utc 時間 2012-02-02 00:00:00) 以 UTC 時間處理
    console.log(Date.parse('2012-02-02T02:02:02')) // 1328119322000 (utc 時間 2012-02-01 18:02:02) 以本地時間處理

    console.log(Date.parse("Feb 2, 2012")) // 1328112000000 (utc 時間 2012-02-01 16:00:00) 以本地時間處理
    console.log(Date.parse("Thu, 01 Jan 1970 00:00:00 GMT")) // 0 格式裏指定時區,以該時區處理

    // 格式錯誤,無法解析
    console.log(Date.parse('2012-02-42')) // NaN
  2. Date.UTC()

    Date.parse()一樣都是返回相應的日期的毫秒數(从1970-1-1 00:00:00 UTC 到该日期對象(UTC時間)的毫秒数),區別在於傳入的參數不同,其可以傳入很多參數。其傳入的參數會以 UTC 時區處理。

    Date.UTC(year,month[,date[,hrs[,min[,sec[,ms]]]]])

    • 【必須】year 年份

      可以是完整格式的年份,如 2022。

      如果是 0 到 99 之間,代表著 1900 年後的日期(傳入 8,會被渲染為 1908年)

    • 【必須】month 月份

      0 到 11 之間的整數

    • date 天數

      1 到 31 之間的整數

    • hrs 小時

      0 到 23 之間的整數

    • min 分鐘

      0 到 59 之間的整數

    • sec 秒

      0 到 59 之間的整數

    • ms 毫秒

      0 到 999 之間的整數

    注意: 如果沒有傳入 date 天數,則默認爲 1。

    而省略其它參數,這些參數都默認為 0

    如果有一個指定的參數超出其合理範圍,則 UTC 方法會通過更新其他參數直到該參數在合理範圍內。例如,為月份指定 15,則年份將會加 1,然後月份將會使用 3。

    1
    2
    console.log(Date.UTC(2021,1)) // 1612137600000 (utc 時間 2021-02-01 00:00:00)
    console.log(Date.UTC(2021,1,2,12,12,12)) // 1612267932000 (utc 時間 2021-02-02 12:12:12)

獲取當前時間

返回自 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数。

等同於 new Date().getTime()

1
2
console.log(new Date().getTime()) // 1615389429001
console.log(Date.now()) // 1615389429001

Getter 方法

方法 解釋
getFullYear() 根据本地时间返回指定日期对象的年份(四位数年份时返回四位数字)
getMonth() 根据本地时间返回指定日期对象的月份(0-11)
getDate() 根据本地时间返回指定日期对象的月份中的第几天(1-31)
getHours() 根据本地时间返回指定日期对象的小时(0-23)
getMinutes() 根据本地时间返回指定日期对象的分钟(0-59)
getSeconds() 根据本地时间返回指定日期对象的秒数(0-59)
getMilliseconds() 根据本地时间返回指定日期对象的毫秒(0-999)
getDay() 根据本地时间返回指定日期对象的星期中的第几天(0-6)
0 表示 星期日
getTime() 返回从1970-1-1 00:00:00 UTC(协调世界时)到该日期经过的毫秒数,对于1970-1-1 00:00:00 UTC之前的时间返回负值。
getTimezoneOffset() 返回协调世界时(UTC)相对于当前时区的时间差值,单位为分钟
getUTCFullYear() 根据世界时返回特定日期对象所在的年份(4位数)
getUTCMonth() 根据世界时返回特定日期对象的月份(0-11)
0 代表 1 月,依此類推
getUTCDate() 根据世界时返回特定日期对象一个月的第几天(1-31)
getUTCHours() 根据世界时返回特定日期对象当前的小时(0-23)
getUTCMinutes() 根据世界时返回特定日期对象的分钟数(0-59)
getUTCSeconds() 根据世界时返回特定日期对象的秒数(0-59)
getUTCMilliseconds() 根据世界时返回特定日期对象的毫秒数(0-999)
getUTCDay() 根据世界时返回特定日期对象一个星期的第几天(0-6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const today = new Date(2021,2,10,22,55,21,44)

console.log(today) // Wed Mar 10 2021 22:55:21 GMT+0800 (香港標準時間)
console.log(today.getFullYear()) // 2021
console.log(today.getMonth()) // 2 (返回 0 到 11 之間,所以 3 月是 2)
console.log(today.getDate()) // 10
console.log(today.getHours()) // 22
console.log(today.getMinutes()) // 55
console.log(today.getSeconds()) // 21
console.log(today.getMilliseconds()) // 44
console.log(today.getDay()) // 3
console.log(today.getTime()) // 1615388121044
console.log(today.getTimezoneOffset()) // -480

console.log(today.getUTCFullYear()) // 2021
console.log(today.getUTCMonth()) // 2 (返回 0 到 11 之間,所以 3 月是 2)
console.log(today.getUTCDate()) // 10
console.log(today.getUTCHours()) // 14 (utc 即 GMT+0,所以 22 - 8 = 14)
console.log(today.getUTCMinutes()) // 55
console.log(today.getUTCSeconds()) // 21
console.log(today.getUTCMilliseconds()) // 44
console.log(today.getUTCDay()) // 3

Setter 方法

方法 解釋
setDate() 根据本地时间为指定的日期对象设置月份中的第几天
setFullYear() 根据本地时间为指定日期对象设置完整年份(四位数年份是四个数字)
setMonth() 根据本地时间为指定日期对象设置月份
setHours() 根据本地时间为指定日期对象设置小时数
setMinutes() 根据本地时间为指定日期对象设置分钟数
setSeconds() 根据本地时间为指定日期对象设置秒数
setMilliseconds() 根据本地时间为指定日期对象设置毫秒数
setTime() 通过指定从 1970-1-1 00:00:00 UTC 开始经过的毫秒数来设置日期对象的时间,对于早于 1970-1-1 00:00:00 UTC的时间可使用负值
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)
setUTCSeconds() 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)

Conversion 方法

方法 解釋
toDateString() 以美式英语和人类易读的形式返回该日期对象日期部分的字符串
toISOString() 把一个日期转换为符合 ISO 8601 扩展格式的字符串。
格式:YYYY-MM-DDTHH:mm:ss.sssZ ,时区总是UTC
toJSON() 調用 toISOString() 返回一个表示该日期的字符串。为了在 JSON.stringify() 方法中使用
toString() 返回一个表示该日期对象的字符串。覆盖了Object.prototype.toString() 方法
toTimeString() 以人类易读格式返回日期对象时间部分的字符串
toUTCString() 把一个日期对象转换为一个以UTC时区计时的字符串
valueOf() 返回一个日期对象的原始值。覆盖了 Object.prototype.valueOf() 方法
toLocaleString() 返回一个表示该日期对象的字符串,该字符串与系统设置的地区关联(locality sensitive)。覆盖了 Object.prototype.toLocaleString() 方法
toLocaleTimeString() 返回一个表示该日期对象时间部分的字符串,该字符串格式与系统设置的地区关联(locality sensitive)
toLocaleDateString() 返回一个表示该日期对象日期部分的字符串,该字符串格式与系统设置的地区关联(locality sensitive)
1
2
3
4
5
6
7
8
9
const today = new Date(2021,2,10,22,55,21,44)
console.log(today) // Wed Mar 10 2021 22:55:21 GMT+0800 (香港標準時間)
console.log(today.toDateString()); // Wed Mar 10 2021
console.log(today.toISOString()); // 2021-03-10T14:55:21.044Z
console.log(today.toJSON()); // 2021-03-10T14:55:21.044Z
console.log(today.toString()); // Wed Mar 10 2021 22:55:21 GMT+0800 (香港標準時間)
console.log(today.toTimeString()); // 22:55:21 GMT+0800 (香港標準時間)
console.log(today.toUTCString()); // Wed, 10 Mar 2021 14:55:21 GMT
console.log(today.valueOf()); // 1615388121044

toLocaleString() toLocaleTimeString()toLocaleDateString()

這3個方法都接受兩個參數

  • 【可選】locales

    指定語言代碼,例如 ‘zh-HK’

    具體語言代碼 可參考 MDN Intl 文檔

    當沒有指定語言代碼時,會返回一個使用運行時默認的語言環境和格式(options)的格式化字符串

  • 【可選】options

    指定日期時間等輸出(文字/格式 等)

    具體 options 可參考 MDN 文檔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var today = new Date(2021,2,10,22,55,21,44)
console.log(today); // Wed Mar 10 2021 22:55:21 GMT+0800 (香港標準時間)

// toLocaleDateString()
console.log(today.toLocaleDateString()); // 2021/3/10
console.log(today.toLocaleDateString('zh-HK')); // 10/3/2021
console.log(today.toLocaleDateString('en-US')); // 3/10/2021

var options = {weekday: "long", year: "numeric", month: "long", day: "numeric"};
console.log(today.toLocaleDateString("zh-HK", options)); // 2021年3月10日星期三

// toLocaleTimeStrig()
console.log(today.toLocaleTimeString()); // 下午10:55:21
console.log(today.toLocaleTimeString('zh-HK')); // 下午10:55:21
console.log(today.toLocaleTimeString('en-US')); // 10:55:21 PM
console.log(today.toLocaleTimeString('zh-HK',{hour12: false})); // 22:55:21

// toLocaleString()
console.log(today.toLocaleString()); // 2021/3/10 下午10:55:21
console.log(today.toLocaleString('zh-HK')); // 10/3/2021 下午10:55:21
console.log(today.toLocaleString('en-US')); // 3/10/2021, 10:55:21 PM
console.log(today.toLocaleString('zh-HK',{hour12: false})); // 10/3/2021 22:55:21

參考

  1. 書籍 《JavaScript 高級程序設計》
  2. MDN 文檔