一篇文章让你彻底搞懂js中的位置计算

目录
  • 引言
  • scroll
    • Element.scroll()
    • Element.scrollHeight/scrollWidth
    • Element.scrollLeft/scrollTop
    • 判断当前元素是否存在滚动条
    • 判断用户是否滚动到底部
  • client
    • MouseEvent.clientX/Y
    • Element.clientHeight/clientWidth
    • Element.clientTop/clientLeft
  • offset
    • MouseEvent.offsetX/offsetY
    • offsetWidth/offsetHeight
    • offsetTop/left
    • 计算元素距离 body 的偏移量
  • Element.getBoundingClientRect
    • 用法讲解
    • 计算元素是否出现在视口内
  • window.getComputedStyle
    • 用法讲解
  • 总结

引言

文章中涉及到的api列表:

  1. scroll相关Api
  2. client相关Api
  3. offset相关Api
  4. Element.getBoundingClientRectAPi
  5. Window.getComputedStyleApi

我们会结合api定义,知名开源库中的应用场景来逐层分析这些api。足以应对工作中关于元素位置计算的大部分场景。

注意在使用位置计算api时要格外的小心,不合理的使用他们可能会造成布局抖动Layout Thrashing影响页面渲染。

scroll

首先我们先来看看scroll相关的属性和方法。

Element.scroll()

Element.scroll()方法是用于在给定的元素中滚动到某个特定坐标的Element 接口。

element.scroll(x-coord, y-coord)
element.scroll(options)
  • x-coord 是指在元素左上方区域横轴方向上想要显示的像素。
  • y-coord 是指在元素左上方区域纵轴方向上想要显示的像素。

也就是element.scroll(x,y)会将元素滚动条位置滚动到对应x,y的位置。

同时也支持element.scroll(options)方式调用,支持传入额外的配置:

{
    left: number,
    top: number,
    behavior: 'smooth' | 'auto' // 平滑滚动还是默认直接滚动
}

Element.scrollHeight/scrollWidth

Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。

scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。 没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值clientHeight相同。包括元素的padding,但不包括元素的border和margin。scrollHeight也包括 ::before 和 ::after这样的伪元素。

换句话说Element.scrollHeight在元素不存在滚动条的情况下是恒等于clientHeight的。
但是如果出现了滚动条的话scrollHeight指的是包含元素不可以见内容的高度,出现滚动条的情况下是scrollHeight恒大于clientHeight。

  • Element.scrollWidth 这也是一个元素内容宽度的只读属性,包含由于溢出导致视图中不可以见的内容。

原理上和scrollHeight是同理的,只不过这里是宽度而非高度。

简单来说一个元素如果不存在滚动条,那么他们的scroll和client都是相等的值。如果存在了滚动条,client只会计算出当前元素展示出来的高度/宽度,而scroll不仅仅会计算当前元素展示出的,还会包含当前元素的滚动条隐藏内容的高度/宽度。

clientWidth/height + [滚动条被隐藏内容宽度/高度] = scrollWidth/Height

Element.scrollLeft/scrollTop

  • Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数.
  • Element.scrollLeft 属性可以读取或设置元素滚动条到元素左边的距离.

需要额外注意的是: 注意如果这个元素的内容排列方向(direction) 是rtl (right-to-left) ,那么滚动条会位于最右侧(内容开始处),并且scrollLeft值为0。此时,当你从右到左拖动滚动条时,scrollLeft会从0变为负数。

scrollLeft/Top在日常工作中是比较频繁使用关于操作滚动条的相关api,他们是一个可以设置的值。根据不同的值对应可以控制滚动条的位置。

其实这两个属性和上方的Element.scroll()可以达到相同的效果。

在实际工作中如果对于滚动操作有很频繁的需求,个人建议去使用better-scroll,它是一个移动/web端的通用js滚动库,内部是基于元素transform去操作的滚动并不会触发相关重塑/回流。

判断当前元素是否存在滚动条

出现滚动条便意味着元素空间将大于其内容显示区域,根据这个现象便可以得到判断是否出现滚动条的规则。

