最新版的 FineUIPro v5.2.0 中,咱們將內置的 jQuery v1.12.4 升級到 jQuery v3.3.1 ,能夠看升級記錄:javascript
+升級到jQuery v3.3.1。
-jQuery v3.x支持的瀏覽器:Chrome,Edge,Firefox,Safari,IE9+。
-增長類型JSLibrary枚舉值JQv1,用來引入jQuery v1.x。
-若是須要支持IE8,請在Web.config中增長配置項JSLibrary=JQv1。
-IE8有限支持而且複雜頁面可能會有性能問題,建議你們積極引導用戶使用現代瀏覽器。css
之因此作這個升級,主要考慮以下因素:html
1. IE8的市場份額逐漸萎縮,能夠考慮將IE8的支持放到次要位置java
2. 經過所有配置參數 JSLibrary=JQv1 引入老版本的 jQuery v1.12.4,這樣老客戶仍然能夠支持IE8jquery
3. 默認使用 jQuery v3.3.1,緊跟jQuery版本意味着較好的性能和安全性,以及遇到問題可以及時解決git
4. 考慮到 jQuery 這麼多年的發展,穩定性應該不是問題github
整個升級過程仍是很平緩的,沒有大的改動。api
惟一讓我頭疼的時,好像表格中節點的位置總計算不對,好比在表格的單元格編輯時,若是表格沒有滾動條,顯示的編輯框是正確的:瀏覽器
可是,表格的滾動條一出現,編輯框就錯位了:安全
而這個問題,在使用 jQuery v1.12.4 時是不存在的!
通過一段時間的調試,最終肯定這是最新版 jQuery v3.3.1 的一個BUG,出現這個問題需知足以下條件:
1. 引用最新版 jQuery v3.3.1
2. 計算表格 tr 或者 td 的相對位置時出錯,好比:$('table tr:eq(1) td(0)').position()
爲了更直觀的演示這個問題,我寫了個例子,其中HTML代碼塊:
<div class="container"> <table> <tr> <td>row-1</td> </tr> <tr> <td id="theTD">row-2</td> </tr> </table> </div> <div id="result"> </div>
CSS代碼塊:
.container { background-color: lightgreen; padding: 50px; position: relative; } table { width: 100%; border-collapse: collapse; border-spacing: 0; background-color: green; } table td { padding: 0; height: 50px; color: #fff; vertical-align: top; }
爲了更直觀的描述問題,咱們用不一樣的背景色標識外層的容器(.container)和內部的表格(table)。
JavaScript代碼塊:
$(function() { var tdPosition = $('#theTD').position(); $('#result').html('top:' + tdPosition.top + ' left:' + tdPosition.left); });
按照正常的思惟模式,上面的 theTD 的相對位置應該是相對 .container 的偏移量(由於 .container 是 td 的第一個遇到的相對定位的父元素)。
由於 .container 設置了 50px 的內邊距,表格每行的高度爲 50px,因此 theTD 距離 .container 的垂直高度應該是 100px。
因此咱們指望的輸出結果應該是:
top:100 left:50
可是結果然的如此嗎?我建立了兩個示例,分別是 引用 jQuery v1.9.1 的示例 和 引用 jQuery v3.3.1 的示例,獲得的結果以下所示:
可見,在 jQuery v3.3.1 中,獲取表格中 td 節點的相對位置(position)獲得的結果並非咱們想要的。那這個值究竟是什麼?
top: 50 left:0
通過認真分析,我認爲這個值是 td 相對外部 table 節點的偏移量,而不是相對於第一個浮動父節點的位置(position: relative / absolute)。
曾經一度我對 position() 和 offset() 的確切含義產生了懷疑,難道是我理解錯了?後來發現個人理解沒有問題:
.offsetParent() is supposed to return the nearest positioned element, where "positioned" means it has a css position attribute of "relative", "absolute", or "fixed".
我在不少地方依賴於 position 的計算解決,難道是 jQuery 計算 offsetParent 節點時出了差錯:
打開瀏覽器的調試窗口,我輸入以下代碼:
$('#theTD').offsetParent()
可見,經過 jQuery 獲取到的依然是 .container, 這個方法返回的沒問題。
沒辦法,既然出了問題,只好先在本身的代碼中修正了。
既然 td 的相對位置是相對於 table,那不如用 table 作箇中轉,計算出 table 的 position 加上去不就好了,以下所示:
$(function() { var tdPosition = $('#theTD').position(); var tablePosition = $('#theTD').parents('table').position(); $('#result').html('top:' + (tdPosition.top + tablePosition.top) + ' left:' + (tdPosition.left + tablePosition.left)); });
看着好像是正確的,後來測試發現遇到嵌套表格就不行了,以下示例:
<div class="container"> <table class="table-outer"> <tr> <td>table1-row1</td> </tr> <tr> <td> <table> <tr> <td>row-1</td> </tr> <tr> <td id="theTD">row-2</td> </tr> </table> </td> </tr> </table> </div> <div id="result"> </div>
.container { background-color: lightgreen; padding: 50px; position: relative; } .table-outer { background-color: blue; } table { width: 100%; border-collapse: collapse; border-spacing: 0; background-color: green; } table td { padding: 0; height: 50px; color: #fff; vertical-align: top; }
一計不成,再生一計。既然 table 的 position 出問題,那麼 div 的 position 應該是正常的吧。此次我就來在 td 裏面動態建立一個 div 節點怎麼樣?
$(function() { var tmpDiv = $('<div style="position:relative;"/>').prependTo($('#theTD')); var tdPosition = tmpDiv.position(); tmpDiv.remove(); $('#result').html('top:' + tdPosition.top + ' left:' + tdPosition.left); });
看着好像沒有問題,能夠問題依舊,當設置 td 的 vertical-align: middle 時,問題暴露出來了,由於此時 td 內的 div 是居中顯示的:
就在我束手無策時,我突然想到前面的 offsetParent() 函數,這個函數能獲取正確的父節點。既然 position() 函數有問題,我何不嘗試 offset() 函數。
1. jQuery.position(): 獲取元素相對於父元素的偏移量(position: relative / absolute)
2. jQuery.offset(): 獲取元素相對於視口的偏移量
$(function() { var tdOffset = $('#theTD').offset(); var tdParentOffset = $('#theTD').offsetParent().offset(); $('#result').html('top:' + (tdOffset.top - tdParentOffset.top) + ' left:' + (tdOffset.left - tdParentOffset.left)); });
此次使用當前元素和相對父元素的 offset 相減,獲得的結果是否咱們所須要的呢?
不錯,正是咱們所須要的。由於這個邏輯判斷很簡單,和是否表格嵌套不要緊,因此測試下第一次嘗試失敗的狀況:
Bingo! 一切正常。此問題圓滿解決!
想一想就難以想象,一個被全球用戶使用的公共JavaScript竟然有這樣的BUG,難道就沒人發現麼?
網上搜索了一圈,看起來我多慮了,早就有用戶提出這個問題:
https://github.com/jquery/api.jquery.com/issues/1081
Per the spec, an offsetParent is defined as an element with a position that is non-static or is a table, th, or td element. Since 3.3.0,
position
started using the nativeoffsetParent
property which started respected table, th, and td as offset parents, but the.offsetParent()
method was left unchanged. We'll fix this in an upcoming release, but we'll need to note the special behavior for those 3 elements in the docs.
這個是 jQuery 核心開發團隊給出的答案,大意是說 offsetParent 這個節點屬性指的是這樣一個父節點:
1. 節點是非靜態的,也就是擁有 position: relative / absolute / fixed 樣式
2. 節點是 table, th 或者 td
哈,這麼多年過去了,我竟然第一次據說 offsetParent 還有相對於 td,th,table這個說法,儘管這個td,th,table節點是 position:static,下面使用代碼來驗證一下:
$(function() { var tdPosition = $('#theTD').position(); var offsetParent = $('#theTD').offsetParent()[0].tagName; var nativeOffsetParent = $('#theTD')[0].offsetParent.tagName; $('#result').html('top:' + tdPosition.top + ' left:' + tdPosition.left + '<br>offsetParent():' + offsetParent + '<br>Native offsetParent:' + nativeOffsetParent); });
分別在不一樣瀏覽器中查看結果。
Chrome:
Firefox:
Edge:
IE11(因爲IE11打不開jsfiddle網站,咱們單首創建了一個頁面):
甚至IE9也是這樣的:
好吧,看來我真是孤陋而寡聞了。
jQuery 是在 v3.3.0 開始考慮到第二種狀況的,可是 jQuery.offsetParent() 的定義沒有改變。這種不一致性自己就是一個BUG。
而不和以前的 jQuery v1.x, v2.x 乃至於 v3.3.0 以前的版本兼容,對於一個普遍使用的公共庫而言,更應該算是一個BUG,或者至少提供兼容之間的方法。
考慮另外一個更現實的問題:在實際應用中,咱們爲何要獲取 td ,table 的 position() 呢?
不是爲了拿獲取到的值去更新其餘 td 或者 table 的 top/left 屬性!
而是爲了拿獲取的值去更新其餘相關 div 節點的 top/left 屬性,而 div 節點的 position() 是相對於 position: relative / absolute / fixed 父節點的,而非 td ,table 節點,這就必定會出問題。
因此,咱們仍是認定這是一個BUG。
可是 jQuery 官方貌似沒有修正這個問題的意思,而是可能在未來版本修改 jQuery.offsetParent() 的定義:
可是jQuery你這樣作真的好麼,都10多年了你都沒遵照標準,忽然來個小版本號稱支持標準,搞的和以前的代碼不兼容,你讓以前的代碼情何以堪!
無論怎樣,咱們有了本身的解決辦法。
這篇文章描述了咱們 FineUIPro 產品 更新中遇到的一個問題,最終將問題定位到 jQuery.position() 函數,雖然jQuery的作法是依照HTML規範來的,可是 jQuery.offsetParent() 和 jQuery.position() 兩個函數有衝突,而且會致使以前的jQuery插件出錯,應該算是一個BUG吧。
好在本文給出了一個補救的方法,若是這裏的方法可以對你有所啓發或者幫助,就給個推薦唄。