移动端 Web 开发踩坑之旅
前言
最近在一個移動端的 Web 項目中踩了很多的坑,感覺有必要把它們記錄下來,分享給即將踏入移動端 Web 開發大門的新人們。
一、從布局說起
移動端的整體布局一般來說可以分為上中下三個部分,分別為 header、main、footer,其中header、footer 是固定高度,分別固定在頁面頂部和頁面底部,而 main 是占據頁面其余位置,并且可以滾動。
(上圖是使用純 CSS 實現,然后截圖,上傳到專欄有點失真,看官老爺們將就著看吧。)
頁面布局如下:
根據頁面滾動的位置分為兩種布局,一種是滾動 body,另一種是固定 body 的高度為100%,在 main 中滾動。
第一種布局有個優點,就是頁面的地址欄會隨著 body 的滾動隱藏起來,并且 Android 設備中,滾動 body 會更加的流暢,如果項目中有類似需求可以考慮。
實現布局的方式如下:
body?{overflow:?auto; }.header, .footer?{position:?fixed;left:?0;right:?0;height:?44px; }.header?{top:?0; }.footer?{bottom:?0; }.main?{height:?100%;padding:?44px?0; }第一種情況比較適合長列表頁面,整個頁面除了 header 和 footer 之外都需要滾動,但很多時候,我們只希望頁面的某個元素滾動,這個時候,就采取第二種布局方式。
這種頁面布局有三種相對簡單的實現方式:
fixed 定位
absolute 定位
flex 定位
最容易想到的實現方式是 fixed 定位,實現方式如下:
html,?body?{height:?100%;overflow:?hidden; } .header, .footer?{position:?fixed;left:?0;right:?0;height:?44px; }.header?{top:?0; }.footer?{bottom:?0; }.main?{height:?100%;padding:?44px?0;box-sizing:?border-box; }fixed 定位實現起來簡單,在大多數瀏覽器中也能正常顯示,但是 fixed 定位在移動端會有兼容性問題,后面會提到,所以不建議這種實現方式。
absolute 定位和 fixed 定位類似,只要把 header 的 footer 的 position 改為 absolute 就可以了。
細心的小伙伴可能發現了,這里的 main 沒有設置 overflow ,因為這里有一個坑,不管是absolute 定位還是 fixed 定位都一樣,為了方便描述,以下只說 fixed 定位(在 absolute 定位也一樣成立)。在PC端沒有問題,但是在移動端,如果 main 設置了 overflow 為 true,header 會被 main 遮住,對,沒有錯,雖然是 fixed 定位,但是在移動端,如果 fixed 定位節點后面緊接跟著的兄弟節點是可滾動的(也就是設置了 overflow 為 true ),那么 fixed 節點會被其后的兄弟節點遮住。
這個問題解決方式有很多,既然是 fixed 定位后面緊接著可滾動的兄弟節點才會有這個坑,只要讓他的條件有一個不成立就好了,有以下解決方案:
讓 fixed 定位節點后面不緊接著可滾動的節點
不讓 scroll 節點遮住 fixed 節點
第一種方方案有以下可選方法:
1. 把所有 fixed 節點放在 scroll 元素后面,即把 header 節點放在 main 節點后面
<body><div?class="main"></div><div?class="header"></div><div?class="footer"></div> </body>但這樣顯然不太符合一般人的思維習慣,代碼可讀性降低。
2. 使 main 不可滾動,給 main 嵌套一層可滾動的子節點
<body><div?class="header"></div><div?class="main"><div?class="scroll-container"></div></div><div?class="footer"></div> </body><style>.main?{overflow:?hidden;}.scroll-container?{height:?100%;overflow:?auto;} </style>第二種方案有以下可選方法:
1. 讓 scroll 節點不與 fixed 節點有重合
body?{padding:?44px?0; }.main?{padding:?0; }2. 給 fixed 節點設置 z-index
.header, .footer?{z-index:?8888; }看到這里可能會有小伙伴覺得,一個簡單的布局,還要繞過這么多坑,難道沒有簡單的方式嗎,答案當然是肯定的,那就是第三種實現方式,flex 布局。flex 定位在移動端兼容到了 iOS 7.1+,Android 4.4+,如果使用 autoprefixer 等工具還可以降級為舊版本的 flexbox ,可以兼容到 iOS 3.2 和 Android 2.1。而且用 flex 實現起來相對簡單,在各個瀏覽器里表現也相對一致。實現如下:
body?{display:?flex;flex-direction:?column; } .main?{flex:?1;overflow:?auto;-webkit-overflow-scrolling:?touch; } .header?{height:?44px; } .footer?{height:?44px; }二、fixed 與 input
剛接觸移動端 Web 開發的小伙伴應該都會聽前輩們說過,不要在有 input 標簽的頁面使用 fixed 定位,因為這兩者在一起的時候,總是會有奇奇怪怪的問題。
在 iOS 上,當點擊 input 標簽獲取焦點喚起軟鍵盤的時候,fixed 定位會暫時失效,或者可以理解為變成了 absolute 定位,在含有滾動的頁面,fixed 定位的節點和其他節點一起滾動。
其實這個問題也很好解決,只要保證 fixed 定位的節點的父節點不可滾動,那么即使 fixed 定位失效,也不會和其他滾動節點一起滾動,影響界面。
但是除此之外,還有很多坑比較難以解決,例如 Android 軟鍵盤喚起后遮擋住 input 標簽,用戶沒法看到自己輸入的字符串,iOS 則需要在輸入至少一個字符之后,才能將對應的 input 標簽滾動到合適的位置,所以為了避開這些難以解決的坑,在有表單輸入的頁面,盡量用absolute 或者 flex 替換 fixed。
三、input 的 compositionstart 和 compositionend 事件
在 Web 開發中,經常要對表單元素的輸入進行限制,比如說不允許輸入特殊字符,標點。通常我們會監聽 input 事件:
inputElement.addEventListener('input',?function(event)?{let?regex?=?/[^1-9a-zA-Z]/g;event.target.value?=?event.target.value.replace(regex,?'');event.returnValue?=?false });這段代碼在 Android 上是沒有問題的,但是在 iOS 中,input 事件會截斷非直接輸入,什么是非直接輸入呢,在我們輸入漢字的時候,比如說「喜茶」,中間過程中會輸入拼音,每次輸入一個字母都會觸發 input 事件,然而在沒有點選候選字或者點擊「選定」按鈕前,都屬于非直接輸入。
所以輸入「喜茶」兩個字,會觸發6次 input 事件,如果把每次 input 的 value 打印出來,結果如下:
這顯然不是我們想要的結果,我們希望在直接輸入之后才觸發 input 事件,這就需要引出我要說的兩個事件—— compositionstart 和 compositionend。
compositionstart 事件在用戶開始進行非直接輸入的時候觸發,而在非直接輸入結束,也即用戶點選候選詞或者點擊「選定」按鈕之后,會觸發 compositionend 事件。
添加一個 inputLock 變量,當用戶未完成直接輸入前,inputLock 為 true,不觸發 input 事件中的邏輯,當用戶完成有效輸入之后,inputLock 設置為 false,觸發 input 事件的邏輯。這里需要注意的一點是,compositionend 事件是在 input 事件后觸發的,所以在 compositionend事件觸發時,也要調用 input 事件處理邏輯。
四、iOS 1px border 實現
iOS設備上,由于retina屏的原因,1px 的 border 會顯示成兩個物理像素,所以看起來會感覺很粗,這是一個移動端開發常見的問題。解決方案有很多,但都有自己的優缺點。
0.5px border
從iOS 8開始,iOS 瀏覽器支持 0.5px 的 border,但是在 Android 上是不支持的,0.5px 會被認為是 0px,所以這種方法,兼容性是很差的。
背景漸變
CSS3 有了漸變背景,可以通過漸變背景實現 1px 的 border,實現原理是設置 1px 的漸變背景,50% 有顏色,50% 是透明的。
@mixin?commonStyle()?{background-size:?100%?1px,1px?100%?,100%?1px,?1px?100%;background-repeat:?no-repeat;background-position:?top,?right?top,??bottom,?left?top; }@mixin?border($border-color)?{@include?commonStyle();background-p_w_picpath:linear-gradient(180deg,?$border-color,?$border-color?50%,?transparent?50%),linear-gradient(270deg,?$border-color,?$border-color?50%,?transparent?50%),linear-gradient(0deg,?$border-color,?$border-color?50%,?transparent?50%),linear-gradient(90deg,?$border-color,?$border-color?50%,?transparent?50%); }這種方法雖然可行,但是沒有辦法實現圓角。
偽類 + transform
這類方法的實現原理是用偽元素的 box-shadow 或 border 實現 border,然后用 transform縮小到原來的一半。即使有圓角的需求也能很好的實現。
@mixin?hairline-common($border-radius)?{position:?relative;z-index:?0;&:before?{position:?absolute;content:?'';border-radius:?$border-radius;box-sizing:?border-box;transform-origin:?0?0;} } @mixin?hairline($direct:?'all',?$border-color:?#ccc,?$border-radius:?0)?{@include?hairline-common($border-radius);&:before?{transform:?scale(.5);@if?$direct?==?'all'?{top:?0;left:?0;width:?200%;height:?200%;box-shadow:?0?0?0?1px?$border-color;z-index:?-1;}?@else?if?$direct?==?'left'?or?$direct?==?'right'?{#{$direct}:?0;top:?0;width:?0;height:?200%;border-#{$direct}:?1px?solid?$border-color;}?@else?{#{$direct}:?0;left:?0;width:?200%;height:?0;border-#{$direct}:?1px?solid?$border-color;}} }總結
以上的坑都是在項目里頻繁遇到的,每一個都給出了對應的解決方式,但由于筆者也是初入坑移動 Web 開發的新人一枚,所以給出的方案未必是最合適的,做了點微小的工作,希望能為大家提供一點幫助,不足的地方也請大家多多指正。
轉載于:https://blog.51cto.com/12879205/1922420
總結
以上是生活随笔為你收集整理的移动端 Web 开发踩坑之旅的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [UOJ #222][NOI2016]区
- 下一篇: python基础一 -------如何在