## 視口
**示例**
```
<meta name="viewport" content="width=device-width; initial-scale=1.0">
@media screen and (max-width: 480px){
// TODO
}
```
### 什么是像素?
一個像素就是屏幕能夠顯示的一種特定顏色的最小區域, 屏幕上的像素越多, 同一時間你能夠看到的就越多. 設備尺寸相同但像素變得更加密集時, 屏幕能夠顯示的畫面的過渡更細致, 網站看起來更加明快
實際上, 有2種像素:
1. 設備像素: 設備屏幕的物理像素, 任何設備的物理像素的數量都是固定的.
2. CSS像素: 為Web開發者創造的, 在CSS和JavaScript語言中使用的一個抽象的層.
`width:200px`代表200個CSS像素, 相當于多少個物理像素取決于屏幕的特性(是否是高密度)和用戶進行的縮放. 用戶放大得越大, 一個CSS像素覆蓋的設備像素就越多.
每一個CSS聲明和幾乎所有的JavaScript屬性使用的都是CSS像素, 實際上幾乎從來不用設備像素, 唯一的例外是`screen.width/height`
### 三個視口
**html元素的包含塊是什么?**
在CSS標準文檔中, 視口被稱為包含塊, 這個初始包含塊是所有CSS百分比寬度推算的根源, 它給CSS布局限制了一個最大的寬度.
1. 布局視口
在手機上, 視口與移動瀏覽器的寬度不再關聯, 而是完全獨立, CSS布局會根據它來計算, 并被它約束. 例如給PC網站設計的1024x768的布局, 在移動端的布局視口就是1024
x768, 但屏幕默認會進行縮放以能夠在手機屏幕上顯示下, 除非你設置了理想視口`<meta name='viewport'>`
2. 視覺視口
它是用戶正在看到的網站區域, 用戶可以通過縮放來操作視覺視口, 同時不會影響布局視口(仍保持原來的寬度).
視覺視口對開發者不太重要, 除非你想通過javascript知道用戶正在看網頁的那部分.
3. 理想視口
對設備來說最理想的布局視口尺寸. 顯示在理想視口的網站擁有最理想的瀏覽和閱讀的寬度, 用戶剛進入頁面時不需要進行縮放.
只要當網站是為手機準備的時候才應該使用理想視口, 也就是只有當你添加了meta標簽時理想視口才會生效, 如果沒有meta viewport標簽, 那么布局視口將會維持它的默認寬度
定義理想視口是瀏覽器的工作, 而不是設備或操作系統的工作, 因此, 同一設備上的不同瀏覽器可能擁有不同的理想視口
**總結**
1. 在在桌面瀏覽器中, 瀏覽器窗口就是約束你的CSS布局的視口(又被稱為初始包含塊), 它決定了用戶看到什么.
2. 在手機上, 桌面視口被拆分為兩個: 布局視口會顯示你的CSS布局, 視覺視口會決定用戶看到什么.
3. 移動瀏覽器還有一個理想視口, 它是對于特定設備上的特定瀏覽器的布局視口的一個理想尺寸.
4. 可以把布局視口的尺寸設置為理想視口. 這就是響應式設計的基礎.
### 縮放
從技術上講, 縮放是一個放大或縮小CSS像素的過程, 典型的例子是變換到一個可讀的字體大小, 但縮放會影響到頁面中的所有元素.
縮放會影響桌面和手機的(視覺)視口的尺寸, 放大使視口變小, 因而屏幕上顯示的CSS像素變少了; 縮小使視口變大, 因而屏幕上顯示的CSS像素變多了.
桌面和手機上的一個重大的區別是, 在手機上布局視口不會被縮放影響, 但在桌面會, 因為在桌面上布局視口和視覺視口是同一個視口
**最大和最小的縮放比例**
在手機上, 用戶可以縮放到20%到500%, 也就是5個因子. 通過合理使用meta標簽, 你可以講范圍擴大到10%~1000%, 但安卓Webkit永遠是25%~400%.
瀏覽器根據理想視口的大小來計算縮放程度, 所以`width: 25%`是相對于理想視口的寬度來的.
### 分辨率
**物理分辨率**
所有屏幕都有物理分辨率. 用像素的數量除以屏幕的以英寸為單位的寬度可以得到你的設備的每英寸的點數, 簡稱DPI. 每英寸的像素數量越多越好, 因為這意味著畫面會更清晰.
**設備像素比**
JavaScript有一個屬性`window.devicePixelRatio`, CSS也有一個`device-pixel-ratio`(基于webkit的瀏覽器)和分辨率的媒體查詢(所有瀏覽器都支持), 但它們都根物理分辨率無關.
它們代表的是設備像素個數和理想視口的比, 稱為像素比(Device Pixel Ratio), 簡稱DPR. 如iphone4s的DPR=1, iphone5s的DPR=2, iphone6p的DPR=3
JavaScript的屬性`window.devicePixelRatio`和CSS的`device-pixel-ratio`單位都是dppx: 每一個像素的點數, 但實際上是不允許在后面添加單位的.
```
if(window.devicePixelRatio >= 2){
// DPR >= 2
}
@media screen and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi){
// TODO
}
```
為了兼容<IE11的版本, 比如使用dpi單位代替(因為不用使用ddpx), 而1ddpx=96dpi.
**meta視口**
meta視口有如下幾個值:
1. width: 設置布局視口的寬度為特定的值
2. initial-scale: 設置頁面的初始縮放程度和布局視口的寬度
3. minimum-scale: 設置用戶可縮放的最小程度
4. maximum-scale: 設置用戶可縮放的最大程度
5. user-scalable: 是否阻止用戶進行縮放
當用戶旋轉設備時, 布局視口的寬度也會發生變化, 但iOS的safari是一個例外, 它不會根據設備旋轉調整理想視口.
`width=device-width`將布局視口的尺寸設置為一個理想的值, 如果為device-width, 那就講布局視口的寬度設置為理想視口的寬度
`initial-scale`有一個副作用: 它會同時將布局視口的尺寸設置為縮放后的尺寸, 結果就是`initial-scale`和`width=device-width`的效果是一樣的, 看起來沒有什么作用, 但它會讓iOS的safari根據設備旋轉調整理想視口.
**媒體查詢**
媒體查詢有以下3中類型:
1. 媒介類型查詢: 如all, screen, print等.
2. 視口相關的媒體查詢-這一部分的核心
3. 特性相關的媒體查詢: 瀏覽器是否支持X特性?
```
@media all and (max-width: 400px) { // 只有在布局視口的寬<=400px時生效
}
```
**在媒體查詢中使用em**
在媒體查詢中1em相對的是html元素的字體大小, 默認html字體元素為16px.
**轉向**
window.orientation
portrait: 橫向
landscape: 縱向
所有瀏覽器都支持.
```
@media all and (orientation: portrait){
}
```
**js處理視口**
1. `document.documentElement.clientWidth/clientHeight`返回的是布局視口的寬高, 普遍支持
2. `window.innerWidth/innerHeight`返回視覺視口的尺寸, 接近普遍支持.
3. `screen.width/height`返回理想視口的尺寸, 有很嚴重的瀏覽器兼容問題.
**screen.width/height**
可能有2種類型的值:
1. 理想視口的尺寸
2. 屏幕的設備像素尺寸
記住, device-width和device-height媒體查詢使用了js提供的screen.width/height, 不用管瀏覽器使用的是哪個定義.
**改變meta視口標簽**
```
var meta = document.getElementByTagName('meta')[0];
meta.setAttribute("content", "wdith=device-width");
```
**orientationchange事件**
所有webkit和blink瀏覽器都支持, IE11和Firefox31不支持
**resize事件**
只要視口尺寸發生改變, resize事件就會觸發. 最好不要信任移動端的resize事件, 它在瀏覽器上的差別太大了.
----
## CSS
移動瀏覽器和桌面瀏覽器對CSS的支持不盡相同, 有如下4個原因:
1. 桌面端的用例在觸摸屏上不存在, 或者無法更好地適應觸摸屏, 比如hover.
2. 需要考慮視口規范時沒有說明是哪個視口(布局視口或者視覺視口), 比如單位vw和vh.
3. 對獨立可滾動層的需求, 這在各種資源受限制的移動設備上更難實現, 如`background-attachment`.
4. 硬件限制, 尤其是涉及內存和GPU的時候, 比如過渡(transition)和動畫(animation)
### position: fixed
在手機屏幕放大和縮小時有問題, 但在android4.0以上的機器上基本沒有問題了.
### overflow: auto
iPhone上通過`--webkit-overflow-scrolling: auto`來指定是否滾動
### background-attachment
### 尺寸單位vw和vh
vw和vh尺寸單位代表布局視口的百分比. 50vw代表視口的50%.
目前只有blink內核和IE, android上的firefox支持.
## :active, :hover
`:focus`在移動端可以很好的工作. 所以更加安全的技巧是同時使用`:active`和`:focus`
---
## 觸摸和指針事件
4個觸摸事件:
1. touchstart: 在用戶的手指觸摸屏幕的瞬間觸發
2. touchmove: 在用戶移動手指的過程中連續地觸發
3. touchend: 用戶的手指離開屏幕的瞬間觸發
4. touchcancel: 其含義取決于瀏覽器, 很少使用
### 手勢事件
`gesturestart`, `gesturechange`, `gestureend`事件只有ios的safari支持.
### 其他事件
`touchenter`和`touchleave`曾經包含在規范中, 但從來沒有被實現
**問題: 制作下拉菜單?**
### 等價事件
|鼠標| 觸摸 | 鍵盤 |
|----|--------|-----|
|mousedown| touchstart| keydown|
|mousemove| touchmove | keydown/keypress|
|mouseup| touchend | keyup|
|mouseover| - | focus |
|mouseout| - | blur|
**觸摸事件的不同之處**
1. 多個觸摸事件可以同時發生
2. 鼠標指針總是指向某一個像素, 但手指觸摸會覆蓋更多的像素點
3. 觸摸事件是不連續的, 而鼠標事件是連續的
**判斷是否支持觸摸事件? **
```
var hasTouch = !!('ontouchstart' in window)
```
### 觸摸事件的級聯
**輕觸操作的事件級聯**
1. touchstart/pinterdown
2. touchend/pointerup
3. mouseover
4. mousemove
5. mousedown
6. mouseup
7. click
8. :hover樣式應用于元素
**事件級聯的總結**
1. 滑動操作(swipe): touchstart, touchmove, touchend, scroll
2. 縮放操作(Pinch): touchstart, touchmove, touchend, scroll, 可能還有resize
3. 雙觸(Double-Tap): touchstart, 兩次touchend, scroll, 可能還有resize
4. 按住(Touchhold): touchstart, touchend, 有些瀏覽器還會觸發contentmenu事件
**safari: 鼠標事件冒泡**
只有如下幾種情況下Safari中的鼠標事件和click事件才會冒泡:
1. 目標元素是一個鏈接或者是表單域
2. 目標元素或者上溯到其祖先元素(上溯到body但不包含)顯示包含任意鼠標事件的處理函數. 這個函數可以是空函數
3. 目標元素或其祖先元素
如果你遇到了safari的冒泡問題, 可以給元素設置一個空的click處理函數即可.
### 剖析Click
當你激活某個元素時, 瀏覽器總會觸發click事件. 當前所有的瀏覽器都支持click事件, 未來的瀏覽器也會支持click事件. 所以一個重要的指導原則是: 堅持使用click事件.
**移動端300毫秒延遲**
這是因為在手指觸摸屏幕的瞬間, 瀏覽器無法預知用戶是在輕觸(Tap), 雙觸(Double-Tap), 滑動(Swipe), 按住不放(Hold)還是其他什么操作.
chrome在頁面包含`<meta name='viewport' content='width=device-width'>`時, 假設頁面不會出現雙觸操作, 因為頁面不需要縮放. 在這種情況下, 瀏覽器不需要等待下一個輕觸操作就可以直接觸發相應的事件級聯
**同一像素**
在桌面瀏覽器上, click事件只有當mousedown和mouseup在同一個像素觸發后才會觸發. 也就是按下和松開鼠標之間不能移動鼠標指針. 但在觸摸設備上這就無法工作, 因為你的大手指不可避免會觸摸到一小塊屏幕區域, 然后移動一點距離, 最后抬起. 所以touchstart和touchend事件不在同一個像素觸發, click事件也就沒有觸發.
好在移動瀏覽器廠商處理了這種情況: 允許用戶手指移動4-20個像素, 以決定是觸發touchmove還是click.
### 剖析觸摸事件
```javascript
var el = [an element]
el.addEventListener("touchstart", handleTouch, false);
function handleTouch(e){
// e.type 事件類型
// e.target 事件目標元素
// 返回fasle或者e.preventDefault()會阻止默認行為
}
```
**touchList**
觸摸事件有一個指針事件沒有的touchList數組屬性, 其中包含了每個觸摸點的信息. 如果用戶使用四個手指觸摸屏幕, 這個數組就會有四個元素. 一共有3個這樣的數組:
1. touches: 當前觸摸屏幕的觸摸點數組.
2. changedTouches: 導致觸摸事件被觸發的觸摸點數組. 只包含引起事件的觸摸點信息: 那個移動的手指
3. targetTouches: 事件目標元素上的觸摸點數組. 只包含那兩個放在元素上的手指對應的觸摸信息
這里有一個技巧: 使用changedTouches. 因為如果用戶最后一個手指離開屏幕觸發touchend事件, 這最后一個觸摸點信息不會出現在touches或者targetTouches數組中, 但是會出現在changedTouches數組中.
**獲取事件坐標**
```
function handleTouch(e){
// 如果需要, 用pageX/Y代替clientX/Y
var touch = e.changedTouches[0];
var coorX = touch.clientX;
var coorY = touch.clientY;
}
```
changedTouches數組中的第一個元素就是導致事件觸發的那個觸摸點對象(通常這個觸摸點數組不包含其他對象).
> clientX/Y和pageX/Y的區別在于前者相對于視覺視口的左上角, 后者相對于布局視口的左上角. 布局視口是可以滾動的.
**100%兼容所有瀏覽器獲取事件坐標的函數**
```
function findCoordinates(e){
var x, y ;
if(e.changedTouches){ // touch event
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
}else{ // pointer or mouse event
x = e.clientX;
y = e.clientY;
}
return [x, y];
}```
### 離開元素
如果你在某個元素上綁定了touchstart, touchmove, touchend事件, 即使你的手指已經離開了這個元素, touchmove事件還是會觸發. 有的瀏覽器還會發出touchend.
- 職業生涯
- 如何提升你的能力?給年輕程序員的幾條建議
- 那些年,那些事
- 阿里巴巴離職DBA 35歲總結的職業生涯
- 人生的四種選擇
- 程序人生的四個象限和兩條主線
- 幾縷代碼與閑思
- 張小龍-學習筆記
- Web前端
- 移動Web手冊
- 精通CSS: 高級Web標準解決方案
- 悟透JavaScript
- 架構設計
- 大型網站技術架構
- 周愛民-大道至簡
- RESTful Web Services Cookbook - 讀書筆記
- 大話設計模式
- Unix編程藝術
- 把程序員修煉之道讀薄
- 學習能力
- 奇特的一生:讀書筆記
- zhh-看源碼那些事
- 一個創業者怎么看待讀書和寫作
- 程序員修煉之道
- 2015/1/5 頭腦風暴
- 書單計劃
- 2014年我讀過的那些書
- 我的后端開發書架2015
- 別人的書單
- 讀書筆記
- 浪潮之巔
- 達內時期自己筆記整理
- Effective Java
- 打造facebook: 讀書筆記
- 面試整理
- 阿里面試的一點感受
- 騰訊的三輪面試
- 三十之惑–面霸
- 前端面試問題匯總
- 八爪網絡面試總結
- 2015面試總結總結
- 找工作流程梳理
- 最全前端面試問題及答案總結
- 前端開發面試題收集
- 百度web前端--2015一面
- 百度web前端--2015二面