> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/css-xi-lie/00css-chang-jian-wen-da-02.md).

# CSS常见问答02

## layout viewport、visual viewport 和 ideal viewport 有什么区别？

视口（Viewport）是移动 Web 开发中一个非常重要的概念，最早是由苹果公司在推出 iPhone 手机时发明的，其目的是为了让 iPhone 的小屏幕尽可能完整显示整个网页。通过设置视口，不管网页原始的分辨率尺寸有多大，都能将其缩小显示在手机浏览器上，这样保证网页在手机上看起来更像在桌面浏览器中的样子。在苹果公司引入视口的概念后，大多数的移动开发者也都认同了这个做法。

视口（Viewport）简单来说就是**浏览器显示页面内容的屏幕区域**。但在移动端浏览器涉及三个视口（Viewport）概念 ：布局视口（Layout Viewport）、视觉视口（Visual Viewport）和理想视口（Ideal Viewport）。

![三种视口](https://my-files-1259410276.cos.ap-chengdu.myqcloud.com/md_images/three_viewports.jpg)

### 布局视口（Layout Viewport）

布局视口（Layout Viewport）是指**浏览器在其中绘制网页的视口，它涵盖页面上的所有元素**。布局视口的宽度取决于文档内容，不会随用户在屏幕上缩放页面而变化。

获取布局视口：

```js
document.documentElement.clientWidth | document.body.clientWidth
```

**注意：** 一般移动端浏览器都默认设置了布局视口的宽度。根据设备的不同，布局视口的默认宽度有可能是 768px、980px 或 1024px等，这个宽度并不适合在手机屏幕中展示。移动端浏览器之所以采用这样的默认设置，是为了解决早期的 PC 端页面在手机上显示的问题。

### 视觉视口（Visual Viewport）

视觉视口（Visual Viewport）是指是屏幕上实际可见的内容，不包括屏幕键盘、缩放区域之外的区域或任何其他不随页面尺寸缩放的屏幕控件。视觉视口区域的宽度是指浏览器窗口的宽度，而移动设备的浏览器窗口宽度一般也是整个屏幕的宽度。

获取视觉视口：

```js
window.innerWidth
```

**注意：** 当用户缩放页面，或者屏幕键盘等用户界面功能开启/关闭时，视觉视口会变化，但布局视口不会改变。

### 理想视口（Ideal Viewport）

理想视口（Ideal Viewport）是指对设备来讲最理想的视口尺寸，**即布局视口和屏幕宽度一致**。采用理想视口的方式，可以使网页在移动端浏览器上获得最理想的浏览和阅读的宽度。

设置理想视口：

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, minimum-scale=1, user-scalable=no">
```

获取理想视口：

```js
window.screen.width
```

## 滚动条隐藏后，为什么无法再显示？

### ::-webkit-scrollbar 伪元素的层级结构

WebKit 滚动条由多个伪元素组成，形成一个层级结构：

```bash
::-webkit-scrollbar     (整个滚动条容器)
 ├── ::-webkit-scrollbar-track  (滚动条轨道/背景)
 ├── ::-webkit-scrollbar-thumb  (滚动条滑块/可拖拽部分)
 ├── ::-webkit-scrollbar-button  (滚动条按钮)
 └── ::-webkit-scrollbar-corner  (滚动条角落)
```

### 设置 display: none 后，无法通过改变 display 重新显示？

当在 ::-webkit-scrollbar 上设置 display: none 时：浏览器不会创建或渲染该伪元素及其子元素，也就是，伪元素从渲染树中被移除，而非仅设为不可见。

设置 display: none 之后，尝试设置 display: block、display: initial、display: revert 等都不会生效，因为：

* 浏览器已经不再处理该伪元素；
* 后续样式无法作用于一个不存在的元素。

这是一个已知的 WebKit 渲染行为，而不是 CSS 标准的预期行为。

### 为什么设置 thumb 和 track 样式后能显示？

当直接为 ::-webkit-scrollbar-thumb 和 ::-webkit-scrollbar-track 设置样式时，WebKit 会：

* 检测到子伪元素的样式声明；
* 重新评估滚动条的渲染状态；
* 即使父元素有 display: none，浏览器仍会根据子元素样式重新创建这些伪元素；
* 通过 !important 提高优先级，确保样式生效。

这是 WebKit 的一个渲染优化机制：**当子伪元素有明确的样式声明时，浏览器会优先渲染它们，即使父元素被隐藏。**

根据 MDN 文档和 WebKit 实现：::-webkit-scrollbar 伪元素是非标准的，主要用于 WebKit 浏览器。当为滚动条的子部分设置了样式，浏览器会认为你想要自定义滚动条，浏览器会重新渲染滚动条，即使之前被 display: none 隐藏。

### 正确的解决方案

```css
/* 设置子伪元素的样式*/
&::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 4px;
}
&::-webkit-scrollbar-track {
  background-color: transparent;
}

/* 设置滚动条的高度*/
&::-webkit-scrollbar {
  width: 0;
  height: 0;
}
```

## letter-spacing、word-spacing、white-space 有什么不同？

* **letter-spacing：** 用于控制**字符**之间的间距，包括英文字母、汉字以及空格等。
* **word-spacing：** 用于控制**单词**之间间距，换句话说，就是是增加空格的宽度。
* **white-space：** 用于设置如何处理元素中的 空白 (en-US)，包括空格、换行符、制表符等。

`letter-spacing` 具有以下一些特性：

* 默认值是 `normal`，间距是按照当前字体的正常间距确定的。和 0 不同的是，用户代理根据此属性来确定文字的默认对齐方式；
* 无论值多大或多小，第一行一定会保留至少一个字符；
* 一个很大的正或负的值会将字符变为不可读的：设置很大的正值，会使每个字符独占一行，类似垂直排列；设计很大的负值，除第一个字符，会使字符反向排列，除第一个字符，其他字符可能无法阅读；
* 支持小数值，但不支持百分比值。

`white-space` 取值如下：

* normal：连续的空白符会被合并，换行符会被当作空白符来处理；
* nowrap：连续的空白符会被合并，但文本内的换行无效；
* pre：连续的空白符会被保留，在遇到换行符或者 br元素时才会换行；
* pre-wrap：连续的空白符会被保留，在遇到换行符或者 br 元素，或者需要为了填充时才会换行；
* pre-line：连续的空白符会被合并，在遇到换行符或者 br 元素，或者需要为了填充时才会换行；
* break-spaces：与 `pre-wrap` 的行为相同，除了：
  * 任何保留的空白序列总是占用空间，包括在行尾；
  * 每个保留的空格字符后都存在换行机会，包括空格字符之间；
  * 这样保留的空间占用空间而不会挂起，从而影响盒子的固有尺寸（最小内容大小和最大内容大小）。

## min-width|max-width|width 的覆盖规则？

* `max-width` 比 `width` 小时，即使 `width` 使用了 `!important` ，仍是 `max-width` 生效；
* `min-width` 和 `max-width` 冲突时，`min-width` 生效。

此规则也适用于 `height`。

## 参考资料

[布局视口、视觉视口和理想视口分别是什么？](http://www.itheima.com/news/20200910/171437.html)
