博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery源码解析之width()
阅读量:6412 次
发布时间:2019-06-23

本文共 16781 字,大约阅读时间需要 55 分钟。

一、在讲之前,先弄清 boxSizing 属性

(1)box-sizing 是默认值 "content-box"

 
这是divTwo
复制代码

$().width()的值是 55

(2)box-sizing 是 "border-box"

这是divTwo 复制代码

$().width()的值是 53

因为 border-box 是包括 border、padding、content 的,而 content-box 只包括 content。

可想而知,jQuery的$().width() 中也包含了对 borderBox 的判断。

  • 注意下div标签的默认值

二、$().width()

作用:
获取目标元素的宽度

源码:

  //源码7033行   //$.each(obj,callback(index,item){})   jQuery.each( [ "height", "width" ], function( i, dimension ) {
    //i:0 dimension:height     //i:1 dimension:width     //cssHooks是用来定义style方法的     jQuery.cssHooks[ dimension ] = {
      //读       //$().width()       //参数:elem:目标DOM元素/computed:true/extra:"content"       get: function( elem, computed, extra ) {
        console.log(elem, computed, extra,'extra7040')         if ( computed ) {
          // 某些元素是有尺寸的信息的,如果我们隐式地显示它们,前提是它必须有一个display值           // Certain elements can have dimension info if we invisibly show them           // but it must have a current display style that would benefit           // 上面这句话的意思是,某个元素用display:none,将它从页面上去掉了,此时是获取不到它的宽度的           // 如果要获取它的宽度的话,需要隐式地显示它们,比如display:absolute,visible:hidden           // 然后再去获取它的宽度           // block:false           // none:true           // rdisplayswap的作用是检测 none和table开头的           return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&           // 兼容性的考虑,直接看 getWidthOrHeight           // Support: Safari 8+           // Table columns in Safari have non-zero offsetWidth & zero           // getBoundingClientRect().width unless display is changed.           // Support: IE <=11 only           // Running getBoundingClientRect on a disconnected node           // in IE throws an error.           // display为none的话,elem.getBoundingClientRect().width=0           // elem.getClientRects() 返回CSS边框的集合           // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getClientRects           ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?             swap( elem, cssShow, function() {
              return getWidthOrHeight( elem, dimension, extra );             } ) :             //$().width()情况             //dimension:width/extra:"content"             getWidthOrHeight( elem, dimension, extra );         }       },    }; } ); 复制代码

解析:

(1)box-sizing 是默认值,并且 display 不为 none
rdisplayswap
作用:
检测目标元素的display属性的值 是否为none或以table开头

    // 检测 display 的值是否为 none 或以 table 开头     // Swappable if display is none or starts with table     // 除了 "table", "table-cell", "table-caption"     // except "table", "table-cell", or "table-caption"     // display 的值,请访问 https://developer.mozilla.org/en-US/docs/CSS/display     // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display     // 源码6698行 var rdisplayswap = /^(none|table(?!-c[ea]).+)/, 复制代码

如果displaynone的话,就会调用swap()方法,反之,就直接调用getWidthOrHeight()方法

getWidthOrHeight()

作用:
获取widthheight的值

 //获取 width 或 height   //dimension:width/extra:"content"   //源码6823行   function getWidthOrHeight( elem, dimension, extra ) {
    // Start with computed style     var styles = getStyles( elem ),       val = curCSS( elem, dimension, styles ),       //判断 box-sizing 的值是否 是 border-box       //如果启用了 box-sizing,js 的 width 是会算上 margin、border、padding的       //如果不启用的话,js 的 width 只会算 content       //jQuery 的 width 自始至终都是算的 content       isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",       valueIsBorderBox = isBorderBox;     //火狐兼容性处理,可不看     // Support: Firefox <=54     // Return a confounding non-pixel value or feign ignorance, as appropriate.     if ( rnumnonpx.test( val ) ) {
      if ( !extra ) {
        return val;       }       val = "auto";     }     // 通过getComputedStyle检查style属性,并返回可靠的style属性,这样可以防止浏览器返回不可靠的值     // Check for style in case a browser which returns unreliable values     // for getComputedStyle silently falls back to the reliable elem.style     valueIsBorderBox = valueIsBorderBox &&       ( support.boxSizingReliable() || val === elem.style[ dimension ] );     console.log(valueIsBorderBox,'valueIsBorderBox6853')     // Fall back to offsetWidth/offsetHeight when value is "auto"     // This happens for inline elements with no explicit setting (gh-3571)     // Support: Android <=4.1 - 4.3 only     // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)     if ( val === "auto" ||       !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) {
      val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];       console.log(val,'val6862')       // offsetWidth/offsetHeight provide border-box values       valueIsBorderBox = true;     }     // Normalize "" and auto     // 55px     val = parseFloat( val ) || 0;     console.log(val,extra,'val6869')     // Adjust for the element's box model     return ( val +       boxModelAdjustment(         //DOM节点         elem,         //width         dimension,         //content         extra || ( isBorderBox ? "border" : "content" ),         //true/false         valueIsBorderBox,         //styles         styles,         //55         // Provide the current computed size to request scroll gutter calculation (gh-3589)         val       )     ) + "px";   } 复制代码

