likes
comments
collection
share

[译] 两种视口的故事(一)

作者站长头像
站长
· 阅读数 74

原作者: Peter-Paul Koch

原文地址: www.quirksmode.org/mobile/view…

在这个小系列中, 我会解释视口以及各种重要元素的宽度是如何计算的, 如<html>元素, window 和 screen.

本篇文章讲的是桌面端浏览器, 目的是为同样讨论移动端浏览器做铺垫. 大多数 web 开发者凭直觉理解了大部分关于桌面端的概念. 在移动端也有相同的概念, 但更为复杂, 所以提前讨论这些常用术语会非常有助于理解移动端浏览器.

概念:设备像素和 CSS 像素(device pixels and CSS pixels)

你需要明白的第一个概念是 CSS 像素及其与设备像素的区别.

设备像素是我们直觉上所理解的像素. 这些像素确切地指出你在使用哪种设备, 其值通常可由screen.width/height获取.

如果你给某个元素的设置宽度为 128px, 并且显示屏宽度为 1024px, 当你把浏览器窗口调到最大, 屏幕上最多能容纳 8 个这样的元素(大体上是的, 暂时忽略一些微妙的情况).

但是, 如果用户缩放屏幕, 计算结果会随之变化. 若缩放至 200%, 1024px 宽的屏幕只能容纳 4 个 128px 宽的元素.

现代浏览器实现缩放只不过是在"拉伸"像素. 也就是说, 不是元素的宽度从 128 变到 256 像素, 而是实际像素的尺寸增加了一倍. 形式上, 元素的宽度仍然是 128 个 CSS 像素, 尽管它恰好占用了 256 个设备像素的空间.

换句话说, 缩放至 200% 时, 一个 CSS 像素变成一个设备像素的 4 倍.(长两倍, 宽两倍, 共 4 倍).

几张图片可以阐明这个概念. 下面有四个像素, 此时缩放比是 100%. 这个没什么值得注意的; CSS 像素与设备像素完全重叠.

[译] 两种视口的故事(一)

缩小时, CSS 像素开始收缩, 意味着一个设备像素覆盖了多个 CSS 像素

[译] 两种视口的故事(一)

放大时则相反. CSS 像素开始变大, 一个 CSS 像素覆盖了多个设备像素.

[译] 两种视口的故事(一)

这里的关键点在于 CSS 像素, 正是 CSS 像素决定了样式表的呈现.

100% 缩放

开始举例前, 假设缩放比为 100%. 100%缩放更严格的定义是:

100%的缩放比指的是: 一个 CSS 像素等于一个设备像素

100%缩放这个概念对于后续的解释说明非常有用, 但是你不必在日常工作中过于纠结. 桌面端测试网页时, 通常是 100%的缩放比例, 但即使用户放大或缩小, CSS 像素的魔力也会确保你的布局保持相同比例.

屏幕(screen)尺寸

让我们看看一些实用的尺寸. 首先是screen.widthscreen.height. 这俩包含用户屏幕的总宽度和总高度. 这两个尺寸是用设备像素度量的, 因为它是固定的: 是显示器的特性, 而非浏览器.

[译] 两种视口的故事(一)

有意思吧! 可这些信息有什么用呢?

基本上没什么用. 用户的显示器对我们并不重要--除非你想用于 web 统计数据库.

窗口(window)尺寸

相反, 你想知道的是浏览器窗口的内部尺寸. 这个尺寸告诉了你用户还有多少剩余空间可用于 CSS 布局. 其值可由window.innerWidthwindow.innerHeight获得.

[译] 两种视口的故事(一)

很明显, 窗口的内部宽度是用 CSS 像素表示的.

译者注: 可以用鼠标滚轮缩放网页, 然后打印window.innerWidthwindow.innerHeight查看.

你需要知道你的布局有多少能挤在浏览器的窗口, 也要知道用户放大时, 能挤在窗口的布局会变少.

因此, 如果用户放大了, 窗口可用空间会变少, window.innerWidth/Height相应地也会变小.

(Opera 是一个例外, window.innerWidth/Height不会随着用户的放大而减小: 因为这两个属性是通过设备像素测量的. 这个问题在桌面端上很烦人, 在手机上更是致命, 我们稍后会看到)

[译] 两种视口的故事(一)

注意: 计算出来的宽高包括了滚动条(译者注: 垂直水平滚动条). 因为滚动条是窗口内的一部分.(主要是历史原因造成的)

滚动偏移距离(scrolling offset)

window.pageXOffsetwindow.pageYOffset包括文档的水平和垂直滚动距离. 因此你可以获取用户滚动了多少距离.

[译] 两种视口的故事(一)

这些属性使用的也是 CSS 像素. 不管缩放状态是什么, 你都能知道文档向上滚动了多少距离.

理论上, 如果用户向上滚动, 然后放大页面, window.pageX/YOffset的值会改变. 然而, 浏览器试图保持同一元素在页面可见部分的顶部, 以保持网页前后一致. 这也并不总是完美的, 但也意味着window.pageX/YOffset并没有真正的改变: 滚动出窗口的 CSS 像素数仍然保持(大致)一样.

[译] 两种视口的故事(一)

概念: 视口(viewport)

在介绍更多 JavaScript 属性之前, 我们得先介绍一下另一个概念: 视口.

视口的作用是约束<html>元素, 该元素是网页中最顶级的包含块.

这听起来似乎有点模糊, 所以来看一个实例吧. 假设你有一个流式布局, 侧边栏中的其中一个宽度为width: 10%. 此时, 这个侧边栏会随着窗口大小的调整而跟着放大和缩小. 这里的工作原理是什么?

