样式操作模块可用于管理DOM元素的样式、坐标和尺寸,本节讲解一下坐标这一块。

对于坐标来说,jQuery提供了一个offset方法用于获取第一个匹配元素的坐标或者设置所有匹配元素的坐标,还有offsetParent获取最近的定位祖先元素,position用于获取获取第一个匹配元素相对于最近定位祖先元素的坐标,如下:

  • offset(options)                 ;返回匹配元素集合中的一个元素的文档坐标,或者设置每个元素的文档坐标,;不能带单位,默认是px,有两种使用方法:

    • offset()                            ;返回第一个匹配元素的文档坐标
    • offset(val)                        ;设置每个匹配的元素的文档坐标
  • offsetParent(options)      ;获取最近的定位祖先元素
  • position()                        ;获取第一个匹配元素相对于最近定位祖先元素的坐标    ;如果是body元素则返回{ top: 0, left: 0 }。

也就是说如果不传递参数则获取第一个匹配元素的文档坐标,如果传递了参数(含有left、top的对象),则设置每个匹配元素的坐标.

举个栗子:

writer by:大沙漠 QQ:22969969

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
<style>
*{margin:0;padding:0;}
div{margin:20px;width: 200px;height: 180px;position: relative;padding-top: 20px;background: #c38;}
h1{margin:10px;color: #333;}
</style>
</head>
<body>
<div>
<h1>123</h1>
</div>
<button id="b1">获取h1元素的文档坐标</button><br/>
<button id="b2">获取h1元素最近的定位祖先元素</button><br/>
<button id="b3">获取h1元素离最近定位祖先元素的坐标</button><br/>
<button id="b4">设置h1元素的文档坐标</button>
<p></p>
<script>
$('#b1').click(()=>{ //获取h1元素的文档坐标
console.log( $('h1').offset() )
})
$('#b2').click(()=>{ //获取h1元素最近的定位祖先元素,也就是div元素
console.log( $('h1').offsetParent() )
})
$('#b3').click(()=>{ //获取h1元素离最近定位祖先元素的坐标,也就是相对div元素的坐标
console.log( $('h1').position() )
})
$('#b4').click(()=>{ //设置h1元素的文档坐标,相对于整个文档的
$('h1').offset({top:'10',left:'10'})
})
</script>
</body>
</html>

我们添加了一个div和一个h1,另外,div设置了relation属性,div内的h1相对于div设置了margin属性,另外定义了四个按钮,分别用于获取h1的文档坐标、获取h1最近的定位组件元素、获取h1元素离最近定位祖先元素的坐标和修改h1元素的坐标,效果如下:

点击按钮1获取的文档坐标是相对于整个文档的,按钮2获取的定位元素也就是div元素,按钮3获取的是相对于div的偏移坐标,按钮4设置h1的文档坐标,此时h1元素上会新增一个position:relative;属性,jQuery经过计算在h1上设置对应的偏移地址,如下:

源码分析


offset的实现是通过getBoundingClientRect整个原生API来实现的,如下:

if ( "getBoundingClientRect" in document.documentElement ) {    //原生方法getBoundingClientRect()返回元素的窗口坐标,返回值含有4个整型属性:top、left、right、bottom
jQuery.fn.offset = function( options ) {
var elem = this[0], box; //elem指向第一个匹配元素 if ( options ) { //如果传入了options参数
return this.each(function( i ) { //则遍历匹配元素集合
jQuery.offset.setOffset( this, options, i ); //并在每个元素上调用jQuery.offset.setOffset(elem,options,i)设置文档坐标。
});
} if ( !elem || !elem.ownerDocument ) { //如果没有匹配元素或匹配元素不在文档中,
return null; //则不做任何处理,立即返回null
} if ( elem === elem.ownerDocument.body ) { //如果elem是body元素
return jQuery.offset.bodyOffset( elem ); //则调用jQuery.offset.bodyOffset(body)返回body元素的文档坐标
} try {
box = elem.getBoundingClientRect(); //调用原生方法getBoundingClientRect()返回元素的窗口坐标,用try-catch语句来'吞掉'IE可能抛出的异常。
} catch(e) {} var doc = elem.ownerDocument, //指向document对象
docElem = doc.documentElement; //指向html元素 // Make sure we're not dealing with a disconnected DOM node
if ( !box || !jQuery.contains( docElem, elem ) ) {
return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
} var body = doc.body,
win = getWindow(doc), //调用getwindow(doc)获取window对象
clientTop = docElem.clientTop || body.clientTop || 0, //clientTop是html或body元素的上边框厚度
clientLeft = docElem.clientLeft || body.clientLeft || 0, //clientLeft是html或body元素的左边框厚度
scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, //滚动条的垂直偏移
scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, //滚动条的水平偏移
top = box.top + scrollTop - clientTop, //第一个元素的文档上坐标
left = box.left + scrollLeft - clientLeft; //第一个元素的文档左坐标 return { top: top, left: left }; //返回第一个元素的文档坐标
}; } else { //不支持原生方法getBoundingClientRect()时,现在大多数浏览器已经支持了,所以这里不讨论。
}

上面是获取文档坐标的,对于设置文档坐标是通过jQuery.offset.setOffset()来实现的,也就是上面标红的地方,jQuery.offset.setOffset的实现如下:

jQuery.offset = {
setOffset: function( elem, options, i ) { //设置单个元素的文档坐标。
var position = jQuery.css( elem, "position" ); // set position first, in-case top/left are set even on static elem
if ( position === "static" ) { //如果该元素的position属性等于static的
elem.style.position = "relative"; //则修正为relative,使得设置的样式left、top能够生效。
} var curElem = jQuery( elem ), //当前元素的jQuery对象
curOffset = curElem.offset(), //当前元素的文档坐标
curCSSTop = jQuery.css( elem, "top" ), //获取 当前元素的计算样式top,带有单位
curCSSLeft = jQuery.css( elem, "left" ), //获取 当前元素的计算样式left,带有单位
calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, //如果当前属性样式position是absolute或fixed,并且样式left或top是auto,则设置calculatePosition为true。
props = {}, curPosition = {}, curTop, curLeft; // need to be able to calculate position if eit her top or left is auto and position is either absolute or fixed
if ( calculatePosition ) { //如果calculatePosition为true,修正curTop和curLeft坐标。
curPosition = curElem.position(); //通过.position()获取当前元素相对于最近定位祖先元素或body元素的坐标
curTop = curPosition.top; //获取组件元素的top
curLeft = curPosition.left; //获取组件的left
} else { //将curCSSTop、curCSSLeft解析为数值,以便参与计算。
curTop = parseFloat( curCSSTop ) || 0;
curLeft = parseFloat( curCSSLeft ) || 0;
} if ( jQuery.isFunction( options ) ) { //如果options是函数
options = options.call( elem, i, curOffset ); //则执行该函数,取其返回值作为要设置的文档坐标。
} if ( options.top != null ) { //计算最终的内联样式top
props.top = ( options.top - curOffset.top ) + curTop;
}
if ( options.left != null ) { //计算最终的内联样式left
props.left = ( options.left - curOffset.left ) + curLeft;
} if ( "using" in options ) { //如果参数options中有回调函数using,则调用
options.using.call( elem, props );
} else {
curElem.css( props ); //否则调用.css(name,value)方法设置最终的内联样式top、left。
}
}
};

从源码里可以看到,如果元素有设置了absolute则会获取祖先元素的的偏移,然后经过一些运算获取最后的值,最后通过css()修改样式来实现最后的定位。

对于offsetParent来说,它的实现如下:

jQuery.fn.extend({
offsetParent: function() { //获取最近的定位祖先元素,就是CSS position属性被设置为relative、absolute 或 fixed 的元素,返回一个jQuery对象,包含所有祖先元素。
return this.map(function() {
var offsetParent = this.offsetParent || document.body; //offsetParent是最近的定位祖先元素;如果没有找到,则返回body元素。
while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { //如果找到的祖先元素的样式position是static,则继续沿着树向上找,直到遇到body元素或html元素为止。
offsetParent = offsetParent.offsetParent;
}
return offsetParent; //返回的定位组选元素将被添加到新构造的jQuery对象上。
});
}
})

offsetParent是原生的DOM操作的API,用于获取第一个祖定位元素,所有浏览器都支持的,这里就是把它进行了一个封装而已。

position()比较简单,通过offsetParent()获取第一个祖先节点的文档坐标,然后用组件节点的top减去当前节点的top,组件节点的left减去当前节点的left,返回一个相对位置,仅此而已,代码就不贴了。

jQuery 源码解析(二十七) 样式操作模块 坐标详解的更多相关文章

  1. jQuery 源码解析(二十三) DOM操作模块 替换元素 详解

    本节说一下DOM操作模块里的替换元素模块,该模块可将当前匹配的元素替换指定的DOM元素,有两个方法,如下: replaceWith(value)     ;使用提供的新内容来替换匹配元素集合中的每个元 ...

  2. jQuery 源码分析(十五) 数据操作模块 val详解

    jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue)        ;获取匹配元素集合中第一个元素的 ...

  3. jquery源码解析:addClass,toggleClass,hasClass详解

    这一课,我们将继续讲解jQuery对元素属性操作的方法. 首先,我们先看一下这几个方法是如何使用的: $("#div1").addClass("box1 box2&quo ...

  4. jQuery 源码分析(二十一) DOM操作模块 删除元素 详解

    本节说一下DOM操作模块里的删除元素模块,该模块用于删除DOM里的某个节点,也可以理解为将该节点从DOM树中卸载掉,如果该节点有绑定事件,我们可以选择保留或删除这些事件,删除元素的接口有如下三个: e ...

  5. jQuery 源码分析(二十) DOM操作模块 插入元素 详解

    jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...

  6. Spring源码解析二:IOC容器初始化过程详解

    IOC容器初始化分为三个步骤,分别是: 1.Resource定位,即BeanDefinition的资源定位. 2.BeanDefinition的载入 3.向IOC容器注册BeanDefinition ...

  7. jquery源码解析:val方法和valHooks对象详解

    这一课,我们将讲解val方法,以及对value属性的兼容性处理,jQuery中通过valHooks对象来处理. 首先,我们先来看下val方法的使用: $("#input1").va ...

  8. Vue.js 源码分析(二十七) 高级应用 异步组件 详解

    当我们的项目足够大,使用的组件就会很多,此时如果一次性加载所有的组件是比较花费时间的.一开始就把所有的组件都加载是没必要的一笔开销,此时可以用异步组件来优化一下. 异步组件简单的说就是只有等到在页面里 ...

  9. jquery源码解析:expando,holdReady,ready详解

    jQuery的工具方法,其实就是静态方法,源码里面就是通过extend方法,把这些工具方法添加给jQuery构造函数的. jQuery.extend({       //当只有一个对象时,就把这个对象 ...

随机推荐

  1. Python解释器安装及环境变量配置

    python官网www.python.org 1.python3安装 1.1到官网找到电脑相应系统下载(下载路径越简单越好) 2.找需要下载的python版本 3. 手动添加环境变量 右键我的电脑-- ...

  2. 一个简单的示例在spring boot中实现国际化

    最近在网上找了一个有关账单管理的spring boot项目,其中有一部分是涉及显示国际化信息的,即将页面上的中英文进行转换.因为在这之前这部分内容没有接触过,所以在这记录下过程. 中文效果图如下所示: ...

  3. 建议3:正确处理Javascript特殊值---(1)正确使用NaN和Infinity

    NaN时IEEE 754中定义的一个特殊的数量值.他不表示一个数字,尽管下面的表达式返回的是true typeof(NaN) === 'number' //true 该值可能会在试图将非数字形式的字符 ...

  4. hdu 6298 Maximum Multiple (简单数论)

    Maximum Multiple Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  5. C语言笔记 08_函数指针&回调函数&字符串&结构体&位域

    函数指针 函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: / ...

  6. Winform中实现拖拽文件到ListView获取文件类型(附代码下载)

    场景 效果 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建一个for ...

  7. DOM事件流的三个阶段

    事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流. DOM事件流分为三个阶段,分别为: 捕获阶段:事件从Document节点自上而下向目标节点传播的阶段: 目标阶段:真正的目标 ...

  8. springcloud配置中心

    SpringCloud Config简介 Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持 ...

  9. .NET Core 3.0正式版发布

    是的,.NET Core 3.0正式版发布了,令人兴奋. WPF 其实,.NET Core 2.1开始已经是个很完善的版本,3.0又带来了什么呢?我站在我的使用角度来看,最最令人振奋的就是:能用WPF ...

  10. ReactNative: 使用AppReistry注册类

    一.简介 每一个应用程序的运行都有一个入口文件或者入口函数,例如iOS中的使用UIApplicationMain类完成入口函数的实现,在React-Native中,AppRegistry类就肩负着这个 ...