## 视口
**示例**
```
<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二面