前端重構範式之 position

本文由 Rhyme、Captain 發表在 ScalaCool 團隊博客。css

本文旨在讓你更深刻地瞭解position,併爲你提供一套使用position的範式,爲你使用position提供一點建議和參考。html

在此以前先讓咱們來看看 learncss 中文文檔中對position的定義web

描述
absolute 生成絕對定位的元素,相對於 static 定位之外的第一個父元素進行定位。元素的位置經過 "left", "top", "right" 以及 "bottom" 屬性進行規定。
fixed 生成絕對定位的元素,相對於瀏覽器窗口進行定位。元素的位置經過 "left", "top", "right" 以及 "bottom" 屬性進行規定。
relative 生成相對定位的元素,相對於其正常位置進行定位。所以,"left:20" 會向元素的 LEFT 位置添加 20 像素。
static 默認值。沒有定位,元素出如今正常的流中(忽略 top, bottom, left, right 或者 z-index 聲明)。
inherit 規定應該從父元素繼承 position 屬性的值。

以上是較爲官方的定義,你能夠從中獲取到關於position的一些基本操做,接下來讓咱們試圖從另外一個角度來理解position.瀏覽器

position是一種描述物體相對位置的藝術,它的核心是「參考座標系」的選擇佈局

正如咱們在現實生活中看到的那樣,咱們須要使用各類不一樣的「參考座標系」,來更方便準確地描述咱們身邊各類物體的相對位置。由於,咱們已經知道,若是單純依靠一個座標系來描述這個世界上的各個位置,這個世界將變得異常複雜。咱們須要使用不一樣的座標系,來幫住咱們簡化對位置的描述。spa

在咱們的網頁佈局中也是同樣,咱們不能只採用一個座標系來對網頁中的元素進行定位,那將使得咱們的網頁變得異常複雜且不易維護。咱們須要多個不一樣的座標系,來幫助咱們對網頁進行更好地佈局。css中的 position,就是咱們俗稱的「定位」,就是定義了這麼一套規則,使得咱們能夠應用不一樣的座標系,來對頁面中的元素進行更好地佈局和管理。scala

反應在具體的規則當中,就是咱們熟知的static,relative,absolute,fixed。而它們最本質的區別就在於「參考座標系」的不一樣。其餘的一系列問題,例如典型的元素層級,均可以歸結爲position 所帶來的反作用。code

到這裏,你或許對position有了一個不同的認識,接下來能夠嘗試着思考如下幾個問題orm

  1. position的反作用是什麼?
  2. position的應用場景又是什麼?

不知道?不要緊,或許如下的文章可以給你一點提示cdn

反作用

首先咱們須要討論的是position的反作用,緣由很簡單,你手中有一個錘子,你總得知道這個錘子能幹什麼,不能幹什麼。咱們只有像如來佛祖瞭解悟空那樣瞭解position,才能發揮出悟空(position)的真正威力!

要分析position的反作用,咱們或許能夠採用這樣的思路:

如下的組件均指開啓了position定位的元素,這裏把它稱爲組件

  1. 把該組件以及內部的元素做爲一個總體,分析它對外界或者外界對它有什麼影響。簡稱對外的影響
  2. 單獨分析該組件,不考慮對外的影響,分析該position屬性對元素內部的元素會帶來什麼影響。簡稱對內的影響
  3. 除了單獨分析每一個定位屬性的反作用,咱們還須要考慮不一樣的定位屬性之間的組合會帶來什麼樣的反作用,例如最爲典型的relativeabsolute

順着這個思路,接下來咱們來思考不一樣的position屬性都會帶來什麼影響或反作用

反作用absoluterelative

relativeabsolute是咱們再熟悉不過的一對歡喜冤家,它們兩個常常是成對出現,產生的反作用也更多地來自於它們之間的組合。所以在這裏將它們放在一塊兒分析。

特性與反作用每每是相伴而生的。咱們先來回顧一下,這兩個屬性各有什麼特性?

position:absolute元素具有的特性以下:

  1. 相對於最近的設置了非static定位的祖先元素進行定位,若是沒有,則相對於瀏覽器窗口進行定位
  2. 脫離了文檔流,對文檔流不可見
  3. 脫離了文檔流,改變了元素性質,內聯元素和塊元素統一變性成爲脫離文檔流以後的元素,具體表現爲相似inline-block的效果。
  4. 使元素提高一個層級,層級要比浮動和文檔流元素要高