export const hasScrolled = (element, direction) => {
  if (!element || element.nodeType !== 1) return;
  if (direction === "vertical") {
    return element.scrollHeight > element.clientHeight;
  } else if (direction === "horizontal") {
    return element.scrollWidth > element.clientWidth;
  }
};

判断用户是否滚动到底部

本质上就是当元素出现滚动条时,判断当前元素出现的高度 + 滚动条高度 = 元素本身的高度(包含隐藏部分)。

element.scrollHeight - element.scrollTop === element.clientHeight

client

MouseEvent.clientX/Y

MounseEvent.clientX/Y同样也是只读属性,它提供事件发生时的应用客户端区域的水平坐标。

例如,不论页面是否有垂直/水平滚动,当你点击客户端区域的左上角时,鼠标事件的 clientX/Y 值都将为 0 。

其实MouseEvent.clientX/Y也就是相对于当前视口(浏览器可视区)进行位置计算。

转载一张非常直白的图:

Element.clientHeight/clientWidth

Element.clientWidth/clinetHeight 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。

内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。

在不出现滚动条时候Element.clientWidth/Height === Element.scrollWidth/Height

Element.clientTop/clientLeft

Element.clientLeft表示一个元素的左边框的宽度,以像素表示。如果元素的文本方向是从右向左(RTL, right-to-left),并且由于内容溢出导致左边出现了一个垂直滚动条,则该属性包括滚动条的宽度。clientLeft 不包括左外边距和左内边距。clientLeft 是只读的。

同样的Element.clientTop表示元素上边框的宽度,也是一个只读属性。

这两个属性日常使用会比较少,但是也应该了解以避免搞混这些看似名称都类似的属性。

offset

MouseEvent.offsetX/offsetY

MouseEvent接口的只读属性 offsetX/Y 规定了事件对象与目标节点的内填充边(padding edge)在 X/Y 轴方向上的偏移量。

相信使用过offest的同学对这个属性深有体会,它是相对于父元素的左边/上方的偏移量。

注意是触发元素也就是 e.target,额外小心如果事件对象中存在从一个子元素当移动到子元素内部时,e.offsetX/Y 此时相对于子元素的左上角偏移量。

offsetWidth/offsetHeight

HTMLElement.offsetWidth/Height 是一个只读属性,返回一个元素的布局宽度/高度。

所谓的布局宽度也就是相对于我们上边说到的clientHeight/Width,offsetHeight/Width,他们都是不包含border以及滚动条的宽/高(如果存在的话)。

而offsetWidth/offsetHeight返回元素的布局宽度/高度,包含元素的边框(border)、水平线/垂直线上的内边距(padding)、竖直/水平方向滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值。

offsetTop/left

HTMLElement.offsetLeft 是一个只读属性,返回当前元素左上角相对于 HTMLElement.offsetParent 节点的左边界偏移的像素值。

注意返回的是相对于 HTMLElement.offsetParent 节点左边边界的偏移量。

何为HTMLElement.offsetParent?

HTMLElement.offsetParent 是一个只读属性,返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 table,td,th,body 元素。当元素的 style.display 设置为 "none" 时,offsetParent 返回 null。offsetParent 很有用,因为 offsetTop 和 offsetLeft 都是相对于其内边距边界的。  -- MDN

讲讲人话,当前元素的祖先组件节点如果不存在任何 table,td,th 以及 position 属性为 relative,absolute 等为定位元素时,offsetLeft/offsetTop 返回的是距离 body 左/上角的偏移量。

当祖先元素中有定位元素(或者上述标签元素)时,它就可以被称为元素的offsetParent。元素的 offsetLeft/offsetTop 的值等于它的左边框左侧/顶边框顶部到它的 offsetParent 元素左边框的距离。

我们来看看这张图:

计算元素距离 body 的偏移量

当我们需要获得元素距离 body 的距离时,但是又无法确定父元素是否存在定位元素时(大多数时候在组件开发中,并不清楚父节点是否存在定位)。此时需要实现类似 jqery 的 offset()方法:获得当前元素对于 body 的偏移量。

  • 无法直接使用 offsetLeft/offsetTop 获取,因为并不确定父元素是否存在定位元素。
  • 使用递归解决,累加偏移量 offset,当前 offsetParent 不为 body 时。
  • 继续递归向上超着 offsetParent 累加 offset,直到遇到 body 元素停止。
