昨天我發了一篇文章【抓個Firefox的小辮子,圍觀羣衆有:Chrome、Edge、IE8-11】,提到了一個Firefox不少版本都存在的問題,而相同的測試頁面在Chrome、Edge、IE8-11下面一切正常。javascript
在評論裏面,網友 @Blackheart 的回覆引發了個人注意:css
我就按照網友提供的方法從新測試了一下:html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="https://code.jquery.com/jquery-1.11.3.js"></script> <style> *, :after, :before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } </style> </head> <body> <fieldset id="fieldset1" style="border:solid 1px red;width:500px;position:absolute;top:0;left:0;"> <legend>fieldset</legend> </fieldset> <script> $(function () { // $('#fieldset1').height(200); document.getElementById("fieldset1").style.height = "200px"; alert(parseInt($('#fieldset1').height(), 10)); }); </script> </body> </html>
在Firefox下顯示效果:java
在Chrome下顯示效果:jquery
截圖中提示 181px,而不是 200px,這是徹底正確的:web
這二者的差別體如今最終設置的fieldset的高度不一樣:函數
$('#fieldset1').height(200);
document.getElementById("fieldset1").style.height = "200px";
固然,這兩種設置高度的差別不是咱們本篇文章討論的重點。但倒是問題的關鍵所在,下面分析jQuery源代碼時會用到這個知識。 測試
問題好像真的消失了,有那麼一刻,我差點就要將Firefox的問題丟給 jQuery 了,但事實果然如此嘛?ui
俗話說:不入虎穴,焉得虎子,咱們就來調試一把 jQuery.height 函數。3d
雖然用了十來年的jQuery,但真正去調試 jQuery 代碼卻沒有幾回,畢竟 jQuery 的代碼可謂是千錘百煉,吹毛求疵想找個BUG都難。
此次就沒辦法了,既然懷疑到這裏只好入手了:
1. 入口函數
2. 首先進入 style 函數:
3. 進入 set 函數
此時若是咱們來執行一下 augmentWidthOrHeight:
獲得的是一個負值,經過前面對 jQuery.height 的介紹咱們知道,jQuery.height(200)是不包含padding和border的,最終仍是要經過 $('#fieldset1')[0].style.height 來設置,因此咱們須要先計算 fieldset 上下的padding和border。
4. 進入 augmentWidthOrHeight 函數:
這個截圖能夠印證咱們以前的設想,經過 jQuery.css 函數來獲取 paddingTop,borderTopWidth,paddingBottom,borderBottomWidth 四個值。
5. 最後回到 style 函數:
此時 value 值已是計算後的值了,其中包含用戶要設置的 200px,以及上下padding和border 17.6px,總計 217.6px
下面經過 style[ name ] = value,將 217.6px 設置到 fieldset 節點:
此時,咱們來仔細觀察下三個變量,說白了,這句話的意思下面的代碼是如出一轍的:
document.getElementById("fieldset1").style.height = "217.6px";
那就奇怪了,既然 jQuery.height 最終也是調用的 style.height,爲啥直接用 style.height 設置就沒問題呢?
最初,我懷疑是 try-catch 搞的鬼,後來發現不是。按照 jQuery.height 的調用流程,咱們本身動手來重現一下:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="https://code.jquery.com/jquery-1.11.3.js"></script> <style> *, :after, :before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } </style> </head> <body> <fieldset id="fieldset1" style="border:solid 1px red;width:500px;position:absolute;top:0;left:0;"> <legend>fieldset</legend> </fieldset> <script> $(function () { var elem = $('#fieldset1')[0]; var view = elem.ownerDocument.defaultView; if (!view || !view.opener) { view = window; } var styles = view.getComputedStyle(elem); var val = 0; val -= jQuery.css(elem, "paddingTop", true, styles); val -= jQuery.css(elem, "borderTopWidth", true, styles); val -= jQuery.css(elem, "paddingBottom", true, styles); val -= jQuery.css(elem, "borderBottomWidth", true, styles); elem.style.height = (200 - val) + "px"; alert(parseInt($('#fieldset1').height(), 10)); }); </script> </body> </html>
在Firefox下能夠重現問題:
簡化下代碼:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="https://code.jquery.com/jquery-1.11.3.js"></script> <style> *, :after, :before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } </style> </head> <body> <fieldset id="fieldset1" style="border:solid 1px red;width:500px;position:absolute;top:0;left:0;"> <legend>fieldset</legend> </fieldset> <script> $(function () { var elem = $('#fieldset1')[0]; var styles = window.getComputedStyle(elem); var val= jQuery.css(elem, "paddingTop", true, styles); elem.style.height = (200 - val) + "px"; alert(parseInt($('#fieldset1').height(), 10)); }); </script> </body> </html>
Firefox下問題依然存在,很明顯是在 jQuery.css 中出現的問題,跟蹤到 jQuery 的源代碼:
說白了,也很簡單。經過 styles.getPropertyValue(name) || styles[name] 來獲取某個CSS樣式的值。
下面,再次更新示例,去除 jQuery 的相關代碼:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="https://code.jquery.com/jquery-1.11.3.js"></script> <style> *, :after, :before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } </style> </head> <body> <fieldset id="fieldset1" style="border:solid 1px red;width:500px;position:absolute;top:0;left:0;"> <legend>fieldset</legend> </fieldset> <script> $(function () { var elem = $('#fieldset1')[0]; var styles = window.getComputedStyle(elem); var val = parseFloat(styles.getPropertyValue("paddingTop") || styles["paddingTop"]); elem.style.height = (200 - val) + "px"; alert(parseInt($('#fieldset1').height(), 10)); }); </script> </body> </html>
Firefox問題依然存在,而這裏面咱們設置 fieldset 的高度根本沒用到 jQuery,只是調用了系統的 getCompotedStyle 方法等!!!
中間插播一個廣告:
#######################################
# 9 年只作一件事
# 由三生石上親自打造的 FineUI 控件庫,現已支持 ASP.NET Core 2.0,跨平臺 Windows、Mac、Linux 都能用!
# 在線示例:http://core.fineui.com/
########################################
廣告結束,請繼續....
上面還不是最簡單,下面咱們從頁面中徹底排除 jQuery ,3 行代碼就能重現問題:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style> *, :after, :before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } </style> </head> <body> <fieldset id="fieldset1" style="border:solid 1px red;width:500px;position:absolute;top:0;left:0;"> <legend>fieldset</legend> </fieldset> <script> window.onload = function() { var elem = document.getElementById('fieldset1'); window.getComputedStyle(elem)["paddingTop"]; elem.style.height = "200px"; }; </script> </body> </html>
在Chrome和Firefox下的顯示效果對比(後面是Chrome,前面是Firefox):
的的確確,這是 Firefox Quantum(v57) 以及不少老版本的BUG,和 jQuery 沒有關係,jQuery只在作了該作的事情,碰巧遇到這個 Firefox 的BUG而已。
這個BUG在Firefox下重現須要知足以下幾個條件:
在這 3 個條件下,調用JS代碼 window.getComputedStyle(elem)["paddingTop"] 以後再設置 fieldset 標籤的高度,頁面上不會更新!
解決辦法請看個人上一篇文章:【原創】抓個Firefox的小辮子,圍觀羣衆有:Chrome、Edge、IE8-11
喜歡三石的文章,你就給個推薦唄!