译---a tale of viewport-part one
这一节将解释说明viewport和各种重要元素的宽度是如何工作的,例如元素<html>,以及窗口和屏幕。
这个页面是关于桌面浏览器的,它的唯一目的是为移动浏览器的类似讨论奠定基础。大多数web开发人员已经直观地理解了大多数桌面概念。在移动设备上,我们会发现同样的概念,但是更加复杂,并且对每个人都已经知道的术语进行预先讨论将极大地帮助您理解移动浏览器。
设备像素和css像素
第一个需要理解的概念是css像素,以及它与设备像素的差异。
设备像素被直观的认为是“正确”的像素。它给出了我们正在使用的各种设备的分辨率,可以通过读取screen.width/height获取具体的数值。
如果给一个元素设置宽度width:128px,你的显示器是1024px,最大化你的浏览器,8个给定宽度的元素刚好填满你的显示屏(当然这并非绝对精确,先忽略那些细节)。
如果用户使用放大或者缩小功能,填充的数量将会发生变化。如果放大浏览器到200%,那么填充1024宽度的显示器将只需要四个width:128px的元素。
在现代浏览器中,缩放只不过是“拉伸”像素。元素的宽度并没有从128px变化为256px,但是实际像素翻倍了。形式上,该元素依然具有128个css像素的宽度,但是却占据了256个设备像素。
换句话说,缩放到200%,使得CSS像素扩大为设备像素的四倍(宽度两倍,高度两倍,总为四倍)。
用几张图来说明这个概念。这里是一个缩放100%的四个像素。CSS像素与设备像素完全重叠。
现在来缩小,CSS像素就会缩小,也就是一个设备像素与多个CSS像素重叠。
再来放大,就会发生相反的情况。CSS像素开始变大,结果一个css像素可以多个设备像素重叠。
这里的重点是,你只需要关注CSS像素,因为它直接影响你如何呈现元素样式。
设备像素对您几乎毫无用处。不是对用户;用户会将页面放大或缩小,直到他能够舒服地阅读为止。然而,缩放级别对您来说并不重要。浏览器会自动确保你的CSS布局被拉伸或压缩。
100%缩放
最开始假设缩放100%,是时候给出一个确切的定义:
缩放级别100%,一个CSS像素正好等于一个设备像素。
100%缩放的概念对于理解接下来的内容很有用,但日常工作中,并不需要过多担心。在桌面,通常都是在100%缩放级别下测试你的网站,即使用户选择放大或者缩小操作,CSS像素的魔法会确保你的网站保持原有的布局。
屏幕尺寸(screen size)
看一看实际的测量。首先,screen.width和screen.height,它们分别表示用户屏幕的宽度和高度。这个数值大小的单位是设备像素,因为它们不会发生改变:它们是显示器的特征,并非浏览器的。
PS:IE7和IE8模式下,测量使用的是CSS像素。
Fun!但是我们用这个信息来做什么呢?
基本上,什么都不做。用户显示器的大小对我们来说并不重要——除非你需要将这些数据记录在web统计数据库中。
窗口大小(window size)
相反,你需要知道浏览器窗口的内部尺寸。这个大小就是你可以用来进行CSS布局的空间大小。可以通过window.innerWIdth和window.innerHeight得出这个数值。
PS:这个尺寸包含滚动条的宽度,单位是CSS像素。IE浏览器不支持。Opera得出的结果是设备像素单位。
显然,这个窗口的内部尺寸测量使用的是CSS像素。你需要知道你的布局有多少可以挤进浏览器窗口,当用户放大时,可以挤进去的内容就会减少。用户放大时,你可利用的窗口空间就会变小。window.innerWidth/Height数值就会反映出来。
Opera浏览器是一个例外。用户进行放大操作时,window.innerWidth/Height数值并不会发生变化。该数值的单位是设备像素。在桌面很烦人,在移动设备上确实非常致命的,下面再议。
记住,这个数值包含滚动条。滚动条也是包含在窗口的尺寸中(由于历史原因)。
滚动偏移(scrolling offset)
window.pageXOffset和window.pageYOffset表示文档水平和垂直滚动的偏移量。通过这个数值,可以知道用户滚动了多少
这些测量数值是CSS像素单位的。无论缩放程度如何,你都可以知道文档被滚动了多少。
理论上,如果用户滚动了页面,然后再放大。window.pageX/YOffset会发生变化。事实上,浏览器试图通过在用户缩放时保持可见页面顶部的相同元素来保持web页面的一致性。虽然实际效果并不完美,但是重要的是window.pageX/YOffset并不会发生改变——滚动超出窗口的部分的CSS像素数值并不会发生改变。(经过测量,实际数值会发生非常微小的差距)
the viewport
在继续介绍更多JavaScript属性之前,我们必须引入另一个概念:viewport。
viewport是用来约束<html>元素,该元素是网站页面最外层的包含块。
这样说可能还是觉得不太好理解吧。来举一个例子。假设你有一个流布局页面,页面中有一个宽度width:10%的侧边栏。当你调整浏览器窗口大小时,侧边栏会整洁的放大或者缩小。这是怎么回事呢?
从技术上来说,侧边栏宽度就是其父元素宽度的10%。假设其父元素是<body>(你并没有给body一个固定的宽度),那么问题就变成了<body>元素的宽度从何而来?
通常,所有块级元素会填充其父元素,也即宽度为父元素的宽度(当然也有例外,先忽略)。所以<body>和它的父元素宽度一样,也就是<html>元素。
那么,<html>的宽度呢?它和浏览器窗口一样宽,这就是为什么侧边栏总是会占据浏览器窗口的10%。所有web开发人员都知道并在使用这个事实。
但是你可能想要知道这其中的原理。理论上,元素<html>的宽度是由viewport的宽度约束的。<html>获取viewport宽度的100%。
反过来,viewport与浏览器窗口完全一样,因为它是被这样定义的。viewport并不是HTML结构,不可以通过CSS影响它。在桌面系统,viewport的宽高就是浏览器窗口的宽高。但是在移动设备上略微复杂一点。
意外(consequences)
这种情况产生了一些奇怪的后果。你可以在这个网站上看到其中一个。滚动到顶部,并放大页面,以便该站点的内容溢出浏览器窗口。
现在向右滚动,您将看到站点顶部的蓝色栏不再正常对齐。
这就是viewport定义带来的意外。我给顶部的蓝色条设置width:100%,但是100%是相对于谁呢?是<html>元素,它和viewport具有一样的宽度,和浏览器窗口宽度一样。
重点是,页面内容在100%缩放时完全能够正常显示。当进行放大操作时,viewport变得比网站宽度要小了。对于它本身,这并没有什么影响,内容从<html>中溢出了,但是由于设置了overflow:visible,溢出的内容都会显示出来。
顶部的蓝色条并没有溢出。我设置了width:100%,于是浏览器给了它跟viewport一样的宽度。他们并不关心宽度是不是太小。
(这个图应该是带有水平滚动条的,但是图片看不出来)
文档宽度(document width?)
我真正需要知道的是页面的总内容有多宽,包括“突出”的部分。就我所知,要找到这个值是不可能的(嗯,除非您计算页面上所有元素的单独宽度和页边距,但这很容易出错)。
我开始相信,我们需要一个JavaScript属性对来提供我称之为“文档宽度(document width)”(显然是CSS像素)。
如果我们真的感觉很时髦,那为何不把这个值暴露给CSS呢?我很愿意将我的蓝色条宽度设置为100%的document width,而不是<html>的宽度。(实现起来大概很棘手吧,即使无法实现,我也不会意外)
浏览器供应商们,你们怎么想呢?
测量viewport
你也许想要知道如何测量viewport。document.documentElement.clientWidth/Height提供了这样的功能。
如果你了解DOM,你就会知道document.documentElement其实就是<html>元素:HTML文档的根元素。然而,可以说,viewport更高一层。它是包含元素<html>的元素。这会影响到也许你会给<html>一个宽度(虽然我不推荐,但是这是可能并允许的)
在下面这种情况下,document.docuemntElement.clientWidth/Height依然会给出了viewport的数值,而非<html>元素。(这是一个特殊的规则,只适用于这个元素,只适用于这个属性对。在所有其他情况下,都使用元素的实际宽度)
document.documentElement.clientWidth/Height总是给出viewport的大小,无论<html>的多大多小
属性对
但是viewport宽度的大小不是也由window.innerWidth/Height给出的吗?是也不是。
有一个形式上的差别,那就是window.innerWidth/Height包含了滚动条,但是document.documentElement.clientWidth/Height没有包含。这有点吹毛求疵。
事实上,这两对属性是浏览器战争的遗留问题。当时Netscape只支持window.innerWidth/Heigh,IE只支持document.documentElement.clientWidth/Height。从那时起,所有其他浏览器都开始支持clientWidth/Height,但IE没有选择window.innerWidth/Height。
在桌面上有两个可用的属性对是一个小麻烦,但我们将会看到,在移动端却是一个福音。
测量html大小(page)
clientWidth/Height无论在设么情况下都可以给出viewport的大小。但,我们从哪里可以知道<html>元素的大小呢 。该数值存储在document.documenElement.offsetWidth/Height。
这些属性使您能够以块级元素的形式访问<html>元素;如果设置宽度,offsetWidth将会反映出这个数值。
事件坐标
接下来说说事件坐标。当一个鼠标事件触发时,公开的属性对不少于5对,以提供有关事件确切位置的信息。在我们的讨论中,其中三个问题很重要:
- pageX/Y能够给出相对于<html>元素的坐标(CSS像素)
- clientX/Y给出相对于viewport的坐标(CSS像素)
- scrrenX/Y给出相对于屏幕的左边(设备像素)
90%你都会使用pageX/Y,大多情况下,你需要知道事件相对于文档的坐标。剩下的10%,你会需要clientX/Y。而相对于屏幕的坐标几乎从来不用。
媒体查询
最后是一些关于媒体查询的单词。这个想法非常简单:可以定义特殊的CSS规则,只有当页面的宽度大于、等于或小于一定的值时才执行这些规则。例如:
div.sidebar { width: 300px; } @media all and (max-width: 400px) { // styles assigned when width is smaller than 400px; div.sidebar { width: 100px; } }
侧边栏的宽度是300px,除非宽度小于400px时,侧边栏将会变成100px.
有一个问题:这里的宽度是什么宽度呢?
这里有两个媒体查询:width、height 和device-width/device-height
- width/height 使用的是document.docuemntElement.clientWidth/Height(也就是viewport),使用的CSS像素单位
- device-width和divece-height使用的是screen.width和height(也就是屏幕),使用的设备像素
我们应该使用哪个呢?显而易见,当然是:width。web开发者们对设备宽度(device width)不感兴趣,重要的是浏览器窗口的宽度。
所以使用width,忘记device-width吧——在桌面系统中。就如我们所知,在移动设备中会复杂的多。
总结
设备像素与CSS像素是不同的,web开发者基本上只需要关注CSS像素即可,css设置的值直接影响页面元素的CSS像素大小。
window.pageX/YOffset表示页面滚动偏移量。(滚动隐藏起来的部分CSS像素)
window.pageX/YOffset与document.docuemtnElement.scrollTop/Left得到的都是滚动偏移量。前者只读,后者可以读写。前者主要应用在现代浏览器中(IE8及其以下不兼容),后者几乎兼容所有浏览器(混杂模式的IE浏览器下,使用document.body代替document.docuemntElement)
document,docuemntElement,clientWidth/Height——viewport的大小
window.innerWIdth/Height表示浏览器窗口(标签页)的大小,包含滚动条。(CSS像素)
innerWidth/Height 和clientWidth/Height差异在包不包含滚动条(浏览器战争的遗留问题,IE不支持innerWidth)。
document.docuemntElement.offsetWidth/Height表示<html>大小,也就是页面的总大小。IE浏览器中得到的是viewport的大小,而非<html>的大小。
document.documentElement.clientX/Y——鼠标相对于视口(viewport)的位置。(10%)
document.documentElement.pageX/Y——鼠标相对于页面(page)的位置。(90%)
document.documentElement.screenX/Y——鼠标相对于屏幕的位置。(几乎不用)
媒体查询中用来的width/height是viewport的大小,也就是document.docuemntElement.clientWidth.Height.
有关移动浏览器的请看下一篇翻译