const getOffsetSize = function(Node: any, offset?: any): any {
  if (!offset) {
    offset = {
      x: 0,
      y: 0
    };
  }
  if (Node === document.body) return offset;
  offset.x = offset.x + Node.offsetLeft;
  offset.y = offset.y + Node.offsetTop;
  return getOffsetSize(Node.offsetParent, offset);
};

注意:这里不可以使用 parentNode 上文已经讲过 offsetLeft/top 针对的是 HTMLElement.offsetParent 的偏移量而非 parentNode 的偏移量。

Element.getBoundingClientRect

用法讲解

Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。

element.getBoundingClientRect()返回的相对于视口左上角的位置。

element.getBoundingClientRect()返回的 height 和 width 是针对元素可见区域的宽和高(具体尺寸根据 box-sizing 决定),并不包含滚动条被隐藏的内容。

TIP: 如果是标准盒子模型,元素的尺寸等于 width/height + padding + border-width 的总和。如果 box-sizing: border-box,元素的的尺寸等于 width/height。

rectObject = object.getBoundingClientRect();

返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合,就是该元素的 CSS 边框大小。返回的结果是包含完整元素的最小矩形,并且拥有 left, top, right, bottom, x, y, width, 和 height 这几个以像素为单位的只读属性用于描述整个边框。除了 width 和 height 以外的属性是相对于视图窗口的左上角来计算的。

width和height是计算元素的大小,其他属性都是相对于视口左上角来说的。

当计算边界矩形时,会考虑视口区域(或其他可滚动元素)内的滚动操作,也就是说,当滚动位置发生了改变,top 和 left 属性值就会随之立即发生变化(因此,它们的值是相对于视口的,而不是绝对的) 。如果你需要获得相对于整个网页左上角定位的属性值,那么只要给 top、left 属性值加上当前的滚动位置(通过 window.scrollX 和 window.scrollY),这样就可以获取与当前的滚动位置无关的值。

计算元素是否出现在视口内

利用的还是元素距离视口的位置小于视口的大小。

注意即便变成了负值,那么也表示元素曾经出现过在屏幕中只是现在不显示了而已。(就比如滑动过)

vue-lazy图片懒加载库源码就是这么判断的。

 isInView (): boolean {
    const rect = this.el.getBoundingClientRect()
    return rect.top < window.innerHeight && rect.left < window.innerWidth
  }

如果rect.top < window.innerHeight表示当前元素已经已经出现在(过)页面中,left同理。

window.getComputedStyle

用法讲解

Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值。 私有的CSS属性值可以通过对象提供的API或通过简单地使用CSS属性名称进行索引来访问。

let style = window.getComputedStyle(element, [pseudoElt]);

element

  • 用于获取计算样式的Element。

pseudoElt 可选

  • 指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null)。

返回的style是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。

总结

