zoukankan      html  css  js  c++  java
  • jQuery offset()源码解析

    首先是原型上的offset方法,根据arguments判断到底是取值还是设值。如果是设置,就遍历调用静态方法jQuery.offset.setOffset

    如果是取值。那么就是从"var docElem,win"这里开始了。

    jQuery.fn.offset = function( options ) {
        if ( arguments.length ) {//设置元素坐标
            return options === undefined ?//如果传入的参数是undefined,直接返回
                this :
                this.each(function( i ) {//遍历然后调用jQuery.offset.setOffset
                    jQuery.offset.setOffset( this, options, i );
                });
        }
    
        var docElem, win,
            elem = this[ 0 ],
            box = { top: 0, left: 0 },
            doc = elem && elem.ownerDocument;//当前文档
    
        if ( !doc ) {
            return;
        }
    
        docElem = doc.documentElement;
    
        // Make sure it's not a disconnected DOM node
        if ( !jQuery.contains( docElem, elem ) ) {//如果元素没有被documentElement包含,就返回 0 0
            return box;
        }
    
        // If we don't have gBCR, just use 0,0 rather than error
        // BlackBerry 5, iOS 3 (original iPhone)
        if ( typeof elem.getBoundingClientRect !== core_strundefined ) {//如果元素有getBoundingClientRect方法
            box = elem.getBoundingClientRect();//调用该方法
        }
    
        win = getWindow( doc );//如果是window就返回window,如果是document,返回document.defaultView
        return {
            //元素相对于视窗的距离+滚动距离-边框宽度
            top: box.top + win.pageYOffset - docElem.clientTop,
            left: box.left + win.pageXOffset - docElem.clientLeft
        };
    };

     取值中,它考虑了元素没有被documentElement包含的情况,没有渲染,自然是没有offset的,于是它很神奇的返回了{"top":0,"left":0}。

    因为看的是jQuery 2.x的版本,这里它用到了getBoundingClientRect方法,从名字就可以看出,它返回的是相对于视窗的距离,这不是我们需要的offset,jQuery.offset返回的是相对于文档的距离,所以还要做进一步的计算:

        return {
            //元素相对于视窗的距离+滚动距离-边框宽度
            top: box.top + win.pageYOffset - docElem.clientTop,
            left: box.left + win.pageXOffset - docElem.clientLeft
        };

    关于getWindow,看看函数声明:

    function getWindow( elem ) {
    return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
    }

    如果elem是window,直接返回window,如果是document,则返回document.defaultView。

    不是很理解为什么要使用document.defaultView,群里的小伙伴说可能是多frame的情况

    然后我在stackoverflow上找到的帖子 http://stackoverflow.com/questions/9183555/whats-the-point-of-document-defaultview 也有类似的答案,然后另一种可能是为了修复Firefox 3.6中getComputedStyle的bug(⊙o⊙)…

    接下来是静态方法设置offset。。。

    jQuery.offset = {
    
        setOffset: function( elem, options, i ) {
            var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
                position = jQuery.css( elem, "position" ),//获取当前元素的position属性
                curElem = jQuery( elem ),//缓存当前元素
                props = {};
    
            // Set position first, in-case top/left are set even on static elem
            if ( position === "static" ) {//如果原元素是静态定位的,改成相对定位
                elem.style.position = "relative";
            }
    
            curOffset = curElem.offset();//获取当前元素的offset
            curCSSTop = jQuery.css( elem, "top" );
            curCSSLeft = jQuery.css( elem, "left" );
            //如果元素的position为absolute或者fixed,而且其top和left属性中至少有一项的值是auto,calculatePosition为true
            calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
    
            // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
            if ( calculatePosition ) {
                curPosition = curElem.position();//获取元素相对于父元素的偏移位置
                curTop = curPosition.top;
                curLeft = curPosition.left;
    
            } else {//如果calculatePosition为false,元素相对定位或者原本就有top、left值
                curTop = parseFloat( curCSSTop ) || 0;
                curLeft = parseFloat( curCSSLeft ) || 0;
            }
    
            if ( jQuery.isFunction( options ) ) {//如果options是函数,遍历调用
                options = options.call( elem, i, curOffset );
            }
            //计算props值,因为offset是相对文档定位,新的css的top/left值
            //需要根据原来的offset和css的top/left值计算
            if ( options.top != null ) {
                props.top = ( options.top - curOffset.top ) + curTop;
            }
            if ( options.left != null ) {
                props.left = ( options.left - curOffset.left ) + curLeft;
            }
            //如果有using这个参数,可以调用using方法
            if ( "using" in options ) {
                options.using.call( elem, props );
    
            } else {//如果没有,设置css偏移。
                curElem.css( props );
            }
        }
    };

    关于using这个选项,之前在jQuery API中没有看到过。发现隐藏技能了(⊙o⊙)…??

    好吧,刚在百度上试了下

    $('#page').offset({"top":1000,"left":100,"using":function(prop){console.log(prop)}});
    //Object {top: -302, left: 100} 
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 交换Easy
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法训练 矩阵乘方
    QT中给各控件增加背景图片(可缩放可旋转)的几种方法
    回调函数实现类似QT中信号机制
    std::string的Copy-on-Write:不如想象中美好(VC不使用这种方式,而使用对小字符串更友好的SSO实现)
  • 原文地址:https://www.cnblogs.com/qianlegeqian/p/4437589.html
Copyright © 2011-2022 走看看