# 元素的宽高位置信息梳理

## 前言

日常开发中总会遇到要计算dom元素的位置大小的需求，每次都是用到再查资料，于是想着开始整理这部分知识，便于以后自己翻阅。

## 1. 有没有document标签？

html 文档最顶层的标签为html，没有document标签，于是给document标签添加css并没有效果。document是个抽象的概念， [点击查看详情](https://developer.mozilla.org/zh-CN/docs/Web/API/Document)

document.body 返回文档中的 `<body>` 或 `<frameset>` 节点\
document.documentElement 返回当前文档的直接子节点，一般是 `<html>` 节点

### 1.1 网页的宽高与浏览器宽高

网页宽度 = 浏览器窗口宽度 + 横向滚动距离\
网页高度 = 浏览器窗口高度 + 竖向滚动距离

## 2. 元素的宽高

[Element.clientWidth](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/clientWidth)表示元素的内部宽度，以像素计，包括 `padding`。但不包括滚动条，`border` 和 `margin`。

[Element.scrollWidth](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollWidth)表示不使用水平滚动条的情况下适合视口中的所有内容所需的最小宽度，包括 `padding`。但不包括滚动条(因为没有滚动条)，`border` 和 `margin`。还包括伪元素的宽度，例如::before或::after。 如果元素的内容可以适合而不需要水平滚动条，则其scrollWidth等于clientWidth.

### 2.1 clientWidth

```
div {
  width: 200px;
  padding: 20px;
  box-sizing: border-box;
  // border-box：表示 width200 = padding20*2 + 160内容宽度，此时clientWidth即为200
}

div {
  width: 200px;
  padding: 20px;
  box-sizing: content-box;
  // border-box：表示 width240 = padding20*2 + 200内容宽度，此时clientWidth即为240
}
```

> clientWidth 属性值会被四舍五入成整数，要获取小数的话，使用`Element.getBoundingClientRect()`

### 2.2 document.documentElement.clientWidth()

始终返回浏览器可视区域的宽度，即浏览器窗口的大小(无论内部是否有滚动)，不包含滚动的宽度,即设置`html,body及其它元素的宽度`均不影响该值。

```
html {
  width: 4000px;      // 无关  只取决于可视区宽度
}

body {
  width: 3500px;      // 无关
}

#position {
  width: 3000px;      // 无关
}
```

### 2.3 document.documentElement.scrollWidth()

其值取决于网页中所有元素(html自身+子元素为所有元素)中 `最宽的元素` ,(当子元素很宽的时候，可能将该元素宽度撑开)的`width`(即包含滚动的宽度),设置`html,body及其它元素的宽度`均会改变该值,但是 `最小值` 为`document.documentElement.clientWidth()`,即浏览器的可视区域宽度。

### 2.4 document.body.clientWidth()

取决于`body`标签的宽度值, 而`body`宽度值又取决于 `html` 设置的 `css` 宽度,但与 `子元素` 的`css`宽度无关(即使子元素宽度再宽也不影响该值)。

```
html {
  width: 2000px;
}

body {
  width: 3500px;    // 首先取值，没有该值则 `继承html的width`
}

#position {
  width: 3000px;  // 与该值无关
}
```

### 2.5 document.body.scrollWidth()

```
//1 
html {
}

body {
  width: 3500px;  如果body比子元素宽，则取body的值. 返回3500
}

#position {
  width: 3000px;
}

// 2
html {
  width: 4000px;
}

body {   // 不设置该值 则继承4000， 4000 > 3000 返回4000
}

#position {
  width: 3000px;
}

//3  
html {
  width: 4000px;
}

body {
}

#position {
  width: 5000px;  // 子元素更宽 取5000
}
```

### 2.6. [Element.getBoundingClientRect](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect)

获取元素相对于浏览器窗口左上角的定位属性值，当进行滚动时，该值可能会发生变化，因此如果想获取元素相对于整个网页左上角定位属性值:

> 那么只要给top、left属性值加上当前的滚动位置（通过document.documentElement.scrollLeft和document.documentElement.scrollTop），这样就可以获取与当前的滚动位置无关的值

`Element.scrollTop` 属性可以读取或设置该元素竖直滚动条到该元素顶部的距离。

`Element.scrollLeft` 属性可以读取或设置该元素水平滚动条到元素左边的距离。

### 2.7 [Element.getClientRects()](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getClientRects)

不常用: 用于内部存在换行的情况。

```
<p style={{width: 60, padding: 0}} id='position'>
  <span>
    Paragraph that spans mutiple lines
  </span>
</p>
```

如上，当对 `span` 取 `getBoundingClientRect`时,会返回一个对象。而对 `span` 取 `getClientRects`时,由于p标签限制宽度，导致span换成五行，一个单词一行。则此时 `getClientRects` 返回五个单词各自的 `getBoundingClientRect`组成的集合。

### 2.8 [HTMLElement.offsetLeft()](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetLeft)

返回相对其[offsetParent](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetParent)的左侧距离。

> offsetParent指向最近的父级定位元素

### 2.9 HTMLElement.offsetTop()

返回相对其offsetParent的顶部距离

### 2.10 [window.getComputedStyle(element)](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getComputedStyle)

获取element的计算样式(包含继承之后计算的值)。 如：`window.getComputedStyle(element).color` 获取颜色。

## 3. [jquery位置信息API](https://api.jquery.com/)

```
$ele.innerWidth ()  // content + padding

// 可传参，为true时，则将 margin 计算在内
$ele.outerWidth (isIncludeMargin)  // content + padding + border + (isIncludeMargin ? margin : 0)

$ele.width()  // content 不包含padding

$ele.css('width')  // content + padding + 'px'       // 返回值带有 'px',其它的 `API` 都是返回数字

// 返回或设置相关的定位信息
.offset()
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yes-1-am.gitbook.io/blog/javascript/yuan-su-de-kuan-gao-wei-zhi-xin-xi-shu-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