技术上来说, 该侧边栏的宽度是其父元素宽度的 10%. 假设是<body>(且你还没有给它设置宽度). 因此, 问题就变成了<body>的宽度是多少?

通常, 所有块级元素的宽度都是其父元素宽度的 100%(有例外, 但暂时先忽略). 所以<body>与其父元素<html>一样宽.

<html>元素的宽度又是多少呢? 嗨, 它与浏览器窗口一样宽. 这就是为什么width: 10%的侧边栏占据整个浏览器窗口的 10%. 所有 web 开发者都知道和运用.

你不知道的是这在理论上是如何实现的. 理论上, <html>元素的宽度受限于视口的宽度. <html> 元素的宽度是视口宽度的 100%.

反过来, 视口完全就是浏览器窗口: 就是这样定义的. 视口不是一个 HTML 结构, 所以 CSS 不会影响它. 桌面端中, 视口有浏览器窗口的宽高. 移动端情况要复杂的多.

后果

这会产生一些奇怪的后果. 你能在本网站(译者注: 指原文所在的网站)就能看到. 一直滚动到顶部, 然后放大两三次, 结果就是网站内容溢出了浏览器窗口.

然后滚动至右边, 你会看到网站顶部的一蓝色块排列不整齐了.

[译] 两种视口的故事(一)

这是视口的定义所导致的结果. 我给顶部的蓝色块设置了width: 100%. 基于谁的 100%? 是<html>元素, 它与视口同宽, 视口同浏览器窗口同宽.

关键是: 尽管 100%缩放时工作良好, 视口中我们进行了放大操作, 视口宽度变得比网页的总宽度要小. 在视口内的内容倒没什么, 溢出<html>元素的部分, 而且该元素有 overflow: visible, 所以溢出的内容都会被显示出来.

但是, 蓝色块不会溢出. 毕竟我给它设置了width: 100%, 浏览器把视口的宽度赋给了它. 浏览器才不会在乎宽度是否过于狭窄.

[译] 两种视口的故事(一)

文档宽度

我真正需要知道的是页面内容的总宽度是多少, 包括"突出"的部分. 据我所知, 不可能找到这个值(嗯, 除非分别计算页面所有元素的宽度和边距, 但注意这很容易出错).

我开始相信我们需要一个 JavaScript 属性来给出我所说的"文档宽度"(显然是以 CSS 像素为单位).

[译] 两种视口的故事(一)

如果确实感到奇怪, 为什么不把这个值暴露给 CSS 呢? 我想让蓝色块与文档宽度一样宽, 而不是<html>元素的宽度.(不过这确实棘手, 如果无法实现, 我也不会感到惊讶)

浏览器厂商们, 你们怎么看?

视口(viewport)尺寸

你可能想知道视口的尺寸. 可以通过 document.documentElement.clientWidth/Height 获取.

[译] 两种视口的故事(一)

如果你了解 DOM, 你知道 document.documentElement 实际就是<html>元素: 即任何 HTML 文档的根元素. 然而, 视口更高一个层次; 可以这么说, 它是包含<html>元素的元素. 如果给<html>元素一个宽度, 这可能很重要. (顺带说一句, 我不推荐这样做, 但这是可行的.)

在这种情况下, document.documentElement.clientWidth/Height仍然获取的是视口的尺寸, 而不是<html>. (这有点特殊, 只适用于这个元素的这两个属性. 其他情况是元素的实际宽度.)

[译] 两种视口的故事(一)

所以, document.documentElement.clientWidth/Height总是代表视口的尺寸, 而不管<html>元素的尺寸是多少.

两个属性对

视口尺寸不也是从window.innerWidth/Height获取的吗? 是, 也不是.

document.documentElement.clientWidth/Heightwindow.innerWidth/Height是有区别的: 前者不包括滚动条, 后者包括. 尽管这有点催毛求疵.

有这几个属性是由于历史原因. 那时候, Netscape 只支持window.innerWidth/Height, IE 只支持 document.documentElement.clientWidth/Height. 从那时起, 其他浏览器都开始支持clientWidth/Height, 只有 IE 没有支持window.innerWidth/Height.

桌面端可用两个属性对是个小麻烦, 但对于移动端却是福音, 我们会看到.

<html>元素的尺寸

clientWidth/Height给出了视口在任何情况下的尺寸. 但我们如何获取<html>元素本身的尺寸呢? 那就是: document.documentElement.offsetWidth/Height.

[译] 两种视口的故事(一)

这两个属性可访问真实的块级元素<html>, offsetWidth反应的就是设置的width.

[译] 两种视口的故事(一)

事件坐标

现在来谈谈事件坐标. 触发鼠标事件时, 至少有 5 个属性对可确定事件发生的位置. 我们只讨论其中 3 个.

  1. pageX/Y指相对于<html>元素的 CSS 像素坐标.

  2. clientX/Y指相对于视口的 CSS 像素坐标.

  3. screenX/Y指相对于屏幕的设备像素坐标.

pageX/Y clientX/Y screenX/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/heightdevice-width/device-height.

  1. width/height使用与documentElement.clientWidth/Height相同的值(视口). 是 CSS 像素.

  2. device-width/device-height 使用与screen.width/height相同的值(屏幕). 是设备像素.

[译] 两种视口的故事(一)

如何选择呢? 毫无疑问, 当然是width. web 开发者不关注设备宽度, 重要的是浏览器窗口的宽度.

桌面端用width, 而非device-width. 移动端的情况更混乱.

总结

我们对桌面端浏览器特点的研究到此结束. 本系列的第二部分会将这些概念移植到移动端, 并着重区分了与桌面端的一些重要差异.

转载自:https://juejin.cn/post/7194440722970312763
评论
请登录