沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • javascript 面试题汇总
  • let、var、const的区别

    • let、var、const的区别
  • 值和引用

    • 值和引用
  • 包装类型

    • 包装类型
  • 数据类型的转换

    • 数据类型的转换
  • 运算符

    • 运算符
  • 原型和原型链

    • 原型和原型链
  • 执行栈和执行上下文

    • 执行栈和执行上下文
  • 作用域和作用域链

    • 作用域和作用域链
  • this指向

    • this指向
  • 垃圾回收与内存泄漏

    • 垃圾回收与内存泄漏
  • 闭包

    • 闭包
  • DOM事件的注册和移除

    • DOM 事件的注册和移除
  • DOM事件的传播机制

    • DOM 事件的传播机制
  • 阻止事件的默认行为

    • 阻止事件默认行为
  • 递归

    • 递归
  • 属性描述符

    • 属性描述符
  • Class和普通构造器的区别

    • class 和构造函数区别
  • 浮点数精度问题

    • 浮点数精度问题
  • 严格模式

    • 严格模式
  • 函数防抖和节流

    • 函数防抖和节流
  • WeakSet和WeakMap

    • WeakSet 和 WeakMap
  • 深浅拷贝

    • 深浅拷贝
  • 函数柯里化

    • 函数柯里化
  • Node的事件循环

    • Node的事件循环
  • eval

    • eval
  • 尺寸和位置

    • 尺寸和位置
      • DOM 对象相关尺寸和位置属性
        • 只读属性
        • clientWidth 和 clientHeight 属性
        • offsetWidth 和 offsetHeight 属性
        • clientTop 和 clientLeft 属性
        • offsetLeft 和 offsetTop 属性
        • scrollHeight 和 scrollWidth 属性
        • 可读可写属性
        • scrollTop 和 scrollLeft 属性
        • domObj.style.xxx 属性
      • event 事件对象相关尺寸和位置属性
        • clientX 和 clientY 属性
        • screenX 和 screenY 属性
        • offsetX 和 offsetY 属性
        • pageX 和 pageY 属性
  • 更多知识

    • 更多知识
  • JS面试题汇总
  • 尺寸和位置
luzhichang
2023-10-14
目录

尺寸和位置

# 尺寸和位置

在 JavaScript 中操作 DOM 节点使其运动的时候,常常会涉及到各种宽高以及位置坐标等概念,如果不能很好地理解这些属性所代表的意义,就会在书写代码时遇到不小的问题。而由于这些属性概念较多,加上浏览器之间实现方式不同,常常会造成概念混淆。

本章,我们就一起来总结一下使用 JavaScript 操作 DOM 时,尺寸和宽高相关的属性。

主要分为以下两部分:

  • DOM 对象相关尺寸和位置属性
    • 只读属性
      • clientWidth 和 clientHeight 属性
      • offsetWidth 和 offsetHeight 属性
      • clientTop 和 clientLeft 属性
      • offsetLeft 和 offsetTop 属性
      • scrollHeight 和 scrollWidth 属性
    • 可读可写属性
      • scrollTop 和 scrollLeft 属性
      • domObj.style.xxx 属性
  • event 事件对象相关尺寸和位置属性
    • clientX 和 clientY 属性
    • screenX 和 screenY 属性
    • offsetX 和 offsetY 属性
    • pageX 和 pageY 属性

# DOM 对象相关尺寸和位置属性

在 DOM 对象所提供的尺寸和位置相关属性中,可以分为只读属性和可读可写属性。

# 只读属性

所谓的只读属性指的是 DOM 节点的固有属性,该属性只能通过 JavaScript 去获取而不能通过 JavaScript 去设置,而且获取的值是只有数字并不带单位的( px、em 等 )

常见的只读属性有:

  • clientWidth 和 clientHeight 属性
  • offsetWidth 和 offsetHeight 属性
  • clientTop 和 clientLeft 属性
  • offsetLeft 和 offsetTop 属性
  • scrollHeight 和 scrollWidth 属性

下面我们来一组一组进行介绍。

# clientWidth 和 clientHeight 属性

该属性指的是元素的可视部分宽度和高度,即 padding + content,例如:

<div id="container" class="container"></div>
.container{
  width: 200px;
  height: 200px;
  background-color: red;
  border: 1px solid;
  overflow: auto;
  padding: 10px;
  margin: 20px;
}
var container = document.getElementById("container");
console.log("clientWidth:",container.clientWidth); // 220
console.log("clientHeight:",container.clientHeight); // 220

# offsetWidth 和 offsetHeight 属性

这一对属性指的是元素的 border+padding+content 的宽度和高度。例如:

var container = document.getElementById("container");
console.log("offsetWidth:", container.offsetWidth); // 222
console.log("offsetWidth:", container.offsetWidth); // 222

可以看到该属性和 clientWidth 以及 clientHeight 相比,多了设定的边框 border 的宽度和高度。

# clientTop 和 clientLeft 属性