鑑於本人的經驗限制,下面的反作用例子展現的也只是其中的一部分,若有更好的補充,歡迎在下方評論區中與你們一塊兒分享

position:relative元素具有的特性以下:

  1. 參考座標系以該元素在文檔中的原有位置做爲參考點
  2. 不脫離文檔流,所以,元素自己性質不改變,塊元素仍是塊元素,內聯元素依然是內聯元素。
  3. 元素在原始文檔中佔有的位置依然存在,所以在執行relative定位以後,基本不會影響其餘佈局
  4. 元素層級提高一級,元素層級高於浮動元素和文檔流中的元素,也就是說會覆蓋普通文檔流和浮動的元素,可使用z-index來提高元素的層級
  5. 開啓了相對定位的元素,若不設置任何偏移量,將不會發生任何變化(不考慮定位元素內部的變化)

深度relative

若是咱們單獨分析absolute的反作用,咱們會把它簡單地歸結爲脫離文檔流,從而致使兩個最爲明顯的反作用:改變佈局和覆蓋。

可是我認爲這屬於它自己具有的特性,並非它的反作用。我以爲在現實應用中真正存在的反作用在於relativeabsolute的共同應用所形成的問題,在這裏我把它稱做「深度relative」。

所謂的「深度relative」,指的是relative包裹的組件處於較深的層級,也就是被包裹的比較深,致使其中的absolute組件要想不被其餘元素覆蓋,須要一級一級地往f父元素設置z-index或者排查overflow屬性。這個問題最關鍵的核心就是relative組件層級太深。

這裏講得可能有點抽象,讓咱們一塊兒來看一個例子:

這是我在實際項目開發中遇到的一個典型的例子。簡單來說,就是點擊重選按鈕,在指定的位置跳出圖標選擇框,而且在窗口滾動時依然有效。具體效果以下圖所示:

就是這麼一個簡單的例子,固然以上是已經實現好的效果。若是你沒有遇到過相似的問題,你會以爲這個問題很簡單,使用relativeabsolute就能輕鬆解決。可是,在這裏先賣個關子,以上的效果是使用fixed實現的。

咱們首先使用咱們典型的relativeabsolute來實現以上的效果,咱們能夠獲得以下的效果:

首先你會發現一個最爲明顯的問題,就是圖標選擇框被其餘元素蓋住了,這個時候的緣由就不少樣了,或許是由於它所在的祖先元素的元素層級沒有人家高,又或許是由於祖先元素設置了overflow:hidden等等。這個時候若是咱們依然要堅持使用relativeabsolute的解決方案,那咱們一般會採起的解決方案就是,一級一級的網上排查overflow:hidden又或者是設置z-index,直到解決問題爲止。

z-index

說到z-index,這裏能夠簡單下使用方法。它僅在positon屬性不爲static時起做用。從position從基礎能夠了解到absolutefixed會讓元素脫離文檔流,這兩個修飾的元素默認層級天然比文檔流的要高,然而relative則不會脫離文檔流,但當它設置了 TRBL 時它也會覆蓋文檔流,其默認層級天然也是比後者要高的,脫離文檔流和層級的概念不能混淆

同級原則

所謂同級,顧名思義,也就是同級間層級的比較,一個元素position不爲static後,若是不給它的z-index設定值,默認爲0,由下圖能夠看到,A 元素設定了z-index爲0,B元素未設定,E 元素設定了z-index爲負數,按照順序 B 覆蓋住了 A,而E則被A蓋住。日後則是z-index大的把小的覆蓋。

image-20180917213219642

在此僅展現結構,css 代碼則省去。

<div id="1" style="position: absolute;z-index: 0">A</div>
<div id="2" style="position: absolute">B</div>
<div id="3" style="position: absolute;z-index: 1">C</div>
<div id="4" style="position: absolute;z-index: 2">D</div>
<div id="5" style="position: absolute;z-index: -1">E</div>
複製代碼
多級原則

看到這個標題,你可能會問,這是啥意思?其實很簡單,它的意思是,要比較的元素再也不是簡單的同級概念,而是其中一個或二者都有祖先元素。看看下面這個代碼:

<div id="1" style="position:relative;z-index:2;">
  <div id="1-1" style="position:relative;z-index:1;">A</div>
