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} 

 

posted on 2015-04-18 16:15  叫我钱了个浅  阅读(1073)  评论(3编辑  收藏  举报

导航