移動端的總體佈局通常來講能夠分爲上中下三個部分,分別爲 header、main、footer,其中header、footer 是固定高度,分別固定在頁面頂部和頁面底部,而 main 是佔據頁面其他位置,而且能夠滾動。html
頁面佈局以下:web
<body>
<div class="header"></div>
<div class="main"></div>
<div class="footer"></div>
</body>
根據頁面滾動的位置分爲兩種佈局,一種是滾動 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 以外都須要滾動,但不少時候,咱們只但願頁面的某個元素滾動,這個時候,就採起第二種佈局方式。flex
這種頁面佈局有三種相對簡單的實現方式:flexbox
最容易想到的實現方式是 fixed 定位,實現方式以下:spa
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 定位在移動端會有兼容性問題,後面會提到,因此不建議這種實現方式。code
absolute 定位和 fixed 定位相似,只要把 header 的 footer 的 position 改成 absolute 就能夠了。orm
細心的小夥伴可能發現了,這裏的 main 沒有設置 overflow ,由於這裏有一個坑,不論是absolute 定位仍是 fixed 定位都同樣,爲了方便描述,如下只說 fixed 定位(在 absolute 定位也同樣成立)。在PC端沒有問題,可是在移動端,若是 main 設置了 overflow 爲 true,header 會被 main 遮住,對,沒有錯,雖然是 fixed 定位,可是在移動端,若是 fixed 定位節點後面緊接跟着的兄弟節點是可滾動的(也就是設置了 overflow 爲 true ),那麼 fixed 節點會被其後的兄弟節點遮住。
這個問題解決方式有不少,既然是 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; }
要在有 input 標籤的頁面使用 fixed 定位,由於這二者在一塊兒的時候,老是會有奇奇怪怪的問題。
在 iOS 上,當點擊 input 標籤獲取焦點喚起軟鍵盤的時候,fixed 定位會暫時失效,或者能夠理解爲變成了 absolute 定位,在含有滾動的頁面,fixed 定位的節點和其餘節點一塊兒滾動。
其實這個問題也很好解決,只要保證 fixed 定位的節點的父節點不可滾動,那麼即便 fixed 定位失效,也不會和其餘滾動節點一塊兒滾動,影響界面。
可是除此以外,還有不少坑比較難以解決,例如 Android 軟鍵盤喚起後遮擋住 input 標籤,用戶無法看到本身輸入的字符串,iOS 則須要在輸入至少一個字符以後,才能將對應的 input 標籤滾動到合適的位置,因此爲了避開這些難以解決的坑,在有表單輸入的頁面,儘可能用absolute 或者 flex 替換 fixed。
在 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 事件。
var inputLock = false; function do(inputElement) { var regex = /[^1-9a-zA-Z]/g; inputElement.value = inputElement.value.replace(regex, ''); } inputElement.addEventListener('compositionstart', function() { inputLock = true; }); inputElement.addEventListener('compositionend', function(event) { inputLock = false; do(event.target); }) inputElement.addEventListener('input', function(event) { if (!inputLock) { do(event.target); event.returnValue = false; } });
添加一個 inputLock 變量,當用戶未完成直接輸入前,inputLock 爲 true,不觸發 input 事件中的邏輯,當用戶完成有效輸入以後,inputLock 設置爲 false,觸發 input 事件的邏輯。這裏須要注意的一點是,compositionend 事件是在 input 事件後觸發的,因此在 compositionend事件觸發時,也要調用 input 事件處理邏輯。
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-image: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; } } }