摘要:干想了半天,認為可能還是本身的寫法問題。對象提供了一種通過定義函數來獲取或設置特定值的方法。簡單來說,給我們暴露了一個鉤子,我們可以自己定義方法比如,來實現針對某個屬性的特定行為。
寫在最前
本次分享一下在一次jQuery賦值樣式失效的結果中來分析背后原因的過程。在翻jQuery源碼的過程中,感覺真是還不能說自己只是會用jQuery,我好像連會用都達不到(逃
歡迎關注我的博客,不定期更新中——
一個很簡單的賦值問題$("#" + id).css({"left": "200"})
我只是單純的想控制一個left值,大家都懂,但是竟然失敗了,打印出的元素屬性中可以看到left為"";我其實一開始沒想到可能是jQuery本身的原因導致的,我先考慮的是我這個元素是不是當前要賦值的?js的問題?等等。。干想了半天,認為可能還是本身的寫法問題。所以進行了如下實驗:
$("#" + id).css({"left": 200})
看起來是字符串和數字的區別!omg,從來沒想過字符串和數字的效果竟然會不一致。。你以為事情已經結束了?no,看下面這個:
$("#" + id).css({"width": "200"})
好的為什么,width設定字符串就可以被添加px后綴,left就不可以??
現在我們可以總結一下通過jQuery.fn.css方法來設定元素屬性的時候會有一些不一致的情況,以width和left為例子(因為屬性很多,不一致的情況很多,了解原理即可):
left通過number類型可以補全px完成樣式設定,string類型無法設定屬性
width均可以通過number或string類型完成設定屬性
從而可以拋出由一開始的奇怪現象的底層問題:為什么通過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 ) { return access( this, function( elem, name, value ) { var styles, len, map = {}, i = 0; if ( Array.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); } } );
如何通過瀏覽器來調試源碼呢?(因為直接看源碼太繁瑣了,通過debug的形式可以看到每次的調用棧)我們可以通過console.log的形式,在這段源碼中將console寫入,之后在控制臺中就可以看到對應源碼的調用:
進入jQuery.style之后就會來到最終產生區別的地方:
style: function( elem, name, value, extra ) { ... hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; if ( value !== undefined ) { type = typeof value; if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { value = adjustCSS( elem, name, ret ); type = "number"; } ... if ( type === "number" ) { value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } ... if ( !hooks || !( "set" in hooks ) ||( value = hooks.set( elem, value, extra ) ) !== undefined ) { //此時的value到底是200還是200px;只有添加了后綴才能賦值成功 if ( isCustomProp ) { style.setProperty( name, value ); } else { style[ name ] = value; } } } ... },
源碼中可以看到在傳入的value中確實對string和number做了區分;而不是我之前所認為的,string應該和number差不多:)如果傳入number類型,便會為其添加px后綴;但是這仍然沒有解釋為什么left和width均傳入string而結果不同的問題。重點在于這句話:
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; ... if ( !hooks || !( "set" in hooks ) || ( value = hooks.set( elem, value, extra ) ) !== undefined ) { ... }
在value是string類型,到最終賦值之前,還會經過value = hooks.set( elem, value, extra ) ) !== undefined的判斷,也就是說如果hooks.set方法存在,我們還有一次通過這個方法來將string類型的value進行后綴補全的機會。而這個hooks是由jQuery.cssHooks得到的,那么jQuery.cssHooks是什么:
從源碼中可以看出,cssHooks中包含了屬性的一些方法,其中left只有get;width有get和set。再結合上面的判斷條件就可以推斷出,由于width存在了set方法,在其方法中對string類型的value完成了后綴的補齊,而left則不行從而形成了文中一開始的“神奇”現象。
cssHooks直接向 jQuery 中添加鉤子,用于覆蓋設置或獲取特定 CSS 屬性時的方法,目的是為了標準化 CSS 屬性名或創建自定義屬性。
$.cssHooks 對象提供了一種通過定義函數來獲取或設置特定 CSS 值的方法。可以用它來創建新的 cssHooks 用于標準化 CSS3 功能,例如,盒子陰影(box shadows)及漸變(gradients)。例如,某些基于 Webkit 的瀏覽器會使用 -webkit-border-radius 來設置對象的 border-radius,然而,早先版本的 Firefox 則使用 -moz-border-radius。cssHook 就可以將這些不同的寫法進行標準化,從而讓 .css() 可以使用統一的標準化屬性名(border-radius 或對應的 DOM 屬性寫法 borderRadius)。
該方法除了提供了對特定樣式的處理可以采用更加細致的控制外,$.cssHooks 同時還擴展了 .animate() 方法上的屬性集。
簡單來說,jQuery給我們暴露了一個鉤子,我們可以自己定義方法比如set,來實現針對某個屬性的特定行為。所以出現left和width的問題就是有沒有set這個鉤子方法。so。。我們還剩最后一個問題:
為什么width要對其設定鉤子函數?
答案可以從其set方法來窺探一下:
set: function( elem, value, extra ) { var matches, styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", subtract = extra && boxModelAdjustment( elem, dimension, extra, isBorderBox, styles ); // Account for unreliable border-box dimensions by comparing offset* to computed and // faking a content-box to get border and padding (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, styles ) - 0.5 ); } // Convert to pixels if value adjustment is needed if ( subtract && ( matches = rcssNum.exec( value ) ) && ( matches[ 3 ] || "px" ) !== "px" ) { elem.style[ dimension ] = value; value = jQuery.css( elem, dimension ); } return setPositiveNumber( elem, value, subtract ); }
從這個鉤子函數中我們可以看出,要對width做特殊處理是因為css的盒模型有好幾種,content-box|border-box|inherit分別代表“不包括padding、border、margin” | “包含border和padding” | “繼承”;故為了統一外界的調用,隱藏這些背后的判斷,從而增加了這個set方法。順帶著在其中把px補全了。同時left這種沒什么需要兼容的故沒有設定set方法。
小結雖然cssHooks不常用(我反正從來沒用過,現在對于標準化格式有很多其他的方法來做,cssHooks的鉤子感覺還是有些復雜了),但這次通過頁面上一個很小的問題從而引發思考并且試圖深挖一些的過程還是值得總結下來的。雖然我們不是造輪子的人,但理解別人的輪子也是比“會用”好一些的;更何況看了cssHooks我感覺我都不會用jQuery:)
參考文章jQuery源碼解析
jQuer中文文檔
最后慣例po作者的博客,不定時更新中——
有問題歡迎在issues下交流。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/116389.html
摘要:干想了半天,認為可能還是本身的寫法問題。對象提供了一種通過定義函數來獲取或設置特定值的方法。簡單來說,給我們暴露了一個鉤子,我們可以自己定義方法比如,來實現針對某個屬性的特定行為。 寫在最前 本次分享一下在一次jQuery賦值樣式失效的結果中來分析背后原因的過程。在翻jQuery源碼的過程中,感覺真是還不能說自己只是會用jQuery,我好像連會用都達不到(逃 歡迎關注我的博客,不定期更...
摘要:一在講之前,先弄清屬性是默認值這是的值是是這是的值是因為是包括的,而只包括。可想而知,的中也包含了對的判斷。 showImg(https://segmentfault.com/img/remote/1460000019169187); 一、在講之前,先弄清 boxSizing 屬性(1)box-sizing 是默認值 content-box 這是divTwo $(#pTwo)...
摘要:本文已完結,請看下文求索的動畫快于嗎為何續本文源自對問題動畫性能優于的原理是什么的回答。是這樣的嗎請看下文求索的動畫快于嗎為何續 本文已完結,請看下文: > 求索:GSAP的動畫快于jQuery嗎?為何?/續 本文源自對問題《GSAP js動畫性能優于jQuery的原理是什么?》的回答。GSAP是一個js動畫插件,它聲稱20x faster than jQuery,是什么讓...
閱讀 1670·2021-10-13 09:39
閱讀 2099·2021-09-07 10:20
閱讀 2678·2019-08-30 15:56
閱讀 2945·2019-08-30 15:56
閱讀 932·2019-08-30 15:55
閱讀 625·2019-08-30 15:46
閱讀 3494·2019-08-30 15:44
閱讀 2552·2019-08-30 11:15