</div>

<div id="2" style="position:relative;z-index:1;">
  <div id="2-1" style="position:relative;z-index:999;">B</div>
</div>
複製代碼

效果圖:

image-20180917214330725

可見,A、B父元素的層級影響了相應的子元素的層級,就算 B 的z-index設的再大,它的父元素的z-index老是小於 A 的父元素的z-index值,這時候不論 A 的z-index怎麼變化,元素 A 就會像圖中同樣一直壓着 B,這就是從父原則。不過這裏要劃一下重點,這裏的父元素不必定要同級,換句話說,兩個元素間的層級比較,是相應的的同級祖先元素各自往下找到第一個position不爲static的的兩個元素之間的比較。舉個栗子,結構改爲下面這個結構,實現效果是同樣的。

<div id="1" style="position:relative;z-index:2;">
  <div id="1-1" style="position:relative;z-index:1;">A</div>
</div>

<div>
  <div id="2" style="position:relative;z-index:1;">
  	<div id="2-1" style="position:relative;z-index:999;">B</div>
  </div>
</div>

複製代碼

以上講了這麼多,你或許以爲怎麼這麼繁瑣,要考慮這兒考慮那兒的,也會發現即便你用這種方法達到了你想要的效果,它也並不能從根本上解決問題。我一個簡單的小組件,你卻須要我去影響那麼多父級元素,誰知道會形成什麼意想以外的狀況。除了這個,設置過多的z-index會致使頁面層級管理的混亂,使得頁面很難管理,全部有一個原則,要麼對z-index作明文的規定,要麼就少用層級過深的z-index

我這裏有一種較好的解決方案,就是「fixed組件」。若是你有更好的解決方案,歡迎在下方評論區中進行分享。

fixed組件

咱們都知道當元素設置了position:fixed以後,該元素將具有如下的特性:

  1. 永遠相對於瀏覽器窗口進行定位
  2. 固定在瀏覽器窗口的某個位置,不隨滾動條滾動
  3. 脫離文檔流

以上是咱們衆所周知的一些特性,其實根據以上的特性,咱們還能夠推出如下這些特性:

  1. 單獨使用fixed屬性,不須要再開啓父元素的定位,使得,減小了不少的z-index屬性的設置

在下面的例子中,咱們只對「圖標組件」設置了position:fixed定位,頁面中的其餘大部分都是position:static組件,所以能夠很輕鬆的解決原來的覆蓋問題,也不會影響其餘佈局,更不會形成z-index管理混亂的問題。

  1. 使用js控制fixed組件的margin,咱們能夠實現,fixed組件相對於父元素進行定位

一般咱們在應用fixed組件的時候,都要解決fixed組件的滾動問題。一般fixed組件不會隨着滾動條的滾動而滾動,可是咱們擁有強大的js,咱們能夠經過爲fixed組件設置一個固定的margin-top,而後經過js動態的控制這個值,使得它能夠相對父元素進行滾動。由於咱們都知道,margin屬性調節的是box之間的距離,fixed組件脫了文檔流,但它依然是一個box,所以咱們可使用margin來調節fixed組件與父元素之間的相對位置。

就拿上面的例子而言,具體代碼以下所示:

反作用fixed

fixed 失效

在固定定位元素的父元素上應用transform屬性,固定定位的元素會相對於父元素來定位

具體可參考如下這篇博文,簡單來講就是transform屬性會對fixed組件形成影響

Eric’s Archived Thoughts: Un-fixing Fixed Elements with CSS Transforms

範式

講了這個多關於position的反作用,我想到這裏你或許也早已經有了一套本身的範式,其實裏面的不少細節咱們都已經在前面提到過。在這裏我將分享一套屬於我本身的position範式,學才疏淺,請不要見怪

  1. 相對於具體元素的小組件能夠採用relative+absolute或者「fixed組件」的解決方案。若是組件層級較深,推薦使用「fixed組件」的方式來進行定位;具體能夠參照上面提到過的圖標選擇框的例子。
  2. 相對於瀏覽器窗口固定的小組件,推薦是用fixed定位,例如右下角的小彈窗,固定的底部導航
  3. 要儘可能減小z-index的嵌套使用
  4. 在使用fixed定位時,要留意transform屬性對它的影響
相關文章
相關標籤/搜索