我的2018前端踩坑集錦
某著名小白說過 :世上本來到處都是坑,只要走的人多了,也就把坑都給埋了。該小白還說過:坑本身並不可怕,可怕的是踩了一次之後,還第二、第三次踩到了相同的坑。
所謂"坑",主要是由於我們對某些知識點理解不夠透徹,導致在應用的時出現了一些奇怪的問題。因為我們每個人,對於某個知識點的理解程度不一樣,所以,有些坑我覺得真的很坑,但是你可能覺得一點都不坑,因為你早就對它了如指掌了。
這裏列舉的一些坑,都是我過去一年在項目中所遇到過的,並當時在筆記中記錄下來的,現在稍加整理就形成了這篇博客,以供日後查閱。
不知不覺,開頭又bb了這麽多,還是趕緊進入正題哈。
1. 設置透明度(opacity)引起的慘案
之前做爐石盒子的天梯環境頁面,地址是 爐石天梯環境 ,就在項目做得差不多的時候,準備上線了, QA 突然發現了如下的一個 bug:
有一個選擇排序方式的下拉菜單,它的定位是 position: absolute
,正常來說,它應該會覆蓋在其他元素之上的,可是為什麽 0.14%
反而會覆蓋在它上面呢? 在代碼中找了好久,那個 0.14%
並沒有設置 z-index
屬性,也沒有 position: absolute
這樣的東西,真是好郁悶哦。後來到 mdn 查文檔才發現,原來是 opacity
屬性引起 的: opacity 屬性值小於 1 的元素會創建新的層疊上下文
。因為當時我有個偷懶的做法,字體繼承的顏色是 #666
0.14%
(天梯比率)顏色變淺一些,直接加了個 opacity: 0.6
,導致了創建了新的層疊上下文,層級比下拉菜單高了,所以就覆蓋在了上面。具體什麽是層疊上下文,以及哪些屬性會創建新的層疊上下文,這裏也不介紹了有需要的可以參考一下 層疊上下文
雖然上面描述得已經很詳細了,但是可能由於我的表達能力不太好,有些朋友還不是很明白我的意思,可以看一下這裏的 demo 代碼:
<div class="menu"> <div class="title">下拉菜單</div> <div class="menu-list"> <div class="item">菜單1</div> <div class="item">菜單2</div> <div class="item">菜單3</div> </div> </div> <div class="content"> 我是半透明的文字,可以覆蓋在下拉菜單之上哦~ </div> <style> .menu { position: relative; } .menu-list { display: none; position: absolute; background: #ccc; } .menu:hover .menu-list { display: block; } .content { opacity: 0.6; } </style>
將鼠標移動到下拉菜單上,就會發現文字會發生重疊了:
那這個坑有什麽解決辦法呢?最簡單的就是下拉菜單添加個屬性 z-index: 1
。另外,這裏再啰嗦一下,就是z-index
的值不要亂設置。以前剛剛接觸前端時,會經常看一些視頻教程,看到裏面講師動不動就設置個 z-index: 999
之類的特別大的數值。這是一個不好的習慣。張鑫旭老師在《CSS世界》一書中,提到了 不三原則
,就是說一般情況下,z-index
的值不要超過3,基本能滿足大多數的需求了。
2. flex布局:子項溢出後無法查看全部內容
之前做漫畫閱讀器,因為漫畫可能有長圖片,也可能有短圖片。長圖片可以滾動查看,短圖片就居中顯示。所以,很自然會想到用 flex
布局來實現。簡單的代碼如下:
<div class="app">
<img src="https://m.tuniucdn.com/fb2/t1/G1/M00/F1/51/Cii9EFkAaZ-IRgGNAATB18ldk0UAAJzuQN-p1cABMHv15.jpeg" alt="">
</div>
<style>
html,
body {
height: 100%;
}
.app {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
img {
width: 100%;
}
</style>
這裏,我們的.app
容器裏面這裏有一張很長的圖片。當我們運行上面的代碼,如果你仔細觀察原圖和頁面顯示的圖片,就會發現圖片的頂部和底部的一些內容看不到了,滾動條到了一定位置就無法滾動了。正常來說,我們應該可以通過滾動條的上下滑動看到圖片的全部內容才對的。當時我想了很久也沒有想出來原因,最後到 stackoverflow 找到了答案 Can‘t scroll to top of flex item that is overflowing container
答案中有提到,可以設置子項的 margin: auto
來實現內容溢出時也自動居中(包括水平和垂直的):
修改後的 CSS 代碼如下:
.app {
display: flex;
height: 100%;
}
img {
width: 100%;
margin: auto;
}
所以,以後如果在使用 flex
布局實現居中,如果子項的內容會溢出 flex容器
,可以將子項設置為 margin: auto
試試。
3. transfrom 和 fixed 不能在一起!
CSS3 的 transform
屬性也算是比較常用的,特別是做一些動畫效果的時候,用它來移動元素的位置,性能會比設置 top
或 left
要高一些。但是,如果一個元素設置了 transform
屬性,而它的子元素又設置 fixed
定位,那麽這個 fixed
定位的子元素表現會有些奇怪,如下代碼:
<div class="app">
<button onclick="layer.style.display='block'">彈出蒙層</button>
<div class="layer" id="layer"></div>
</div>
<style>
.app {
position: relative;
width: 100px;
height: 100px;
background: #ccc;
/* 使用transform讓元素向下偏移20px */
transform: translate(0, 20px);
}
.layer {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.7);
}
</style>
打開頁面,效果是這樣的:
我們希望點擊“彈出蒙層”按鈕後,就顯示個覆蓋整個窗口的蒙層。但是結果卻出乎意料。如下:
蒙層只是遮住了小正方形,這不是我們想要的結果,因為我們知道,fixed
定位是相對於屏幕視口(viewport
)定位的, 但是,本例卻相對於它的父親元素。這是為什麽呢?如果認真查閱 mdn文檔 ,就會找到答案:
可見,問題的原因在於,我們只是記住了fixed是相對於 viewport
定位,但是也有特殊情況: 當元素祖先的 transform 屬性非 none 時,容器由視口改為該祖先
。所以,這並不是bug,是因為我基礎不紮實導致的。事實上, 除了 transform
會改變 fixed
的定位元素之外,還有其他屬性也會改變,ChokCoco大佬有一篇文章做了詳細的講解,想要了解更多的請點擊 不受控制的 position:fixed
那遇到這種情況怎麽辦呢?比較好的辦法就是把 fixed
元素移到外面去,不要放到有 transform
屬性的元素裏面。但是,有時候我們沒有辦法移到外面怎麽辦呢?比如,這是它是一個子組件,它的某個父親組件就是用了 transform
,那怎麽辦呢?我也不知道怎麽辦,歡迎大家探討一下哈哈哈~
fixed
定位還具有其他的坑,這裏也不展開了,有興趣的可看看github上有大佬整理好的這篇文章 移動端web頁面使用position:fixed問題總結
4. 安卓微信視頻播放器的層級問題
我們都知道, video
標簽設置了 playsinline
就可以內聯播放視頻,而不是全屏播放。(註意:前提是客戶端的 Webview
配置了允許內聯播放,所以有時候雖然設置了 playsinline
,但在某些app裏面打開依然是全屏播放,這不是前端的鍋哦)。最近有一個需求,類似下面這樣的:
頁面上有一個視頻,視頻播下面有一個按鈕,點擊按鈕就彈出一個圖片,該圖片要覆蓋整個屏幕,比如是這樣的:
示例代碼如下:
<video id="video" controls="" playsinline="" src="https://vod.cc.163.com/file/5bcbe1ae9efdc0608bb6d06b.mp4"></video>
<img id="image" src="https://ds.163.com/2018/mrzh/appointment/static/img/bg-body.cdef1ec.jpg" alt="">
<button id="button">彈出圖片</button>
<style>
video,
img {
width: 100%;
}
img {
display: none;
position: absolute;
top: 0;
}
</style>
<script>
var video = document.getElementById('video');
var image = document.getElementById('image');
var button = document.getElementById('button');
button.onclick = function() {
image.style.display='block';
}
image.onclick = function() {
this.style.display='none';
}
</script>
但是,在安卓上卻發現一個問題,開始播放視頻後(註意,只有播放視頻後才可以復現),點擊“彈出圖片”按鈕,顯示如下所示:
圖片無法覆蓋在視頻播放器上面。然後,我設置了 z-index
或者 transform
,都沒有任何效果。最後, 剩下的可能原因就是: 安卓微信視頻播放器實際上用的是原生組件。為了驗證這一猜想,我們可以啟用開發者選項的繪圖模式(開發者選項 --> 繪圖 --> 顯示布局邊界,不同機型不一樣,找不到的請百度找一下哈),結果如下:
看到沒有,視頻是一個完整的有邊框的東西,證明他是一個獨立於 Webview
的原生組件。
那怎麽辦呢?只能上網找答案呀!我們都知道,微信 webview
使用的是 X5
內核,所以我也希望能從它的開發者文檔上找到一些有用的信息,好不容易找到了一篇叫做 H5同層播放器接入規範 。它說可以可以在 video
標簽添加一個屬性 x5-video-player-type
,並且給出的例子是這樣的:
<video src="http://xxx.mp4" x5-video-player-type="h5"/>
我當時很高興,以為問題就這樣解決了,然並卵,添加了 x5-video-layer-type
屬性之後,playsinlie
屬性就失效了,無法內聯播放了,只能全屏播放,所以,不能添加這個屬性。
然後我就想,既然無法覆蓋這個視頻,那在彈出圖片的時候能不能把視頻給隱藏掉?然後關閉圖片的時候再把視頻顯示回來呢?於是就把 JS 代碼改成下面這樣:
button.onclick = function() {
image.style.display='block';
video.style.display = 'none';
}
image.onclick = function() {
this.style.display='none';
video.style.display = 'block';
}
然後,這樣就可以了。因為我在網上找不到更好的辦法,如果大家遇到這個問題可以參考這種做法。當然,如果你們找到了有更好的辦法,歡迎評論分享出來哈~
關於安卓微信視頻播放器的坑就先講到這裏啦。
等等,一講到原生組件,這裏還得再補充一下微信小程序相關的東西,當然,我自己還沒有過小程序的開發的經驗, 這是之前的一次內部交流會,一位同事的分享:小程序在渲染的時候,大多數組件都是渲染成 HTML
組件,但是有少部分比如 canvas
、 video
、 input
、 map
等會渲染成原生組件的。所以,如果你在寫小程序時,想用一段文字覆蓋在一個 canvas
上,發現怎麽設置都無法實現,那是因為 canvas
渲染後是原生組件,而文字是 html
組件,所以無法覆蓋上去的 。那有什麽辦法呢?可以考慮把文字放到 cover-view
上, 它也是一種原生組件,可以覆蓋在 canvas
上的。具體的可以參考小程序官方文檔 原生組件說明
關於2018踩的坑就寫到這裏了,當然還有一些其他的,暫時沒有時間整理,下次如果整理後,再寫一篇補充一下。
如果大家有什麽問題,或者過去踩到過了哪些坑,歡迎在評論區討論哈。
我的2018前端踩坑集錦