到此这篇关于js中位置计算的文章就介绍到这了,更多相关js中的位置计算内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • js获取鼠标位置实例详解

    本文实例讲述了js获取鼠标位置的方法.分享给大家供大家参考,具体如下: 用 javascript 获取当前页面上鼠标(光标)位置在许多情况下都会用到,比如拖放,悬停提示(tooltip) 等等.当然,这里我们依然要面对浏览器的兼容问题,在不同的浏览器下,对这些相关的属性处理方式也不同,这里详细介绍了浏览器在处理这些属性时的差异和最终的解决方法. Javascript代码如下: <script type="text/javascript"> // 说明:获取鼠标位置 // 整

  • JS中获取 DOM 元素的绝对位置实例详解

    在操作页面滚动和动画时经常会获取 DOM 元素的绝对位置,例如 本文 左侧的悬浮导航,当页面滚动到它以前会正常地渲染到文档流中,当页面滚动超过了它的位置,就会始终悬浮在左侧. 本文会详述各种获取 DOM 元素绝对位置 的方法以及对应的兼容性.关于如何获取 DOM 元素高度和滚动高度,请参考视口的宽高与滚动高度 一文. 概述 这些是本文涉及的 API 对应的文档和标准,供查阅: API 用途 文档 标准 offsetTop 相对定位容器的位置 MDN CSSOM View Module clien

  • js获取元素在浏览器中的绝对位置

    JavaScript中提供获取HTML元素位置的属性: HTMLElement.offsetLeft HTMLElement.offsetHeight 但 是需要注意的是,这两个属性所储存的数值并不是该元素相对整个浏览器画布的绝对位置,而是相对于其父元素位置的相对位置,也就是说这两个数值得到的是以其 父元素左上角为(0,0)点从而计算出的数值.那么如何得到一个HTML元素的绝对位置呢,可以用以下函数: 复制代码 代码如下: //获取元素的纵坐标 function getTop(e){ var o

  • JS控制弹出新页面窗口位置和大小的方法

    本文实例讲述了JS控制弹出新页面窗口位置和大小的方法.分享给大家供大家参考.具体如下: 相信很多朋友都想做一个弹出用来弹出公告或者重要信息,但是弹出的框口位置和大小又不能太大,所以我们今天使用JS来控制弹出窗口的位置和大小,想弹多大就多大 复制代码 代码如下: <html> <head> <title>指定弹出窗口位置(IE)</title> <script language="javascript"> <!-- fun

  • 使用JS获取当前地理位置方法汇总

    今年的项目开发中,初步接触了移动端WEB开发,也就边学习HTML5边开发,主要使用了JQuery Mobile技术,发现这个不适合做互联网产品,大部分样式都需要重写,只用了部分功能.手机端WEB开发过程中第一次接触了定位功能,通过各大搜索引擎发现手机端定位都是通过浏览器的定位,而用在PC端浏览器第一次会弹出提示"是否开启定位功能",Boss看到这个提示,却觉得对用户的体验效果不好,不好那我换一种方式实现不就好了,这又不是多大的事,而Boss的脸色就大变,就说:不应该有这样的体验.我们这

  • JS获取当前地理位置的方法

    本文实例为大家分享了JS获取当前地理位置方法的具体代码,供大家参考,具体内容如下 1.手机定位 var getLocation = function (successFunc, errorFunc) { //successFunc获取定位成功回调函数,errorFunc获取定位失败回调 //首先设置默认城市 var defCity = { id: '000001', name: '北京市', date: curDateTime()//获取当前时间方法 }; //默认城市 $.cookie('VP

  • js获取元素相对窗口位置的实现代码

    JS获取元素的offsetTop,offsetLeft等属性 obj.clientWidth //获取元素的宽度 obj.clientHeight //元素的高度 obj.offsetLeft //元素相对于父元素的left obj.offsetTop //元素相对于父元素的top obj.offsetWidth //元素的宽度 obj.offsetHeight //元素的高度 区别: clientWidth = width + padding clientHeight = height + p

  • js实现获取鼠标当前的位置

    有时候,我们需要得到窗口拖动或者鼠标移动的距离,此时可以通过计算鼠标前后在页面中的位置来得到想要的结果,下面介绍几个事件属性: 1.客户区坐标位置 鼠标事件都是在浏览器视口中的特定位置上发生的.这个位置信息保存在事件对象的 clientX 和 clientY 属性中.它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标(不包括页面滚动的距离).如下图所示: var div = document.getElementById("myDiv"); //获取元素 EventUtil.on(

  • js与jquery中获取当前鼠标的x、y坐标位置的代码

    复制代码 代码如下: <div id="testDiv">放在我上面</div> <script type="text/javascript"> $('#testDiv').mousemove(function(e) { var xx = e.originalEvent.x || e.originalEvent.layerX || 0; var yy = e.originalEvent.y || e.originalEvent.l

  • js实现滚动条滚动到某个位置便自动定位某个tr

    要实现带滚动条的table,定位到某个tr,其实是很简单的,只有几行js代码就可以完成,具体内容如下 js代码 <strong><script type="text/javascript"> function test(){ var $objTr = $("#location"); //找到要定位的地方 tr $objTr.css("background-color","lightgray"); //设

随机推荐