这一对属性是用来读取元素的 border 的宽度和高度的。例如:

var container = document.getElementById("container");
console.log("clientTop:", container.clientTop); // 1
console.log("clientLeft:", container.clientLeft); // 1

# offsetLeft 和 offsetTop 属性

首先需要介绍一下 offsetParent 属性,该属性是获取当前元素的离自己最近的并且定了位的祖先元素,该祖先元素就是当前元素的 offsetParent。

如果从该元素向上寻找,找不到这样一个祖先元素,那么当前元素的 offsetParent 就是 body 元素。

offsetLeft 和 offsetTop 指的是当前元素,相对于其 offsetParent 左边距离和上边距离,即当前元素的 border 到包含它的 offsetParent 的 border 的距离。

下面我们来具体举例说明:

var container = document.getElementById("container");
console.log(container.offsetParent); // body

可以看到,我们直接访问 container 盒子的 offsetParent 属性,因为不存在定了位的祖先元素,所以显示出来的是 body 元素。

接下来我们对上面的代码进行改造:

<div id="container" class="container">
  <div id="item" class="item"></div>
</div>
.container {
  width: 200px;
  height: 200px;
  background-color: red;
  border: 1px solid;
  overflow: auto;
  padding: 10px;
  margin: 20px;
  position: relative;
}
.item{
  width: 50px;
  height: 50px;
  background-color: blue;
  position: absolute;
  left: 100px;
  top: 100px;
}

当前效果如下:

image-20211105143812415

接下来我们来获取 item 盒子的 offsetLeft 以及 offsetTop 属性值。

var container = document.getElementById("container");
var item = document.getElementById("item");
console.log(item.offsetParent); // container 盒子 dom 对象
console.log(item.offsetLeft); // 100
console.log(item.offsetTop); // 100

接下来我们不对 item 子元素进行定位,而是使用 margin 的方式来设置子盒子的位置,如下:

.item{
  width: 50px;
  height: 50px;
  background-color: blue;
  margin: 50px;
}

然后再次获取 item 盒子的 offsetLeft 以及 offsetTop 属性值,如下:

var container = document.getElementById("container");
var item = document.getElementById("item");
console.log(item.offsetParent); // container 盒子 dom 对象
console.log(item.offsetLeft); // 60
console.log(item.offsetTop); // 60

可以看到,打印出来的是 60,因为我们设置的 margin 的值为 50,但是其定了位的父元素还设置了 10 像素的 padding,所以加起来就是 60。

# scrollHeight 和 scrollWidth 属性

顾名思义,这两个属性指的是当元素内部的内容超出其宽度和高度的时候,元素内部内容的实际宽度和高度。

如果当前元素的内容没有超过其高度或者宽度,那么返回的就是元素的可视部分宽度和高度,即和 clientWidth 和 clientHeight 属性值相同。

<div id="container" class="container">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nulla repellat porro atque culpa rem sunt sed! Voluptates vel incidunt accusamus reiciendis aut, adipisci ut. Hic, impedit officia.Quis, animi beatae. Facere dolorum quasi laborum, rem facilis illum necessitatibus sint doloribus beatae
exercitationem sapiente! Quod vel cupiditate quam libero, delectus natus.</div>
.container {
  width: 200px;
  height: 200px;
  background-color: red;
  border: 1px solid;
  overflow: auto;
  padding: 10px;
  margin: 20px;
}

上面的代码中,我们为 container 盒子加入了一些文字,使其能够产生滚动效果,接下来访问 scrollHeight 和 scrollWidth 属性,如下:

var container = document.getElementById("container");
console.log("scrollWidth:", container.scrollWidth); // scrollWidth: 220
console.log("scrollHeight", container.scrollHeight); // scrollHeight 372

如果 container 盒子不具备滚动的条件,那么返回的值和 clientWidth 和 clientHeight 属性值是相同的。

<div id="container" class="container"></div>
var container = document.getElementById("container");
console.log("scrollWidth:", container.scrollWidth); // scrollWidth: 220
console.log("scrollHeight", container.scrollHeight); // scrollHeight 220

# 可读可写属性

所谓的可读可写属性指的是不仅能通过 JavaScript 获取该属性的值,还能够通过 JavaScript 为该属性赋值。

# scrollTop 和 scrollLeft 属性

这对属性是可读写的,指的是当元素其中的内容超出其宽高的时候,元素被卷起的高度和宽度。

<div id="container" class="container">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nulla repellat porro atque culpa rem sunt sed! Voluptates vel incidunt accusamus reiciendis aut, adipisci ut. Hic, impedit officia.Quis, animi beatae. Facere dolorum quasi laborum, rem facilis illum necessitatibus sint doloribus beatae
exercitationem sapiente! Quod vel cupiditate quam libero, delectus natus.</div>
.container {
  width: 200px;
  height: 200px;
  background-color: red;
  border: 1px solid;
  overflow: auto;
  padding: 10px;
  margin: 20px;
}
var container = document.getElementById("container");
container.onscroll = function () {
  console.log("scrollTop:", container.scrollTop);
  console.log("scrollLeft", container.scrollLeft);
}