getWidthOrHeight() 里面有好多方法,我们一一来解析:

getStyles( elem )

作用:
获取该 DOM 元素的所有 css 属性的值

  //获取该DOM元素的所有css属性的值   //源码6501行   var getStyles = function( elem ) {
    // 兼容性处理,旨在拿到正确的view     // Support: IE <=11 only, Firefox <=30 (#15098, #14150)     // IE throws on elements created in popups     // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"     var view = elem.ownerDocument.defaultView;     if ( !view || !view.opener ) {
      view = window;     }     //获取所有CSS属性的值     return view.getComputedStyle( elem );   }; 复制代码

可以看到,本质是调用了getComputedStyle()方法。

curCSS( elem, dimension, styles )

作用:
获取元素的当前属性的值

// 获取元素的当前属性的值   // elem, "position"   // elem,width,styles   // 源码6609行   function curCSS( elem, name, computed ) {
    var width, minWidth, maxWidth, ret,       // Support: Firefox 51+       // Retrieving style before computed somehow       // fixes an issue with getting wrong values       // on detached elements       style = elem.style;     //获取elem所有的样式属性     computed = computed || getStyles( elem );     // console.log(computed,'computed6621')     // getPropertyValue is needed for:     //   .css('filter') (IE 9 only, #12537)     //   .css('--customProperty) (#3144)     if ( computed ) {
      //返回元素的属性的当前值       //position:static       //top:0px       //left:0px       ret = computed.getPropertyValue( name ) || computed[ name ];       console.log(ret,'ret6627')       //如果目标属性值为空并且目标元素不在目标元素所在的文档内(感觉这种情况好奇怪)       if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
        //使用jQuery.style方法来获取目标元素的属性值         ret = jQuery.style( elem, name );       }       // A tribute to the "awesome hack by Dean Edwards"       // Android Browser returns percentage for some values,       // but width seems to be reliably pixels.       // This is against the CSSOM draft spec:       // https://drafts.csswg.org/cssom/#resolved-values       //当属性设置成数值时,安卓浏览器会返回一些百分比,但是宽度是像素显示的       //这违反了CSSOM草案规范       //所以以下方法是修复不规范的width属性的       if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
        // Remember the original values         width = style.width;         minWidth = style.minWidth;         maxWidth = style.maxWidth;         // Put in the new values to get a computed value out         style.minWidth = style.maxWidth = style.width = ret;         ret = computed.width;         // Revert the changed values         style.width = width;         style.minWidth = minWidth;         style.maxWidth = maxWidth;       }     }     return ret !== undefined ?       // 兼容性,IE下返回的zIndex的值是数字,       // 而使用jQuery获取的属性都是返回字符串       // Support: IE <=9 - 11 only       // IE returns zIndex value as an integer.       ret + "" :       ret;   } 复制代码

可以看到,curCSS本质是调用了computed.getPropertyValue( name )方法,也就是说我们可以这样去获取目标元素的属性值:

let a=document.getElementById("pTwo") a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width') //55px 复制代码

目标元素的所属 view,调用getComputedStyle()方法,获取目标元素的所有 CSS 属性,再调用getPropertyValue('width'),获取目标width的属性值,为 55px

注意:无论box-sizing的值是border-box还是content-box,上面的方法获取的width值都是55px,这是不符合 CSS3 盒子模型的,所以 jQuery 拿到该值后,还会继续处理。

boxModelAdjustment

因为这里讨论的是情况一,所以boxModelAdjustment()会直接返回 0

综上:当box-sizing 是默认值,并且 display 不为 none时,返回的width是:

parseFloat(a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width')) //55 复制代码

(2)box-sizing 值为 border-box

这是divTwo 复制代码
$("#pTwo").width() //51 document.getElementById("pTwo").style.width //55px 复制代码

可以看到,原生 js 获取 width 是不遵循 CSS3 盒子规范的。

borderBox 的判断在getWidthOrHeight()方法中,直接看过去:

  //获取 width 或 height   //dimension:width/extra:"content"   //源码6823行   function getWidthOrHeight( elem, dimension, extra ) {
    xxx     ...     var styles = getStyles( elem ),       //true       isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",       //true       valueIsBorderBox = isBorderBox;     xxx     ...     valueIsBorderBox = valueIsBorderBox &&       //val值是通过a.ownerDocument.defaultView.getComputedStyle(a).getPropertyValue('width')得出的       //但又通过js原生的style.width来取值并与val相比较       ( support.boxSizingReliable() || val === elem.style[ dimension ] );     console.log(val === elem.style[ dimension ],'valueIsBorderBox6853')     // 55     val = parseFloat( val ) || 0;     // Adjust for the element's box model     return ( val +       //borderBox走这里       boxModelAdjustment(         //DOM节点         elem,         //width         dimension,         //content         extra || ( isBorderBox ? "border" : "content" ),         //true/false         valueIsBorderBox,         //styles         styles,         //55         // Provide the current computed size to request scroll gutter calculation (gh-3589)         val       )     ) + "px";   } 复制代码

boxModelAdjustment():

作用:
集中处理borderBox的情况

//参数说明:   //elem:DOM节点/dimension:width/box:content/isBorderBox:true/false/styles:styles/computedVal:55   //源码6758行   function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
    var i = dimension === "width" ? 1 : 0,       extra = 0,       delta = 0;     // 如果 boxSizing 的属性值,而不是 borderBox 的话,就直接返回 0     // Adjustment may not be necessary     if ( box === ( isBorderBox ? "border" : "content" ) ) {
      console.log('content1111','content6768')       return 0;     }     //小技巧     //i 的初始值是 0/1     //然后 cssExpand = [ "Top", "Right", "Bottom", "Left" ]     for ( ; i < 4; i += 2 ) {
      // Both box models exclude margin       if ( box === "margin" ) {
        //var cssExpand = [ "Top", "Right", "Bottom", "Left" ];         //width 的话,就是 marginRight/marginLeft         //height 的话,就是 marginTop/marginBottom         //jQuery.css( elem, box + cssExpand[ i ], true, styles ) 的意思就是         //返回 marginRight/marginLeft/marginTop/marginBottom 的数字,并给 delta 加上         delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );       }       // If we get here with a content-box, we're seeking "padding" or "border" or "margin"       // 如果不是 borderBox 的话       if ( !isBorderBox ) {
        // Add padding         // 添加 padding-xxx         delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );         // For "border" or "margin", add border         if ( box !== "padding" ) {
          delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );           // But still keep track of it otherwise         } else {
          extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );         }         // If we get here with a border-box (content + padding + border), we're seeking "content" or         // "padding" or "margin"       } else {
        // 去掉 padding         // For "content", subtract padding         if ( box === "content" ) {
          //width,去掉paddingLeft,paddingRight的值           delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );         }         // For "content" or "padding", subtract border         // 去掉 borderXXXWidth         if ( box !== "margin" ) {
          //width,去掉borderLeftWidth,borderRightWidth的值           delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );         }       }     }     // Account for positive content-box scroll gutter when requested by providing computedVal     if ( !isBorderBox && computedVal >= 0 ) {
      // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border       // Assuming integer scroll gutter, subtract the rest and round down       delta += Math.max( 0, Math.ceil(         //就是将dimension的首字母做个大写         elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -         computedVal -         delta -         extra -         0.5       ) );     }     return delta;   } 复制代码

可以看到,isBorderBox 为 true 的话,会执行下面两段代码:

