# js 盒子模型
js 盒子模型是指通过 js 中提供的一系列的属性和方法,获取页面中元素样式的信息值
var box = document.getElementById('box')
// box 的原型链 __proto__ 指向
// #box -> HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype -> Node.prototype -> EventTarget.prototype -> Object.prototype
1
2
3
2
3
# js 盒子模型部分重要属性:
- clientHeight/clientWidth: 内容的高度/宽度 + 上下/左右的填充(注意,不包含 border)
- clientLeft/clientTop: 左/上 边框的宽度(borderWidth)
- offsetHeight/offsetWidth: 内容的高度/宽度 + 上下/左右的填充 + 上下/左右的边框。如果上下,左右边框相等,等于 clientHeight+clientTop2/clientWidth+clientLeft2
- offsetParent: 当前元素的父级参照物
- offsetLeft/offsetTop: 当前元素的外边框(border)相对父级参照物的内边框的偏移量
- scrollHeight/scrollWidth: 和 cleientHeight/clientWidth 相同(前提是当前元素内容没有溢出),如果溢出,等于真实内容的高度/宽度(包含溢出) + 上/左填充。获取到的结果都是约等于的值,因为同一个浏览器,是否设置 overflow 对于最终结果有影响,在不同的浏览器中获取到的结果不同
- scrollLeft/scrollTop: 滚动条卷去的宽度/高度,可读写。存在最大(内容最大卷去值)最小(0)值。最大值 = dom.scrollHeight - dom.clientHeight
js 盒子模型特点:
- 获取的数值没有小数,都是整数,会在真实结果的基础上做四舍五入
- 获取浏览器可视区域的宽高:document.documentElement.clientWidth,document.documentElement.clientHeight
- 获取整个浏览器真实内容高度/宽度:document.documentElement.scrollHeight/scrollWidth
- scrollTop/scrollLeft: PC端获取浏览器滚动距离,document.documentElement.scrollTop/scrollLeft,mobile 需要获取 document.body.scrollTop/scrollLeft,而且需要注意,在设置 scrollTop 时,PC 通过 document.documentElement.scrollTop/scrollLeft 可以,moblie 需要通过 window.scrollTo(),所以为了兼容都用 window.scrollTo()
- 所以,操作浏览器本身盒子模型属性时,想要都兼容,需要写两套
- client 和 offset 系列以及 scrollWidth, scrollHeight 都是只读属性,只能获取对于的属性值,不可以修改
- scrollTop/scrollLeft: 滚动条卷去的高度/宽度,可读写
document.documentElement[attr] || document.body[attr]
// 必须 documentElement 在前,设置属性也需要写两套
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
1
2
3
4
2
3
4
# offset 部分
offsetTop/offsetLeft 当前元素的外边框(border)相对父级参照物的内边框(不含border)的偏移量,父级参照物可以通过 dom.offsetParent 获取。在同一页面中,最外层的元素是里面所有元素的父级参照物,和 html 层级结构没有必然联系,一般来说,所有元素的父级参照物都是 body。document.body.offsetParent = null
想要改变父级参照物,可以通过 position 定位来实现。可以改变的值为 absolute, fixed, relative。设置了以上属性的元素其自身的 offsetParent 不会改变,仍为 body,其内部子元素的 offsetParent 会变成设置了 position 属性的这个元素
// 模拟 jquery 的 offset 方法,实现获取页面中任意一个元素,距离 body 的偏移(包括上,左偏移),不管当前元素的父级参照物是谁
function offset (curEle) {
let totalLeft = null,
totalTop = null,
par = curEle.offsetParent
// 首先累加自己本身的偏移
totalLeft += curEle.offsetLeft
totalTop += curEle.offsetTop
// 只要父级参照物还不是 body 的父级(null),就去继续累加父级参照物的偏移。需要注意 body 的 offset 也是需要加的,因为 body 本身也有可能有 border,还有需要注意的是 body 的 margin 是不会被计算到的
while (par) {
if (navigator.userAgent.indexOf('MSIE 8.0') === -1) {
// 如果不是 IE 8,就累加父级的 border 宽度,因为 IE 8 会自动加上这部分,就是 offset 是从当前元素的外边框到父级的外边框
// 累加父级参照物的边框的宽度(border)
totalLeft += par.clientLeft
totalTop += par.clientTop
}
// 累加父级参照物的偏移量
totalLeft += par.offsetLeft
totalTop += par.offsetTop
par = par.offsetParent
}
return {top: totalTop, left: totalLeft}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 回到顶部功能
// 1. 匀速回到顶部
// 总时间(duration): 500ms
// 频率(interval): 多长时间走一步 10ms
// 总距离(target): 当前的位置(当前的 scrollTop 值) - 目标的位置(0)
// 步长(step): 每一次走的距离 target/duration -> 每 1ms 走的距离 * interval -> 每一次走的距离
function goTop () {
var duration = 500,
interval = 10
target = document.documentElement.scrollTop || document.body.scrollTop
var step = (target / duration) * interval
var timer = window.setInterval(function () {
var curTop = document.documentElement.scrollTop || document.body.scrollTop
if (curTop === 0) {
window.clearInterval(timer)
return
}
curTop -= step
document.documentElement.scrollTop = curTop
document.body.scrollTop = curTop
}, interval)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 其他 dom 属性:
- contentEditable: 内容是否可编辑
- dataset: 获取 dom 元素上 data-* 的属性,例如设置了 data-test,可以拿到 test
- draggable: 是否允许拖放,这个是一大块内容
- firstChild: 第一个子元素(包含文本,注释啥的),是个对象
- firstElementChild: 第一个子标签元素对象
- hidden: 隐藏,是个属性,如同 class
- id: dom 的 id
- innerHTML: 内部的 html,包含标签,会车,空格啥的
- innerText: 内部的文本
- lastChild: 最后一个子元素(包含文本,注释啥的),是个对象
- lastElementChild: 最后一个子标签元素对象
- nextElementSibling: 下一个相邻的标签元素节点
- nextSibling: 下一个相邻元素节点,包含空格,会车等
- nodeName: 节点名称
- nodeType: 节点类型
- nodeValue: 节点 value 值
- outerHTML: 包含当前标签元素的 html
- outerText: 和 innerText 一样
- parentElement: 父级元素节点
- parentNode: 父级节点,一般和 parentElement 相同
- previousElementSibling: 上一个相邻的标签元素对象
- previousSibling: 上一个相邻的元素对象