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} 
  • 相关阅读:
    获取父类参数类型工具类
    date工具类
    Ascii工具类
    AES加解密工具类
    请求ip获取工具类
    对象和map互相转换工具类
    HTTP中get、post请求工具类
    时间日期各种工具类
    算法练习题——两数相除
    ETag
  • 原文地址:https://www.cnblogs.com/qianlegeqian/p/4437589.html
Copyright © 2011-2022 走看看