1. 程式人生 > >我的2018前端踩坑集錦

我的2018前端踩坑集錦

特殊 .net container user ntb .get 遇到 ans 同事

某著名小白說過 :世上本來到處都是坑,只要走的人多了,也就把坑都給埋了。該小白還說過:坑本身並不可怕,可怕的是踩了一次之後,還第二、第三次踩到了相同的坑。

所謂"坑",主要是由於我們對某些知識點理解不夠透徹,導致在應用的時出現了一些奇怪的問題。因為我們每個人,對於某個知識點的理解程度不一樣,所以,有些坑我覺得真的很坑,但是你可能覺得一點都不坑,因為你早就對它了如指掌了。

這裏列舉的一些坑,都是我過去一年在項目中所遇到過的,並當時在筆記中記錄下來的,現在稍加整理就形成了這篇博客,以供日後查閱。

不知不覺,開頭又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 屬性也算是比較常用的,特別是做一些動畫效果的時候,用它來移動元素的位置,性能會比設置 topleft 要高一些。但是,如果一個元素設置了 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 組件,但是有少部分比如 canvasvideoinputmap 等會渲染成原生組件的。所以,如果你在寫小程序時,想用一段文字覆蓋在一個 canvas 上,發現怎麽設置都無法實現,那是因為 canvas 渲染後是原生組件,而文字是 html 組件,所以無法覆蓋上去的 。那有什麽辦法呢?可以考慮把文字放到 cover-view 上, 它也是一種原生組件,可以覆蓋在 canvas 上的。具體的可以參考小程序官方文檔 原生組件說明

關於2018踩的坑就寫到這裏了,當然還有一些其他的,暫時沒有時間整理,下次如果整理後,再寫一篇補充一下。

如果大家有什麽問題,或者過去踩到過了哪些坑,歡迎在評論區討論哈。

我的2018前端踩坑集錦