我們開發系統的時候,可能會接到這樣的需求:不要讓用戶復制頁面上的文字或者圖片,不要讓用戶調試我們的頁面,更甚至也不要讓用戶進行打印操作等等。
聽起來是不是讓人很頭大,這咋實現啊?這有必要嗎?這能禁住么?
如果你沒做過這些,或者沒接到過這樣的需求,那你也應該看到過某個網站做了一些這樣的措施。
既然要做,我們就得想方案,先來看看禁止復制都有哪些方法。
假設我們有這樣一段代碼:
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
接下來就通過這個例子來論述我們的方案:
x效果
這是一個css屬性,標識了元素及其子元素的文本不可被選中,因此設定之后,文本將不能夠被選中,因此也就不能復制:
<pre style="user-select: none;">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
我們在這段文本上,加上這個樣式。
效果
可以看到,文字壓根就不能選擇,從鼠標形狀也能看出來。
由于用在進行復制操作的時候,會觸發copy事件,我們可以通過監聽它來做一些處理,使得復制的行為發生改變:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("copy", copyFilter)
c.addEventListener("copy", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復制了一段魔法")
}
}
</script>
先獲取到我們要禁止復制的元素,然后給它添加一個copy的事件監聽,在添加監聽之前,要先移除一下,這樣是為了避免局部刷新的時候重復添加,然后我們通過copyFilter函數來對這次操作進行處理。
先獲取剪貼板對象,如果當前事件對象里不存在,那就從window里面取,然后我們通過getSelection再拿到選取的內容,因為我們對剪貼板對象進行修改,所以要阻止默認行為,然后把剪貼板的內容重新賦值,可以是示例中那樣的一段文字,也可以設置為空,甚至是任意其他內容,然后我們就可以看到產生的效果了:
效果
雖然能復制文本,但是由于我們攔截了復制操作,更改了它的行為,因此再粘貼的時候就變成了我們更改的樣子,也做到了禁止復制的功能。
這種方式對于使用快捷鍵或者右鍵的方式都是有效的。
這種情況主要是在可編輯區域,比如文本框、文本域、設置為contenteditable的元素等,用戶可以對文字進行剪切操作,雖然上面禁止了復制,但是剪切是另一個操作,不攔截的話還是相當于能復制出來。
copy和cut只是觸發的事件不同而已,但是它們都是執行相同的邏輯處理:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content" contenteditable>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("cut", copyFilter)
c.addEventListener("cut", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復制了一段魔法")
}
}
</script>
這里我為了方便,給元素添加了contenteditable屬性,讓它變成可編輯的,copyFilter函數沒有變化,我們只是添加了一個剪切事件的監聽,然后它們的處理函數都是copyFilter。看下效果:
效果
可以看到,首先我們對文字進行剪切,沒有出現預期的效果,這時因為我們在代碼里面對剪切進行了攔截,并阻止了它的默認行為,然后我們在粘貼的時候,文字也改變成我們設置的了。
雖然我們可以通過上面的幾種方法禁止在頁面上復制,但是用戶也可能開啟打印預覽模式,在這種情況下,也是可以進行復制的,我們要想對打印頁面進行一些控制,那么就要用到媒體查詢,先看下打印的樣子:
效果
雖然我們做了限制,但是在打印頁面沒有生效,現在我們針對這個場景更改一下代碼:
@media print {
html {
display: none;
}
}
通過添加上面這個樣式規則,我們能夠使頁面在打印的時候,內容隱藏起來,這樣就無法進行復制了:
效果
能夠看到,點擊打印的時候,預覽頁面一片空白,,這樣就禁止了在打印頁面進行復制的操作。當然了,你其實也可以設置其他的樣式屬性來做些控制,但要記住寫在打印的媒體查詢里面,只有這樣才會在打印頁面生效。
還有一種方式就是,通過設定一個偽元素,讓它全面覆蓋文本內容,這樣鼠標就不能選到實際的文本,改造一下代碼:
.content {
position: relative;
}
.content::before {
content: '';
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
<pre id="content" class="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
現在就不能在元素上面選中文字了,不過用戶也可能還有一些操作,比如在內容區域外面ctrl+a全選,或者在外面拖動鼠標來全選,如果是這種情形,那么我們可以通過監聽鍵盤和鼠標事件來禁止全選等操作。
由于用戶有很多種操作的方式,鍵盤全選、鼠標全選、鍵盤右鍵、鼠標右鍵等等,我們如果窮舉的話,情況太多了,因此我們只監聽鼠標按下和抬起事件,以及鍵盤的按下事件:
document.removeEventListener("mousedown", haveSelect)
document.addEventListener("mousedown", haveSelect)
document.removeEventListener("mouseup", haveSelect)
document.addEventListener("mouseup", haveSelect)
document.removeEventListener("keydown", haveSelect)
document.addEventListener("keydown", haveSelect)
function haveSelect() {
window.getSelection().removeAllRanges()
}
主要就是在removeAllRanges方法上面,能夠在觸發上面事件的時候,將所選區域清空,也就是不管你選沒選,咋選的,反正就是你只要進行了操作,那我就那可能選擇的區域給你清空,這樣你就啥也干不了了。
效果
好,這樣就可以啦,無論怎么選,即使出現了選區,但是只要你再按了鼠標或者鍵盤,那么選區就會直接消失,就能達到不能復制的效果,因為你發現啥都做不了。
這里額外說明一下,對于事件的監聽,一定要用addEventListener來實現,因為它會將多個綁定的事件都添加上去,當觸發的時候就會按照綁定的順序進行執行,如果是用賦值的方式,那么后面的會覆蓋前面的,而且賦值的方式很容易被篡改,可以很輕松的讓你的綁定函數不能執行從而失效,而使用addEventListener就不會被人為覆蓋,只能通過綁定的函數句柄來手動移除,也就是說要移除的時候,必須使用跟綁定時使用同一個函數才行。
通過CSS的方式禁止復制,可以很容易的被用戶解除,只能是設置的稍微復雜一點,增加難度。而通過JS的方式禁止復制,也可以通過禁用頁面JavaScript代碼來解除,因此我們可以將內容通過js來渲染,這樣如果頁面禁用了js,那么內容也不會渲染。
對于禁止調試,主要是指用戶打開控制臺,控制臺也就是開發者工具,我為了方便稱之為控制臺,想要對頁面進行調試時,我們做一些處理,阻止這種行為,最大可能的攔截通過控制臺對系統的調試。
主要的方法有幾種,由于打開控制臺是瀏覽器提供的調試功能,因此我們沒法攔截打開操作,即使通過事件監聽不允許快捷鍵這樣做,但是也可以通過其他方式進行打開,因此我們的主要關注點就在于打開控制臺之后,我們能做哪些事情來限制用戶行為。
打開控制臺的快捷鍵主要有F12和ctrl+shift+i,我們先把這倆給禁用了:
document.removeEventListener("keydown", disableDevShortcut)
document.addEventListener("keydown", disableDevShortcut)
function disableDevShortcut(e) {
console.log(e)
if(e.keyCode===123) {
e.preventDefault()
}else if(e.keyCode===73 && e.ctrlKey && e.shiftKey) {
e.preventDefault()
}
}
這樣在使用這兩個快捷鍵的時候,頁面沒有任何反應,控制臺也不會喚起,因為我們阻止了它們的默認行為。
除了通過快捷鍵,還可以使用右鍵的方式,并點擊檢查也會調出控制臺。
效果
這種情景,我們可以通過禁止在頁面上使用右鍵的方式,來阻止打開控制臺:
document.removeEventListener("contextmenu", cancelContextmenu)
document.addEventListener("contextmenu", cancelContextmenu)
function cancelContextmenu(e) {
e.preventDefault()
}
現在就不能通過右鍵打開控制臺了,但是相應的整個右鍵功能也都不能使用了。
如果用戶最終打開了控制臺,比如通過在瀏覽器的更多功能中來打開的話,那么我就需要采取其他的措施,其中之一就是給代碼設置無限斷點,因為斷點只在控制臺打開的時候才會發生作用,從而不必擔心非調試模式下的程序正常運行。
無限斷點的主要思路就是利用定時器等手段,頻繁的觸發斷點效果,使得不能輕松的調試程序,先看下代碼:
;(()=> {
function breakDebugger() {
if(new checkDebugger().check) {
breakDebugger()
}
}
function checkDebugger() {
const now=new Date();
eval('(function () {debugger;false;})()')
const dur=Date.now() - now
if(dur < 5) {
return {check: false}
}else {
return {check: true}
}
}
setInterval(()=> {
eval('(function () {debugger;true;})()')
breakDebugger()
}, 500)
})()
我們利用一個立即執行的自執行函數,來使我們的代碼被封裝在一個固定塊內,不與其他部分有任何影響。
這里主要做了兩步:
第一步設置一個重復執行的定時器,其中包括了一個斷點和一個函數調用。
第二步通過函數來遞歸調用斷點,主要使用了實例化對象的方式和時間差的判斷。
這樣做的主要作用就是在設置無限斷點的同時,也能夠讓每次的斷點都是被重新生成的,看下效果,一目了然:
效果
發現沒有,我們通過這種方式,只要打開了控制臺,那么就會進入到無限斷點的循環中,使得不能做任何其他事情,而且每個斷點的生成都會開辟一個新的虛擬運行環境,這種情況下,只有關閉控制臺,才能結束斷點。
即使使用右鍵選擇Never parse here,也毫無作用,雖然可以通過Deactive breakpoints按鈕來徹底禁用斷點,就是下面這個按鈕:
按鈕
但是,如果這樣做的話,那么用戶也就同時失去了調試其他代碼的能力。
況且,我們接下來還會介紹其他的控制手段,可以配合著使用。
我們如果能有一種手段,可以知道用戶開啟了控制臺,換句話說只要控制臺被打開,就通知我們或者被我們監測到,那么我們就可以執行一些控制手段,這種效果肯定是很理想的,遺憾的是還沒有這種api暴露給我們去讓我們能夠這樣做。
不過我們可以通過其他的方式,利用既有的一些能力來實現這一點,這里我還是使用循環定時器,來不斷的去嗅探用戶是否開啟了控制臺,直接看代碼:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
location.href='about:blank'
}
console.clear()
}, 500)
})()
同樣,通過一個自執行函數,我們開啟了一個循環定時器,然后在回調方法里面,我們就去實現上面的目標,也是分為了兩步:
第一步創建一個a元素,然后通過表格的形式將它打印出來,并記錄下消耗的時間。
第二步判斷耗時的長短來控制是否跳轉到空白頁,然后清空控制臺。
這種方式主要是利用了console.table的特性,它會將元素以表格的形式輸出到控制臺,大概就像下面的樣子:
效果
由于太多了,我就沒有全部截下來,如果沒有打開控制臺的話,使用console.table輸出我們創建的a標簽是很快的,有多快呢,就是js執行一條語句的速度,所以打印a1和a2的時間間隔非常短,幾乎為0,因為他們快到差不多是同時執行的,給大家打印看一下:
效果
我們先不打開控制臺,等輸出完畢再打開,很清楚的發現,我們沒打開控制臺的時候,輸出的a就是它標簽,而且時間間隔是0毫秒。
現在我們打開控制臺的時候刷新一下頁面,看看控制臺的輸出:
效果
這次就變成了以table的形式輸出a元素,而且它的耗時明顯增多,不再是0毫秒,而是耗費了10毫秒,雖然打開控制臺的時候多次刷新頁面,每次輸出的毫秒數是不同的,但是跟關閉控制臺的時候輸出的耗時差距非常明顯,因此我們就可以在這個上面做文章。
我在上面的代碼中假定了,只要是大于1毫秒的耗時,那就表示用戶打開了控制臺,然后我們就把頁面給跳轉到空白頁,當然了你也可以做一些任何你想做的操作,比如彈出一個提示,或者把body內容置空等等等等。
回到我們上面的代碼,看一下它實際發生的作用和帶給我們的效果:
效果
哈哈,古德古德,平時瀏覽一切正常,只要剛一打開控制臺,瞬間頁面就被跳轉走了,什么都干不了。這樣我們就通過這種方式,達到了限制打開控制臺的目的,也就是在當前頁沒法調試,一打開就跳轉。
這種辦法由于是繞路實現的,那么你可能會有疑問,它穩定嗎?會不會誤判,我可以對它絕對放心嗎?
理論上來說,通過輸出的執行時間是不太能精確掌握的,但是我們可以再做一些其他的措施來逼近真相:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
let time=0
for(let i=0; i < 10; i++) {
let a1=+new Date()
console.table(foo)
let a2=+new Date()
time +=a2 - a1
}
if(time > 20) {
location.href='about:blank'
}
}
console.clear()
}, 500)
})()
我又改造了一下判斷的邏輯,當發現輸出耗時為2毫秒甚至更多的時候,我立馬再進行一次真偽判斷,也就是說,萬一由于其他的影響,導致我第7行的代碼誤判了,那么我再同步執行一個循環,連續輸出10次,把他們的耗時總和計算出來,然后判斷是否大于20毫秒,如果還是耗時過高的話,那么就可以非常肯定的知道用戶是打開了控制臺,這個時候就可以放心的做一些處理了。
其實限制用戶行為的方法有很多很多,上面列出了一些主要的,多種方法還是要結合著使用。你也可以自由發揮,多使用一些其他的手段,也會增加用戶復制或者調試的難度,比如防止用戶重寫console的方法,或者清除所有定時器等。
甚至也可以將你的內容繪制到canvas上面來防止復制,多加一些js的處理工作,防止禁用js的時候,我們的代碼不生效,只有在js可用的時候再去渲染內容等。也可以在綜合考慮的情況下加上代碼混淆、代碼加密等措施。
話說回來,大家都是同路人,何必相互為難,哈哈哈,不過提這個需求的人也著實會為難我們,既然提了那就盡力去做,能做到什么程度,只能說是盡量做到極致。
希望上面的內容能夠幫助到你,也希望能夠對你有所啟發。
謝謝
篇文章圍繞了 CSS 的核心知識點和項目中常見的需求來展開。雖然行文偏長,但較基礎,適合初級中級前端閱讀,閱讀的時候請適當跳過已經掌握的部分。
這篇文章斷斷續續寫了比較久,也參考了許多優秀的文章,但或許文章里還是存在不好或不對的地方,請多多指教,可以評論里直接提出來哈。
小tip:后續內容更精彩哦。
CSS 的核心功能是將 CSS 屬性設定為特定的值。一個屬性與值的鍵值對被稱為聲明(declaration)。
css復制代碼color: red;
而如果將一個或者多個聲明用 {} 包裹起來后,那就組成了一個聲明塊(declaration block)。
css復制代碼{
color: red;
text-align: center;
}
聲明塊如果需要作用到對應的 HTML 元素,那還需要加上選擇器。選擇器和聲明塊組成了CSS 規則集(CSS ruleset),常簡稱為 CSS 規則。
css復制代碼span {
color: red;
text-align: center;
}
規則集中最后一條聲明可以省略分號,但是并不建議這么做,因為容易出錯。
CSS 中的注釋:
css復制代碼/* 單行注釋 */
/*
多行
注釋
*/
在 CSS 文件中,除了注釋、CSS 規則集以及 @規則 外,定義的一些別的東西都將被瀏覽器忽略。
CSS 規則是樣式表的主體,通常樣式表會包括大量的規則列表。但有時候也需要在樣式表中包括其他的一些信息,比如字符集,導入其它的外部樣式表,字體等,這些需要專門的語句表示。
而 @規則 就是這樣的語句。CSS 里包含了以下 @規則:
除了以上這幾個之外,下面還將對幾個比較生澀的 @規則 進行介紹。
@charset 用于定義樣式表使用的字符集。它必須是樣式表中的第一個元素。如果有多個 @charset 被聲明,只有第一個會被使用,而且不能在HTML元素或HTML頁面的 <style> 元素內使用。
注意:值必須是雙引號包裹,且和
css復制代碼@charset "UTF-8";
平時寫樣式文件都沒寫 @charset 規則,那這個 CSS 文件到底是用的什么字符編碼的呢?
某個樣式表文件到底用的是什么字符編碼,瀏覽器有一套識別順序(優先級由高到低):
@import 用于告訴 CSS 引擎引入一個外部樣式表。
link 和 @import 都能導入一個樣式文件,它們有什么區別嘛?
@supports 用于查詢特定的 CSS 是否生效,可以結合 not、and 和 or 操作符進行后續的操作。
css復制代碼/* 如果支持自定義屬性,則把 body 顏色設置為變量 varName 指定的顏色 */
@supports (--foo: green) {
body {
color: var(--varName);
}
}
層疊樣式表,這里的層疊怎么理解呢?其實它是 CSS 中的核心特性之一,用于合并來自多個源的屬性值的算法。比如說針對某個 HTML 標簽,有許多的 CSS 聲明都能作用到的時候,那最后誰應該起作用呢?層疊性說的大概就是這個。
針對不同源的樣式,將按照如下的順序進行層疊,越往下優先級越高:
理解層疊性的時候需要結合 CSS 選擇器的優先級以及繼承性來理解。比如針對同一個選擇器,定義在后面的聲明會覆蓋前面的;作者定義的樣式會比默認繼承的樣式優先級更高。
CSS 選擇器無疑是其核心之一,對于基礎選擇器以及一些常用偽類必須掌握。下面列出了常用的選擇器。 想要獲取更多選擇器的用法可以看 MDN CSS Selectors。
屬性選擇器
條件偽類
行為偽類
狀態偽類
結構偽類
優先級就是分配給指定的 CSS 聲明的一個權重,它由匹配的選擇器中的每一種選擇器類型的數值決定。為了記憶,可以把權重分成如下幾個等級,數值越大的權重越高:
可以看到內聯樣式(通過元素中 style 屬性定義的樣式)的優先級大于任何選擇器;而給屬性值加上 !important 又可以把優先級提至最高,就是因為它的優先級最高,所以需要謹慎使用它,以下有些使用注意事項:
在 CSS 中有一個很重要的特性就是子元素會繼承父元素對應屬性計算后的值。比如頁面根元素 html 的文本顏色默認是黑色的,頁面中的所有其他元素都將繼承這個顏色,當申明了如下樣式后,H1 文本將變成橙色。
css復制代碼body {
color: orange;
}
h1 {
color: inherit;
}
設想一下,如果 CSS 中不存在繼承性,那么我們就需要為不同文本的標簽都設置一下 color,這樣一來的后果就是 CSS 的文件大小就會無限增大。
CSS 屬性很多,但并不是所有的屬性默認都是能繼承父元素對應屬性的,那哪些屬性存在默認繼承的行為呢?一定是那些不會影響到頁面布局的屬性,可以分為如下幾類:
對于其他默認不繼承的屬性也可以通過以下幾個屬性值來控制繼承行為:
在 CSS 的世界中,會把內容按照從左到右、從上到下的順序進行排列顯示。正常情況下會把頁面分割成一行一行的顯示,而每行又可能由多列組成,所以從視覺上看起來就是從上到下從左到右,而這就是 CSS 中的流式布局,又叫文檔流。文檔流就像水一樣,能夠自適應所在的容器,一般它有如下幾個特性:
如何脫離文檔流呢?
脫流文檔流指節點脫流正常文檔流后,在正常文檔流中的其他節點將忽略該節點并填補其原先空間。文檔一旦脫流,計算其父節點高度時不會將其高度納入,脫流節點不占據空間。有兩種方式可以讓元素脫離文檔流:浮動和定位。
在 CSS 中任何元素都可以看成是一個盒子,而一個盒子是由 4 部分組成的:內容(content)、內邊距(padding)、邊框(border)和外邊距(margin)。
盒模型有 2 種:標準盒模型和 IE 盒模型,本別是由 W3C 和 IExplore 制定的標準。
如果給某個元素設置如下樣式:
css復制代碼.box {
width: 200px;
height: 200px;
padding: 10px;
border: 1px solid #eee;
margin: 10px;
}
標準盒模型認為:盒子的實際尺寸=內容(設置的寬/高) + 內邊距 + 邊框
所以 .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 個值:
視覺格式化模型(Visual formatting model)是用來處理和在視覺媒體上顯示文檔時使用的計算規則。CSS 中一切皆盒子,而視覺格式化模型簡單來理解就是規定這些盒子應該怎么樣放置到頁面中去,這個模型在計算的時候會依賴到很多的因素,比如:盒子尺寸、盒子類型、定位方案(是浮動還是定位)、兄弟元素或者子元素以及一些別的因素。
從上圖中可以看到視覺格式化模型涉及到的內容很多,有興趣深入研究的可以結合上圖看這個 W3C 的文檔 Visual formatting model。所以這里就簡單介紹下盒子類型。
盒子類型由 display 決定,同時給一個元素設置 display 后,將會決定這個盒子的 2 個顯示類型(display type):
對外顯示方面,盒子類型可以分成 2 類:block-level box(塊級盒子) 和 inline-level box(行內級盒子)。
依據上圖可以列出都有哪些塊級和行內級盒子:
所有塊級盒子都會參與 BFC,呈現垂直排列;而所有行內級盒子都參會 IFC,呈現水平排列。
除此之外,block、inline 和 inline-block 還有什么更具體的區別呢?
block
inline
inline-block
對內方面,其實就是把元素當成了容器,里面包裹著文本或者其他子元素。container box 的類型依據 display 的值不同,分為 4 種:
值得一提的是如果把 img 這種替換元素(replaced element)申明為 block 是不會產生 container box 的,因為替換元素比如 img 設計的初衷就僅僅是通過 src 把內容替換成圖片,完全沒考慮過會把它當成容器。
參考:
格式化上下文(Formatting Context)是 CSS2.1 規范中的一個概念,大概說的是頁面中的一塊渲染區域,規定了渲染區域內部的子元素是如何排版以及相互作用的。
不同類型的盒子有不同格式化上下文,大概有這 4 類:
其中 BFC 和 IFC 在 CSS 中扮演著非常重要的角色,因為它們直接影響了網頁布局,所以需要深入理解其原理。
塊格式化上下文,它是一個獨立的渲染區域,只有塊級盒子參與,它規定了內部的塊級盒子如何布局,并且與這個區域外部毫不相干。
BFC 渲染規則
如何創建 BFC?
BFC 應用場景
1、 自適應兩欄布局
應用原理:BFC 的區域不會和浮動區域重疊,所以就可以把側邊欄固定寬度且左浮動,而對右側內容觸發 BFC,使得它的寬度自適應該行剩余寬度。
html復制代碼<div class="layout">
<div class="aside">aside</div>
<div class="main">main</div>
</div>
css復制代碼.aside {
float: left;
width: 100px;
}
.main {
<!-- 觸發 BFC -->
overflow: auto;
}
2、清除內部浮動
浮動造成的問題就是父元素高度坍塌,所以清除浮動需要解決的問題就是讓父元素的高度恢復正常。而用 BFC 清除浮動的原理就是:計算 BFC 的高度時,浮動元素也參與計算。只要觸發父元素的 BFC 即可。
css復制代碼.parent {
overflow: hidden;
}
3、 防止垂直 margin 合并
BFC 渲染原理之一:同一個 BFC 下的垂直 margin 會發生合并。所以如果讓 2 個元素不在同一個 BFC 中即可阻止垂直 margin 合并。那如何讓 2 個相鄰的兄弟元素不在同一個 BFC 中呢?可以給其中一個元素外面包裹一層,然后觸發其包裹層的 BFC,這樣一來 2 個元素就不會在同一個 BFC 中了。
html復制代碼<div class="layout">
<div class="a">a</div>
<div class="contain-b">
<div class="b">b</div>
</div>
</div>
css復制代碼.demo3 .a,
.demo3 .b {
border: 1px solid #999;
margin: 10px;
}
.contain-b {
overflow: hidden;
}
針對以上 3 個 示例 ,可以結合這個 BFC 應用示例 配合觀看更佳。
參考:CSS 原理 - Formatting Context
IFC 的形成條件非常簡單,塊級元素中僅包含內聯級別元素,需要注意的是當IFC中有塊級元素插入時,會產生兩個匿名塊將父元素分割開來,產生兩個 IFC。
IFC 渲染規則
針對如上的 IFC 渲染規則,你是不是可以分析下下面這段代碼的 IFC 環境是怎么樣的呢?
html復制代碼<p>It can get <strong>very complicated</storng> once you start looking into it.</p>
對應上面這樣一串 HTML 分析如下:
參考:Inline formatting contexts
IFC 應用場景
偷個懶,demo 和圖我就不做了。
在電腦顯示屏幕上的顯示的頁面其實是一個三維的空間,水平方向是 X 軸,豎直方向是 Y 軸,而屏幕到眼睛的方向可以看成是 Z 軸。眾 HTML 元素依據自己定義的屬性的優先級在 Z 軸上按照一定的順序排開,而這其實就是層疊上下文所要描述的東西。
我們對層疊上下文的第一印象可能要來源于 z-index,認為它的值越大,距離屏幕觀察者就越近,那么層疊等級就越高,事實確實是這樣的,但層疊上下文的內容遠非僅僅如此:
在看層疊等級和層疊順序之前,我們先來看下如何產生一個層疊上下文,特定的 HTML 元素或者 CSS 屬性產生層疊上下文,MDN 中給出了這么一個列表,符合以下任一條件的元素都會產生層疊上下文:
層疊等級
層疊等級指節點在三維空間 Z 軸上的上下順序。它分兩種情況:
普通節點的層疊等級優先由其所在的層疊上下文決定,層疊等級的比較只有在當前層疊上下文中才有意義,脫離當前層疊上下文的比較就變得無意義了。
層疊順序
在同一個層疊上下文中如果有多個元素,那么他們之間的層疊順序是怎么樣的呢?
以下這個列表越往下層疊優先級越高,視覺上的效果就是越容易被用戶看到(不會被其他元素覆蓋):
如何比較兩個元素的層疊等級?
參考:徹底搞懂CSS層疊上下文、層疊等級、層疊順序、z-index
CSS 的聲明是由屬性和值組成的,而值的類型有許多種:
而還有些值是需要帶單位的,比如 width: 100px,這里的 px 就是表示長度的單位,長度單位除了 px 外,比較常用的還有 em、rem、vw/vh 等。那他們有什么區別呢?又應該在什么時候使用它們呢?
屏幕分辨率是指在屏幕的橫縱方向上的像素點數量,比如分辨率 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 來說,它的像素密度為:
js復制代碼Math.sqrt(750 * 750 + 1334 * 1334) / 4.7=326ppi
設備獨立像素(DIP)
DIP 是特別針對 Android設備而衍生出來的,原因是安卓屏幕的尺寸繁多,因此為了顯示能盡量和設備無關,而提出的這個概念。它是基于屏幕密度而計算的,認為當屏幕密度是 160 的時候,px=DIP。
計算公式:dip=px * 160 / dpi
em 是 CSS 中的相對長度單位中的一個。居然是相對的,那它到底是相對的誰呢?它有 2 層意思:
我們都知道每個瀏覽器都會給 HTML 根元素 html 設置一個默認的 font-size,而這個值通常是 16px。這也就是為什么 1em=16px 的原因所在了。
em 在計算的時候是會層層計算的,比如:
html復制代碼<div>
<p></p>
</div>
css復制代碼div { font-size: 2em; }
p { font-size: 2em; }
對于如上一個結構的 HTML,由于根元素 html 的字體大小是 16px,所以 p 標簽最終計算出來后的字體大小會是 16 * 2 * 2=64px
rem(root em) 和 em 一樣,也是一個相對長度單位,不過 rem 相對的是 HTML 的根元素 html。
rem 由于是基于 html 的 font-size 來計算,所以通常用于自適應網站或者 H5 中。
比如在做 H5 的時候,前端通常會讓 UI 給 750px 寬的設計圖,而在開發的時候可以基于 iPhone X 的尺寸 375px * 812px 來寫頁面,這樣一來的話,就可以用下面的 JS 依據當前頁面的視口寬度自動計算出根元素 html 的基準 font-size 是多少。
js復制代碼(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);
}
}
})(document, window);
比如當視口是 375px 的時候,經過計算 html 的 font-size 會是 100px,這樣有什么好處呢?好處就是方便寫樣式,比如從設計圖量出來的 header 高度是 50px 的,那我們寫樣式的時候就可以直接寫:
css復制代碼header {
height: 0.5rem;
}
每個從設計圖量出來的尺寸只要除于 100 即可得到當前元素的 rem 值,都不用經過計算,非常方便。偷偷告訴你,如果你把上面那串計算 html 標簽 font-size 的 JS 代碼中的 200 替換成 2,那在計算 rem 的時候就不需要除于 100 了,從設計圖量出多大 px,就直接寫多少個 rem。
vw 和 vh 分別是相對于屏幕視口寬度和高度而言的長度單位:
在 JS 中 100vw=window.innerWidth,100vh=window.innerHeight。
vw/vh 的出現使得多了一種寫自適應布局的方案,開發者不再局限于 rem 了。
相對視口的單位,除了 vw/vh 外,還有 vmin 和 vmax:
CSS 中用于表示顏色的值種類繁多,足夠構成一個體系,所以這里就專門拿出一個小節來講解它。
根據 CSS 顏色草案 中提到的顏色值類型,大概可以把它們分為這幾類:
顏色關鍵字(color keywords)是不區分大小寫的標識符,它表示一個具體的顏色,比如 white(白),黑(black)等;
可接受的關鍵字列表在CSS的演變過程中發生了改變:
如下這張圖是 16 個基礎色,又叫 VGA 顏色。截止到目前為止 CSS 顏色關鍵字總共有 146 個,這里可以查看 完整的色彩關鍵字列表。
需要注意的是如果聲明的時候的顏色關鍵字是錯誤的,瀏覽器會忽略它。
transparent 關鍵字表示一個完全透明的顏色,即該顏色看上去將是背景色。從技術上說,它是帶有 alpha 通道為最小值的黑色,是 rgba(0,0,0,0) 的簡寫。
透明關鍵字有什么應用場景呢?
實現三角形
下面這個圖是用 4 條邊框填充的正方形,看懂了它你大概就知道該如何用 CSS 寫三角形了。
css復制代碼div {
border-top-color: #ffc107;
border-right-color: #00bcd4;
border-bottom-color: #e26b6b;
border-left-color: #cc7cda;
border-width: 50px;
border-style: solid;
}
用 transparent 實現三角形的原理:
看下示例:
增大點擊區域
常常在移動端的時候點擊的按鈕的區域特別小,但是由于現實效果又不太好把它做大,所以常用的一個手段就是通過透明的邊框來增大按鈕的點擊區域:
css復制代碼.btn {
border: 5px solid transparent;
}
currentColor 會取當前元素繼承父級元素的文本顏色值或聲明的文本顏色值,即 computed 后的 color 值。
比如,對于如下 CSS,該元素的邊框顏色會是 red:
css復制代碼.btn {
color: red;
border: 1px solid currentColor;
}
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 用函數表示的時候,每個值的范圍是 0~255 或者 0%~100%,所以紅色是 rgb(255, 0, 0), 或者 rgb(100%, 0, 0)。
如果需要使用函數來表示帶不透明度的顏色值,值的范圍是 0~1 及其之間的小數或者 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] 顏色是由色相(hue)-飽和度(saturation)-亮度(lightness)-不透明度組成的顏色體系。
寫法上可以參考 RGB 的寫法,只是參數的值不一樣。
給一個按鈕設置不透明度為 67% 的紅色的 color 的寫法,以下全部寫法效果一致:
css復制代碼button {
color: #ff0000aa;
color: #f00a;
color: rgba(255, 0, 0, 0.67);
color: rgb(100% 0% 0% / 67%);
color: hsla(0, 100%, 50%, 67%);
color: hsl(0deg 100% 50% / 67%);
}
小提示:在 Chrome DevTools 中可以按住 shift + 鼠標左鍵可以切換顏色的表示方式。
媒體查詢是指針對不同的設備、特定的設備特征或者參數進行定制化的修改網站的樣式。
你可以通過給 <link> 加上 media 屬性來指定該樣式文件只能對什么設備生效,不指定的話默認是 all,即對所有設備都生效:
html復制代碼<link rel="stylesheet" src="styles.css" media="screen" />
<link rel="stylesheet" src="styles.css" media="print" />
都支持哪些設備類型?
需要注意的是:通過 media 指定的 資源盡管不匹配它的設備類型,但是瀏覽器依然會加載它。
除了通過 <link> 讓指定設備生效外,還可以通過 @media 讓 CSS 規則在特定的條件下才能生效。響應式頁面就是使用了 @media 才讓一個頁面能夠同時適配 PC、Pad 和手機端。
css復制代碼@media (min-width: 1000px) {}
媒體查詢支持邏輯操作符:
媒體查詢還支持眾多的媒體特性,使得它可以寫出很復雜的查詢條件:
css復制代碼/* 用戶設備的最小高度為680px或為縱向模式的屏幕設備 */
@media (min-height: 680px), screen and (orientation: portrait) {}
之前我們通常是在預處理器里才可以使用變量,而現在 CSS 里也支持了變量的用法。通過自定義屬性就可以在想要使用的地方引用它。
自定義屬性也和普通屬性一樣具有級聯性,申明在 :root 下的時候,在全文檔范圍內可用,而如果是在某個元素下申明自定義屬性,則只能在它及它的子元素下才可以使用。
自定義屬性必須通過 --x 的格式申明,比如:--theme-color: red; 使用自定義屬性的時候,需要用 var 函數。比如:
css復制代碼<!-- 定義自定義屬性 -->
:root {
--theme-color: red;
}
<!-- 使用變量 -->
h1 {
color: var(--theme-color);
}
上圖這個是使用 CSS 自定義屬性配合 JS 實現的動態調整元素的 box-shadow,具體可以看這個 codepen demo。
Retina 顯示屏比普通的屏幕有著更高的分辨率,所以在移動端的 1px 邊框就會看起來比較粗,為了美觀通常需要把這個線條細化處理。這里有篇文章列舉了 7 中方案可以參考一下:7種方法解決移動端Retina屏幕1px邊框問題
而這里附上最后一種通過偽類和 transform 實現的相對完美的解決方案:
只設置單條底部邊框:
css復制代碼.scale-1px-bottom {
position: relative;
border:none;
}
.scale-1px-bottom::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
background: #000;
width: 100%;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
同時設置 4 條邊框:
css復制代碼.scale-1px {
position: relative;
margin-bottom: 20px;
border:none;
}
.scale-1px::after {
content: '';
position: absolute;
top: 0;
left: 0;
border: 1px solid #000;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
}
什么是浮動:浮動元素會脫離文檔流并向左/向右浮動,直到碰到父元素或者另一個浮動元素。
為什么要清楚浮動,它造成了什么問題?
因為浮動元素會脫離正常的文檔流,并不會占據文檔流的位置,所以如果一個父元素下面都是浮動元素,那么這個父元素就無法被浮動元素所撐開,這樣一來父元素就丟失了高度,這就是所謂的浮動造成的父元素高度坍塌問題。
父元素高度一旦坍塌將對后面的元素布局造成影響,為了解決這個問題,所以需要清除浮動,讓父元素恢復高度,那該如何做呢?
這里介紹兩種方法:通過 BFC 來清除、通過 clear 來清除。
前面介紹 BFC 的時候提到過,計算 BFC 高度的時候浮動子元素的高度也將計算在內,利用這條規則就可以清楚浮動。
假設一個父元素 parent 內部只有 2 個子元素 child,且它們都是左浮動的,這個時候 parent 如果沒有設置高度的話,因為浮動造成了高度坍塌,所以 parent 的高度會是 0,此時只要給 parent 創造一個 BFC,那它的高度就能恢復了。
而產生 BFC 的方式很多,我們可以給父元素設置overflow: auto 來簡單的實現 BFC 清除浮動,但是為了兼容 IE 最好用 overflow: hidden。
css復制代碼.parent {
overflow: hidden;
}
通過 overflow: hidden 來清除浮動并不完美,當元素有陰影或存在下拉菜單的時候會被截斷,所以該方法使用比較局限。
我先把結論貼出來:
css復制代碼.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 是這么寫的:
css復制代碼html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
他的這份 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 有如下特點:
默認:字符太長溢出了容器
字符超出部分換行
字符超出位置使用連字符
單行文本超出省略
多行文本超出省略
查看以上這些方案的示例: codepen demo
有意思的是剛好前兩天看到 chokcoco 針對文本溢出也寫了一篇文章,主要突出的是對整塊的文本溢出處理。啥叫整塊文本?比如,下面這種技術標簽就是屬于整塊文本:
另外他還對 iOS/Safari 做了兼容處理,感興趣的可以去閱讀下:CSS 整塊文本溢出省略特性探究。
讓元素在父元素中呈現出水平垂直居中的形態,無非就 2 種情況:
以下列到的所有水平垂直居中方案這里寫了個 codepen demo,配合示例閱讀效果更佳。
水平居中
此類元素需要水平居中,則父級元素必須是塊級元素(block level),且父級元素上需要這樣設置樣式:
css復制代碼.parent {
text-align: center;
}
垂直居中
方法一:通過設置上下內間距一致達到垂直居中的效果:
css復制代碼.single-line {
padding-top: 10px;
padding-bottom: 10px;
}
方法二:通過設置 height 和 line-height 一致達到垂直居中:
css復制代碼.single-line {
height: 100px;
line-height: 100px;
}
方法一:absolute + 負 margin
方法二:absolute + margin auto
方法三:absolute + calc
這里列了 6 種方法,參考了顏海鏡 寫的文章 ,其中的兩種 line-height 和 writing-mode 方案看后讓我驚呼:還有這種操作?學到了學到了。
方法一:absolute + transform
方法二:line-height + vertical-align
方法三: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
html復制代碼<div class="layout">
<header></header>
<main>
<div class="inner"></div>
</main>
<footer></footer>
</div>
css復制代碼html,
body,
.layout {
height: 100%;
}
body {
margin: 0;
}
header,
footer {
height: 50px;
}
main {
overflow-y: auto;
}
方法一:calc
方法二:absolute
方法三:flex
方法四:grid
這是我斷斷續續寫了 2 周完成的文章,算是自己對 CSS 的一個總結,雖然寫得很長,但不足以覆蓋所有 CSS 的知識,比如動畫和一些 CSS3 的新特性就完全沒涉及,因為這要寫下來估計得有大幾萬字(其實就是懶 )。
碼字作圖不易,如果喜歡或者對你有絲毫幫助的話,幫忙點個 哈,點贊就是我的動力。同時也希望自己能堅持認真的寫下去,因為在總結提升自己的同時如果也能幫助更多的前端er,那將會讓我感覺很開心。
作者:大海我來了
鏈接:https://juejin.cn/post/6941206439624966152
*請認真填寫需求信息,我們會在24小時內與您取得聯系。