在上面的代码中,我们的 container 盒子内容超出了容器的高度,我们为该盒子绑定了 scroll 事件,滚动的时候打印出 scrollTop 和 scrollLeft,即元素被卷起的高度和宽度。

效果如下:

2021-11-05 15.15.58

该属性因为是可读可写属性,所以可以通过赋值来让内容自动滚动到某个位置。例如很多网站右下角都有回到顶部的按钮,背后对应的 JavaScript 代码就是通过该属性来实现的。

# domObj.style.xxx 属性

对于一个 DOM 元素来讲,它的 style 属性返回的也是一个对象,并且这个对象中的任意一个属性是可读写的。例如 domObj.style.top、domObj.style.wdith 等,在读的时候,它们返回的值常常是带有单位的(如 px )。

但是,对于这种方式,它只能够获取到该元素的行内样式,而并不能获取到该元素最终计算好的样式。如果想要获取计算好的样式,需要使用 *obj.currentstyle( IE )*和 getComputedStyle( IE 之外的浏览器 )

另一方面,由于 domObj.style.xxx 属性能够被赋值,所以 JavaScript 控制 DOM 元素运动的原理就是通过不断修改这些属性的值而达到其位置改变的,需要注意的是,给这些属性赋值的时候需要带单位的要带上单位,否则不生效。

# event 事件对象相关尺寸和位置属性

对于元素的运动的操作,通常还会涉及到事件里面的 event 对象,而 event 对象也存在很多位置属性,且由于浏览器兼容性问题会导致这些属性间相互混淆,这里也来进行一个总结。

# clientX 和 clientY 属性

这对属性是当事件发生时,鼠标点击位置相对于浏览器(可视区)的坐标,即浏览器左上角坐标的( 0 , 0 ),该属性以浏览器左上角坐标为原点,计算鼠标点击位置距离其左上角的位置,

不管浏览器窗口大小如何变化,都不会影响点击位置的坐标。

<div id="container" class="container"></div>
*{
  margin: 0;
  padding: 0;
}
.container {
  width: 200px;
  height: 200px;
  background-color: red;
  border: 1px solid;
  overflow: auto;
  padding: 10px;
  margin: 20px;
}
var container = document.getElementById("container");
container.onclick = function (ev) {
  var evt = ev || event;
  console.log(evt.clientX + ":" + evt.clientY);
}

在上面的代码中,我们为 container 盒子绑定了点击事件,获取该盒子的 clientX 以及 clientY 的值,接下来我们来进行点击测试:

image-20211105153614467

我们分别点击该盒子的最左上角和最右下角,打印出来的值分别是(20 , 20)和 (241 , 241),可以看出这确实是以浏览器左上角坐标的( 0 , 0 )为原点来进行计算的。

# screenX 和 screenY 属性

screenX 和 screenY 是事件发生时鼠标相对于屏幕的坐标,以设备屏幕的左上角为原点,事件发生时鼠标点击的地方即为该点的 screenX 和 screenY 值。例如:

var container = document.getElementById("container");
container.onclick = function (ev) {
  var evt = ev || event;
  console.log(evt.screenX + ":" + evt.screenY);
}
image-20211105154500066

我们将浏览器缩小,同样是点击 container 盒子的最左上角,打印出来的是(368 , 365),因为这是以屏幕最左上角为原点的。

# offsetX 和 offsetY 属性

这一对属性是指当事件发生时,鼠标点击位置相对于该事件源的位置,即点击该 DOM 元素,以该 DOM 左上角为原点来计算鼠标点击位置的坐标。例如:

var container = document.getElementById("container");
container.onclick = function (ev) {
  var evt = ev || event;
  console.log("offsetX:", evt.offsetX);
  console.log("offsetY:", evt.offsetY);
}

同样点击 container 元素的最左上角,打印出( 0, 0 )

# pageX 和 pageY 属性

顾名思义,该属性是事件发生时鼠标点击位置相对于页面的位置,通常浏览器窗口没有出现滚动条时,该属性和 clientX 及 clientY 是等价的。

例如:

var container = document.getElementById("container");
container.onclick = function (ev) {
  var evt = ev || event;
  console.log("pageX:", evt.pageX);
  console.log("pageY:", evt.pageY);
  console.log("clientX:", evt.clientX);
  console.log("clientY:", evt.clientY);
}

此时点击 container 盒子,得到的 pageX、pageY 的值和 clientX、clientY 完全相同。

但是当浏览器出现滚动条的时候,pageX 通常会大于 clientX,因为页面还存在被卷起来的部分的宽度和高度。

例如:

...
body{
  height: 5000px;
}
...

我们为 body 添加一个 height 为 500px,使其能够产生滚动效果,此时两组属性的区别就出来了。如下:

2021-11-05 16.06.06

-EOF-

eval
更多知识

← eval 更多知识→

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式