position:fixed定位問題的解決

本篇文章介紹了一個在開發(fā)中遇到的詭異的問題检盼,排查問題過程頗為艱辛肯污,不過最終結(jié)果還是值得的,因為鞏固了一些基礎知識和好的調(diào)試方法吨枉,它們是:

  • fixed定位的特點
  • 樣式優(yōu)先級的判定
  • 如何使用瀏覽器的DOM還原一閃即逝的錯誤效果

1. 問題說明

在獵蘿卜-獵頭端蹦渣,測試提出這樣一個bug(禪道編號12828):

在獵蘿卜的 訂單 - 我的職位 - 已接單 入口,點擊右側(cè)職位列表的任意職位貌亭,在彈出職位預覽之前(圖2)柬唯,會先看到職位詳情的底部串到左下角位置(圖1)

圖1 異常狀態(tài)
圖2 回到正常的狀態(tài)

2. 調(diào)用過程

線框說明圖1
線框說明圖1

首先介紹下觸發(fā)整個動作的邏輯,為了方便理解圃庭,借用上面的線框圖來說明:

  • projectList.vue是職位列表锄奢,其中每個職位對應的條目封裝成了order-project-item.vue失晴,點擊order-project-item.vue后,會觸發(fā)事件全局的expandOrderPreview
  • order-project-preview.vue作為一個常駐在界面右側(cè)的組件拘央,默認是收起在屏幕右側(cè)的涂屁,在偵聽到了expandOrderPreview事件以后,會從右側(cè)彈出
  • next-bar-affix.vue作為order-project-preview.vue底部的子組件灰伟,會一起出現(xiàn)和消失

3. 具體的代碼邏輯

在下圖中的邏輯代碼拆又,整理成如下描述:

order-project-item.vue觸發(fā)點擊事件expandOrderPreview,被order-project-preview.vue偵聽到栏账,然后進行如下處理:

  • 第一步 emit showOrderPreview事件

    • 父級app-frame.vue收到showOrderPreview事件帖族,給引用的order-project-preview.vue添加一個.previewing的樣式
    • .previewing會使order-project-preview.vue的寬度從0變到特定寬度從而在右側(cè)展示
  • 第二步 調(diào)用refresh方法

    • loading設置為2,order-project-preview.vue中詳情面板受到loading==0v-if控制挡爵,因此會隱藏竖般;loading組件發(fā)現(xiàn)loading>0因此會有加載效果
    • 異步調(diào)用getProjectDetailgetOrders 這兩個方法,直到結(jié)果返回茶鹃,loading值會遞減至0捻激,詳情才開始正常展示,加載效果才會去除
order-project-preview.vue偵聽到彈出事件
app-frame.vue被order-project-preview.vue的emit觸發(fā)進行previewing樣式添加
order-project-preview.vue的refresh方法的邏輯
order-project-preview.vue的模板

4. 一些基礎知識

解決問題之前前计,通過查看樣式代碼胞谭,我們鞏固了一些基礎知識。

如下是抽取出來的DOM結(jié)構(gòu)和樣式設置:

<div class="app-frame">
  <div class="order-project-preview">
    <div class="next-bar-affix">
      <div class="h-affix" style="bottom:0"></div>
    </div>
  </div>
</div>
.order-project-preview {
  position: fixed;
  top: 0;
  bottom:0;
  right: 0;
  width: 300px;
}
.next-bar-affix {
  position: fixed;
  bottom: 0;
}
.next-bar-affix .h-affix {
  position: fixed;
  height: 60px;  
}

  • .order-project-preview進行position: fixed設置后男杈,DOM的位置會相對視口固定丈屹,通過left, right, top, bottom來設置定位
  • 在上述示例代碼中,.order-project-preview寬度300px伶棒,高度撐滿屏幕旺垒,底部欄 .next-bar-affix底部內(nèi)容區(qū)的高度為30px
  • 其中.next-bar-affix部分的代碼是由heyuiaffix組件生成的,一個有意思的啟發(fā)是肤无,在.h-affix中也添加了一個bottom:0的屬性先蒋。可以嘗試將該屬性去除宛渐,會發(fā)現(xiàn)底部的內(nèi)容區(qū)域會跑到最底部之下竞漾,變得不可見。
  • 綜上窥翩,可以從上述效果圖推測的一個結(jié)論是:

如果一個子容器設置了fixed定位业岁,它的父容器也是fixed,那么在不指定子容器的left, right, top, bottom時寇蚊,子容器的邊界會以父容的top left作為邊界笔时。進一步可以推斷,在我們的問題中仗岸,必然是有什么因素使得作為父容器的.order-project-preview失去了作為子容器定位基準的條件**允耿,從而跑到了視口的左下角借笙。

5. 具體推測

做過的一些不靠譜的推測:一個DOM樣式的變化 A->A+B 實際上是 A -> 無 -> A+B,即order-project-preview.vue增加 .previewing的過程中较锡,容器樣式到的中間過程暫時使得next-bar-affix.vue丟失了left的基準提澎,從而導致在中過程中串到了視口左下角。

this.$emit('showOrderPreview');
this.refresh();

基于上述推測念链,為了解決這個問題盼忌,考慮將上述兩行代碼對調(diào):

  • 先讓refresh的邏輯去觸發(fā)order-project-preview.vue的隱藏,自然也就隱藏了next-bar-affix.vue
  • 再觸發(fā)showOrderPreview事件來order-project-preview.vue的展開
  • refresh觸發(fā)的異步調(diào)用結(jié)束以后掂墓,order-project-preview.vue中的具體內(nèi)容才連同next-bar-affix.vue被展示出來