if ( box === "content" ) {
   //width,去掉paddingLeft,paddingRight的值    delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } 复制代码
 if ( box !== "margin" ) {
    //width,去掉borderLeftWidth,borderRightWidth的值     delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } 复制代码

去除了paddingLeftpaddingRightborderLeftWidthborderRightWidth,并最终返回值

二、$().width(xxx)

作用:
设置目标元素的宽度

源码:

  //源码7033行   //$.each(obj,callback(index,item){})   jQuery.each( [ "height", "width" ], function( i, dimension ) {
    //i:0 dimension:height     //i:1 dimension:width     //cssHooks是用来定义style方法的     jQuery.cssHooks[ dimension ] = {
      //写       //$().width(55)       //elem:DOM节点,value:55,extra:content       set: function( elem, value, extra ) {
        var matches,           styles = getStyles( elem ),           isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",           //-4           subtract = extra && boxModelAdjustment(             elem,             dimension,             extra,             isBorderBox,             styles           );         // 如果是 borderBox 的话,通过 offset 计算的尺寸是不准的,         // 所以要假设成 content-box 来获取 border 和 padding         // Account for unreliable border-box dimensions by comparing offset* to computed and         // faking a content-box to get border and padding (gh-3699)         //true true 'static'         //调整 subtract         if ( isBorderBox && support.scrollboxSize() === styles.position ) {
          subtract -= Math.ceil(             elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -             parseFloat( styles[ dimension ] ) -             boxModelAdjustment( elem, dimension, "border", false, styles ) -             0.5           );           console.log(subtract,'subtract7169')         }         // 如果需要进行值调整,则转换为像素         // Convert to pixels if value adjustment is needed         //如果是 borderBox 并且 value 的单位不是 px,则会转换成像素         if ( subtract && ( matches = rcssNum.exec( value ) ) &&           ( matches[ 3 ] || "px" ) !== "px" ) {
          elem.style[ dimension ] = value;           value = jQuery.css( elem, dimension );         }         //59px         return setPositiveNumber( elem, value, subtract );       }   }; } ); 复制代码

解析:

(1)整体上看,实际上两个 if ,最后再 return 一个setPositiveNumber()方法

(2)注意subtract,如果有 borderBox 属性,并且 borderWidth、padding 有值的话,subtract 一般为负数,比如下面的例子,subtract = -4

  这是divTwo $("#pTwo").width(55) 复制代码

反之则会是 0

(3)两个 if 我试了下,都会去执行,所以直接看的setPositiveNumber ()

setPositiveNumber:

作用:
设置真正的 width 值

  function setPositiveNumber( elem, value, subtract ) {
    // 标准化相对值     // Any relative (+/-) values have already been     // normalized at this point     //[     // "55px",     // undefined,     // "55",     // "px",     // index: 0,     // input: "55px",     // groups: undefined,     // index: 0     // input: "55px"     // ]     var matches = rcssNum.exec( value );     console.log(matches,( subtract || 0 ),'matches6760')     return matches ?       //(0,55-(-4))+'px'       //Math.max(a,b) 返回两个指定的数中带有较大的值的那个数       // Guard against undefined "subtract", e.g., when used as in cssHooks       Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :       value;   } 复制代码

如果是 borderBox,width 会设置成 59px(虽然表面上开发者设置的是$("#pTwo").width(55)),反之,则是 55px

总结:

1、$().width()
(1)不是borderBox
$().width()=parseFloat(elem.ownerDocument.defaultView.getComputedStyle(elem).getPropertyValue('width'))

(2)是borderBox()

在(1)的基础上执行boxModelAdjustment()方法,去除 borderWidth、padding

2、$().width(xxx)

(1)不是borderBox
width=xxx
(2)是borderBox
width=xxx+ setPositiveNumber()


(完)

转载地址:http://xzura.baihongyu.com/

你可能感兴趣的文章
【Vue】vue.js常用指令
查看>>
NFS学习
查看>>
MySql常用命令总结
查看>>
又一年...
查看>>
文件上传框的美化+预览+ajax
查看>>
Linux VFS
查看>>
ext不能选中复制属性_如何实现Extjs的grid单元格只让选择(即可以复制单元格内容)但是不让修改?...
查看>>
python中print的作用*8、不能+8_在 Python 3.x 中语句 print(*[1,2,3]) 不能正确执行。 (1.0分)_学小易找答案...
查看>>
python 生成html代码_使用Python Markdown 生成 html
查看>>
axure如何导出原件_Axure 教程:轻松导出图标字体所有图标
查看>>
laravel input值必须不等于0_框架不提供,动手造一个:Laravel表单验证自定义用法...
查看>>
cad填充图案乱理石_太快了吧!原来大神是这样用CAD图案填充的
查看>>
activator.createinstance 需要垃圾回收么_在垃圾回收器中有哪几种判断是否需要被回收的方法...
查看>>
rocketmq 消息指定_RocketMQ入坑系列(一)角色介绍及基本使用
查看>>
redis zset转set 反序列化失败_掌握好Redis的数据类型,面试心里有底了
查看>>
p图软件pⅰc_娱乐圈最塑料的夫妻,P图永远只P自己,太精彩了吧!
查看>>
怎么判断冠词用a还是an_葡语干货 | 葡萄牙语冠词用法整理大全
查看>>
js传参不是数字_JS的Reflect学习和应用
查看>>
三个不等_数学一轮复习05,从函数观点看方程与不等式,记住口诀与联系
查看>>
卡尺测量的最小范围_汽车维修工具-测量用具
查看>>