当前位置: 首页 > Web前端 > CSS

从一个“bug”到鲜为人知的jQuery.cssHooks

时间:2023-03-30 23:11:52 CSS

写在开头。这一次,我将分享分析jQuery赋值样式失败结果背后原因的过程。在翻阅jQuery源码的过程中,感觉自己真的不能说只是会用jQuery。我什至似乎无法使用它。'#'+id).css({"left":"200"})我只想控制一个左边的值。每个人都明白它,但它失败了。可以看到左边是“”;其实一开始我也没有想到可能是jQuery本身造成的。我首先考虑的是我的元素当前是否被赋值了?JS问题?等等……想了半天,我觉得可能是我自己的写法问题。于是进行了下面的实验:$('#'+id).css({"left":200})好像是字符串和数字的区别!omg,没想过字符串和数字的效果会不一致……你以为事情就这么结束了?不行,看下面的:$('#'+id).css({"width":"200"})嗯,为什么,把width设置成字符串就好了如果加上px后缀,left是不允许??现在我们可以总结一下,通过jQuery.fn.css方法设置元素属性时存在一些不一致的地方,以width和left为例(因为属性很多,不一致的情况很多,明白原理就好):left可以完成px通过数字类型完成样式设置,字符串类型不能设置属性宽度,两者都可以通过数字或字符串类型完成设置属性,可以从一开始就抛出奇怪的现象底层问题:为什么通过jQuery.fn.css方法设置样式时string类型的值对某些属性不生效?从源码中寻找线索jQuery的源码相对于react和vue来说应该是非常直白的,就是一个js。(不过还是看不懂?先介绍一个未压缩的jQuery,保留了所有注释和代码结构,非常方便大家阅读https://cdn.bootcss.com/jquery/3.3.1/jquery。js首先找到我们设置样式的方法jQuery.fn.css:jQuery.fn.extend({css:function(name,value){returnaccess(this,function(elem,name,value){varstyles,len,map={},i=0;if(Array.isArray(name)){styles=getStyles(elem);len=name.length;for(;i1);}});如何通过浏览器调试源码?(因为直接看源码太麻烦了,debug的形式可以看到每一个调用栈)我们可以通过console.log的形式,在这个源码里写控制台,然后在控制台就可以看到对应源码的调用了:进入jQuery.style后,就会来到最后区别的地方:style:function(elem,name,value,extra){...hooks=jQuery.cssHooks[名称]||jQuery.cssHooks[origName];如果(值!==未定义){类型=类型值;if(type==="string"&&(ret=rcssNum.exec(value))&&ret[1]){value=adjustCSS(elem,name,ret);类型=“数字”;}...if(type==="number"){value+=ret&&ret[3]||(jQuery.cssNumber[origName]?"":"px");}...if(!hooks||!("set"inhooks)||(value=hooks.set(elem,value,extra)!==undefined){//此时的值是200还是200px;只有加上后缀才赋值成功if(isCustomProp){style.setProperty(name,价值);}else{样式[名称]=值;}}}...},在源码中可以看到,传入的值确实区分了string和number;而不是我之前想的,string应该和number类似:)如果传入number类型,会在上面加上一个px后缀;但是这还是不能解释为什么left和width都传入了string但是结果却不一样。重点是这句话:hooks=jQuery.cssHooks[name]||jQuery.cssHooks[origName];...if(!hooks||!("set"inhooks)||(value=hooks.set(elem,value,extra))!==undefined){...}当value为string类型时,在最后赋值之前,会经过value=hooks.set(elem,value,extra))!==undefined的判断,也就是说hooks.set方法是否存在,我们还有机会用这个方法完成string类型的值的后缀补全。而这个hooks是通过jQuery.cssHooks获取的,那么jQuery.cssHooks是什么:从源码可以看出,cssHooks包含了一些属性的方法,其中剩下的只有get;宽度已获取和设置。结合上面的判断条件可以推导出,由于width有set方法,所以方法中对于string类型的值完成了后缀,而left却没有,从而形成了“神奇”的现象在文章的开头。cssHooks直接向jQuery添加挂钩,以覆盖设置或获取特定CSS属性的方法,以便标准化CSS属性名称或创建自定义属性。$.cssHooks对象提供了一种通过定义函数来获取或设置特定CSS值的方法。它可用于为标准CSS3功能(例如框阴影和渐变)创建新的cssHooks。例如,一些基于Webkit的浏览器使用-webkit-border-radius来设置对象的border-radius,而早期版本的Firefox使用-moz-border-radius。cssHook可以将这些不同的写法标准化,使得.css()可以使用统一标准化的属性名(border-radius或者对应的DOM属性写法borderRadius)。除了对特定样式的处理提供更细粒度的控制外,$.cssHooks还扩展了.animate()方法的属性集。简单的说,jQuery向我们暴露了一个钩子,我们可以定义set等方法来实现某个属性的特定行为。所以left和width的问题就是有没有sethook的方法。所以。.我们还有最后一个问题:为什么width会在其上设置一个钩子函数?答案可以从它的set方法中看出:“边框框”,subtract=extra&&boxModelAdjustment(elem,dimension,extra,isBorderBox,styles);//通过比较offset*和computed来解决不可靠的边框尺寸和//伪造内容框以获得边框和填充(gh-3699)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,样式)-0.5);}//如果需要调整值,则转换为像素if(subtract&&a议员;(matches=rcssNum.exec(value))&&(matches[3]||"px")!=="px"){elem.style[dimension]=value;value=jQuery.css(元素,维度);}returnsetPositiveNumber(elem,value,subtract);}从这个钩子函数我们可以看出,对width的特殊处理是因为有几个css盒模型,content-box|border-box|inherit分别代表“不包括padding,边框、边距"|“包括边框和填充”|“遗产”;所以,为了统一外部调用,隐藏这些背后的判断,增加了set方法,把里面的px补全了。同时不需要兼容left,所以没有set方法。总结虽然cssHooks不常用(反正我没用过,现在规范格式的方法很多,cssHookshooks感觉有点复杂),但是这次页面上的一个小问题引发了思考和过程试图深入挖掘值得总结。虽然我们不是制造轮子的人,但了解别人的轮子胜过“会用”;更何况,看了cssHooks,感觉连jQuery都不会用了:)参考文章jQuery源码分析jQuery中文文档上次约定po作者博客不定期更新中~如有问题,请在问题下交流。