但結(jié)果不如預期的那樣谦纱,猜想也許是refresh 成功隱藏next-bar-affix.vue之前,this.$emit('showOrderPreview');的邏輯引起的彈出生效了君编,于是添加了如下代碼(事實上純屬沒有依據(jù)的HardCode):

this.$emit('showOrderPreview');
this.$nextTick(() => {
  this.refresh();
})

運行跨嘉,沒在出現(xiàn)如上所示的問題,似乎是解決了吃嘿。

6. 問題又重現(xiàn)了

然后祠乃,提交代碼后,測試同學告知兑燥,該問題又重現(xiàn)了亮瓷,重現(xiàn)步驟姑且略過,這時候聯(lián)想到之前排錯的過程中降瞳,就猜想可能跟瀏覽器的渲染順序有關(guān)系嘱支,然后把瀏覽器的工作原理的基礎介紹翻閱了一遍,但并沒有解答這個問題挣饥。

又想起團隊的caoq同學分享過如何監(jiān)聽DOM上的屬性變化除师。問題難點無非在于無法讓瀏覽器在某個異常的時刻停下來,如果能停下來觀測DOM的結(jié)構(gòu)和樣式扔枫,差不多就可以排查到問題根源汛聚。于是給order-project-preview.vue的根節(jié)點設置了attribute modification的斷點(設置方法如下圖所示):

image.png

這時候意外地發(fā)現(xiàn)在異常發(fā)生時,主界面order-proejct-preview.vue的內(nèi)容已經(jīng)加載出來了(但卻沒顯示)短荐,即不存在next-bar-affix.vue 丟失默認的left基準的問題倚舀。

倒是發(fā)現(xiàn)了該容器上添加了一個 .h-loading-parent的樣式,這個樣式定位為 relative 搓侄。由此基本能確定了在heyuiloading組件的渲染中瞄桨,會修改所在父容器order-project-preview.vue的樣式,給它添加上.h-loading-parent讶踪。

因為 order-project-preview.vue 最外圍默認的樣式是 fixed, 被.h-loading-parent覆蓋之后變成了 relative(它們具有相同的優(yōu)先級,后出現(xiàn)的relative覆蓋前出現(xiàn)的fixed)泊交,從而導致了next-bar-affix.vueleft基準丟失乳讥。我們只要把給彈出的面板order-project-preview.vue加上更高優(yōu)先級的fixed定位即可柱查。

//loaing組件提供 樣式
.h-loading-parent {
  position: relative;
}

// 自書寫的樣式
.order-project-preview-page {
  // 兩個class累積的優(yōu)先級為2,大于.h-loading-parent的1:
  &.previewing {
    position: fixed;
  }
}

7. 再深入一些

問題:斷點過程已經(jīng)渲染好的面板卻看不到內(nèi)容,只看到一個錯位的底部欄

通過查看heyui<loading>組件源碼的樣式可以發(fā)現(xiàn):

  • heyui組件在loading完畢后云石,會設置500mstimeout來清除.h-loading-parent的樣式(參加下圖1)唉工,即內(nèi)容渲染出來了,但是樣式500ms內(nèi)還沒去除汹忠,也就導致了relative引起的錯位
  • relative的屬性同樣影響到了 .order-project-preview-page.previewing 的高度淋硝,因為它的各個子元素無一例外都absolute的,因此這樣的文檔流無高度的宽菜,而被覆蓋的fixed谣膳,通過了 top',bottom`賦予了它和視口相同的高度。
Loading的渲染代碼
問題2 我們在本地代碼上無法重現(xiàn)該問題

在本地代碼運行在瀏覽器的樣式發(fā)現(xiàn):.order-project-preview-page上設置的fixed的優(yōu)先級高于 h-loading-parentrelative铅乡。

因此可以做一個猜測

  • 在開發(fā)環(huán)境中庫的樣式被先加載继谚,自己組件內(nèi)地樣式后加載,如果計算的優(yōu)先級的值一樣的話阵幸,自己組件的樣式自然就失效了花履。
  • 在線上環(huán)境,樣式的代碼經(jīng)過壓縮以后挚赊,并不能保留開發(fā)環(huán)境中的出現(xiàn)順序诡壁,因此需要人為的設置來保證所期待的優(yōu)先級。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荠割,一起剝皮案震驚了整個濱河市欢峰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涨共,老刑警劉巖纽帖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異举反,居然都是意外死亡懊直,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門火鼻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來室囊,“玉大人,你說我怎么就攤上這事魁索∪谧玻” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵粗蔚,是天一觀的道長尝偎。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么致扯? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任肤寝,我火速辦了婚禮,結(jié)果婚禮上抖僵,老公的妹妹穿的比我還像新娘鲤看。我一直安慰自己,他們只是感情好耍群,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布义桂。 她就那樣靜靜地躺著,像睡著了一般蹈垢。 火紅的嫁衣襯著肌膚如雪慷吊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天耘婚,我揣著相機與錄音罢浇,去河邊找鬼。 笑死沐祷,一個胖子當著我的面吹牛嚷闭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赖临,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼胞锰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兢榨?” 一聲冷哼從身側(cè)響起嗅榕,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吵聪,沒想到半個月后凌那,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡吟逝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年帽蝶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片块攒。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡励稳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出囱井,到底是詐尸還是另有隱情驹尼,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布庞呕,位于F島的核電站新翎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜料祠,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一骆捧、第九天 我趴在偏房一處隱蔽的房頂上張望澎羞。 院中可真熱鬧髓绽,春花似錦、人聲如沸妆绞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽括饶。三九已至株茶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間图焰,已是汗流浹背启盛。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留技羔,地道東北人罢艾。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓磕瓷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侧馅,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容