欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery?v3.3.1的BUG以及解決辦法(附解決方案)

 更新時(shí)間:2023年03月12日 23:50:31   作者:三生石上  
這篇文章描述了我們?FineUIPro?產(chǎn)品?更新中遇到的一個(gè)問(wèn)題,最終將問(wèn)題定位到?jQuery.position()?函數(shù),雖然jQuery的做法是依照HTML規(guī)范來(lái)的,但是?jQuery.offsetParent()?和?jQuery.position()?兩個(gè)函數(shù)有沖突,并且會(huì)導(dǎo)致之前的jQuery插件出錯(cuò),應(yīng)該算是一個(gè)BUG吧

發(fā)現(xiàn)問(wèn)題

最新版的 FineUIPro v5.2.0 中,我們將內(nèi)置的 jQuery v1.12.4 升級(jí)到 jQuery v3.3.1 ,可以看升級(jí)記錄:

+升級(jí)到j(luò)Query v3.3.1。
    -jQuery v3.x支持的瀏覽器:Chrome,Edge,F(xiàn)irefox,Safari,IE9+。
    -增加類型JSLibrary枚舉值JQv1,用來(lái)引入jQuery v1.x。
    -如果需要支持IE8,請(qǐng)?jiān)赪eb.config中增加配置項(xiàng)JSLibrary=JQv1。
    -IE8有限支持并且復(fù)雜頁(yè)面可能會(huì)有性能問(wèn)題,建議大家積極引導(dǎo)用戶使用現(xiàn)代瀏覽器。

之所以做這個(gè)升級(jí),主要考慮如下因素:

1. IE8的市場(chǎng)份額逐漸萎縮,可以考慮將IE8的支持放到次要位置

2. 通過(guò)全部配置參數(shù) JSLibrary=JQv1 引入老版本的 jQuery v1.12.4,這樣老客戶仍然可以支持IE8

3. 默認(rèn)使用 jQuery v3.3.1,緊跟jQuery版本意味著較好的性能和安全性,以及遇到問(wèn)題能夠及時(shí)解決

4. 考慮到 jQuery 這么多年的發(fā)展,穩(wěn)定性應(yīng)該不是問(wèn)題

整個(gè)升級(jí)過(guò)程還是很平緩的,沒(méi)有大的改動(dòng)。

唯一讓我頭疼的時(shí),好像表格中節(jié)點(diǎn)的位置總計(jì)算不對(duì),比如在表格的單元格編輯時(shí),如果表格沒(méi)有滾動(dòng)條,顯示的編輯框是正確的:

但是,表格的滾動(dòng)條一出現(xiàn),編輯框就錯(cuò)位了:

而這個(gè)問(wèn)題,在使用 jQuery v1.12.4 時(shí)是不存在的!

分析問(wèn)題

經(jīng)過(guò)一段時(shí)間的調(diào)試,最終確定這是最新版 jQuery v3.3.1 的一個(gè)BUG,出現(xiàn)這個(gè)問(wèn)題需滿足如下條件:

1. 引用最新版 jQuery v3.3.1

2. 計(jì)算表格 tr 或者 td 的相對(duì)位置時(shí)出錯(cuò),比如:$('table tr:eq(1) td(0)').position()

為了更直觀的演示這個(gè)問(wèn)題,我寫(xiě)了個(gè)例子,其中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;
}

為了更直觀的描述問(wèn)題,我們用不同的背景色標(biāo)識(shí)外層的容器(.container)和內(nèi)部的表格(table)。

JavaScript代碼塊:

$(function() {
    var tdPosition = $('#theTD').position();
    $('#result').html('top:' + tdPosition.top + ' left:' + tdPosition.left);
});

按照正常的思維模式,上面的 theTD 的相對(duì)位置應(yīng)該是相對(duì) .container 的偏移量(因?yàn)?.container 是 td 的第一個(gè)遇到的相對(duì)定位的父元素)。 

因?yàn)?.container 設(shè)置了 50px 的內(nèi)邊距,表格每行的高度為 50px,所以 theTD 距離 .container 的垂直高度應(yīng)該是 100px。

所以我們期望的輸出結(jié)果應(yīng)該是:

top:100 left:50

但是結(jié)果真的如此嗎?我創(chuàng)建了兩個(gè)示例,分別是 引用 jQuery v1.9.1 的示例引用 jQuery v3.3.1 的示例,得到的結(jié)果如下所示:

可見(jiàn),在 jQuery v3.3.1 中,獲取表格中 td 節(jié)點(diǎn)的相對(duì)位置(position)得到的結(jié)果并不是我們想要的。那這個(gè)值到底是什么?

top: 50 left:0

解決問(wèn)題

經(jīng)過(guò)認(rèn)真分析,我認(rèn)為這個(gè)值是 td 相對(duì)外部 table 節(jié)點(diǎn)的偏移量,而不是相對(duì)于第一個(gè)浮動(dòng)父節(jié)點(diǎn)的位置(position: relative / absolute)。

曾經(jīng)一度我對(duì) position() 和 offset() 的確切含義產(chǎn)生了懷疑,難道是我理解錯(cuò)了?后來(lái)發(fā)現(xiàn)我的理解沒(méi)有問(wèn)題:

.offsetParent() is supposed to return the nearest positioned element, where "positioned" means it has a css position attribute of "relative", "absolute", or "fixed".

我在很多地方依賴于 position 的計(jì)算解決,難道是 jQuery 計(jì)算 offsetParent 節(jié)點(diǎn)時(shí)出了差錯(cuò):

打開(kāi)瀏覽器的調(diào)試窗口,我輸入如下代碼:

$('#theTD').offsetParent()

可見(jiàn),通過(guò) jQuery 獲取到的依然是 .container, 這個(gè)方法返回的沒(méi)問(wèn)題。

沒(méi)辦法,既然出了問(wèn)題,只好先在自己的代碼中修正了。

第一次嘗試

既然 td 的相對(duì)位置是相對(duì)于 table,那不如用 table 做個(gè)中轉(zhuǎn),計(jì)算出 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));
    
});

看著好像是正確的,后來(lái)測(cè)試發(fā)現(xiàn)遇到嵌套表格就不行了,如下示例:

<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;
}

第二次嘗試

一計(jì)不成,再生一計(jì)。既然 table 的 position 出問(wèn)題,那么 div 的 position 應(yīng)該是正常的吧。這次我就來(lái)在 td 里面動(dòng)態(tài)創(chuàng)建一個(gè) div 節(jié)點(diǎn)怎么樣?

$(function() {
  var tmpDiv = $('<div style="position:relative;"/>').prependTo($('#theTD'));
  var tdPosition = tmpDiv.position();
  tmpDiv.remove();
  
    $('#result').html('top:' + tdPosition.top + ' left:' + tdPosition.left);
});

看著好像沒(méi)有問(wèn)題,可以問(wèn)題依舊,當(dāng)設(shè)置 td 的 vertical-align: middle 時(shí),問(wèn)題暴露出來(lái)了,因?yàn)榇藭r(shí) td 內(nèi)的 div 是居中顯示的:

第三次嘗試

就在我一籌莫展時(shí),我忽然想到前面的 offsetParent() 函數(shù),這個(gè)函數(shù)能獲取正確的父節(jié)點(diǎn)。既然 position() 函數(shù)有問(wèn)題,我何不嘗試 offset() 函數(shù)。

1. jQuery.position(): 獲取元素相對(duì)于父元素的偏移量(position: relative / absolute)

2. jQuery.offset(): 獲取元素相對(duì)于視口的偏移量

$(function() {
    var tdOffset = $('#theTD').offset();
    var tdParentOffset = $('#theTD').offsetParent().offset();
    $('#result').html('top:' + (tdOffset.top  - tdParentOffset.top) + ' left:' + (tdOffset.left - tdParentOffset.left));
});

這次使用當(dāng)前元素和相對(duì)父元素的 offset 相減,得到的結(jié)果是否我們所需要的呢?

不錯(cuò),正是我們所需要的。因?yàn)檫@個(gè)邏輯判斷很簡(jiǎn)單,和是否表格嵌套沒(méi)關(guān)系,所以測(cè)試下第一次嘗試失敗的情況:

 Bingo! 一切正常。此問(wèn)題圓滿解決!

真是的jQuery的BUG嗎?

想想就不可思議,一個(gè)被全球用戶使用的公共JavaScript居然有這樣的BUG,難道就沒(méi)人發(fā)現(xiàn)么?

網(wǎng)上搜索了一圈,看起來(lái)我多慮了,早就有用戶提出這個(gè)問(wèn)題:

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 native offsetParent 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.

這個(gè)是 jQuery 核心開(kāi)發(fā)團(tuán)隊(duì)給出的答案,大意是說(shuō) offsetParent 這個(gè)節(jié)點(diǎn)屬性指的是這樣一個(gè)父節(jié)點(diǎn):

1. 節(jié)點(diǎn)是非靜態(tài)的,也就是擁有 position: relative / absolute / fixed 樣式

2. 節(jié)點(diǎn)是 table, th 或者 td

哈,這么多年過(guò)去了,我居然第一次聽(tīng)說(shuō) offsetParent 還有相對(duì)于 td,th,table這個(gè)說(shuō)法,盡管這個(gè)td,th,table節(jié)點(diǎn)是 position:static,下面使用代碼來(lái)驗(yàn)證一下:

$(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);});

分別在不同瀏覽器中查看結(jié)果。

Chrome:

Firefox:

Edge:

IE11(由于IE11打不開(kāi)jsfiddle網(wǎng)站,我們單獨(dú)創(chuàng)建了一個(gè)頁(yè)面):

甚至IE9也是這樣的:

好吧,看來(lái)我真是孤陋而寡聞了。

jQuery 是在 v3.3.0 開(kāi)始考慮到第二種情況的,但是 jQuery.offsetParent() 的定義沒(méi)有改變。這種不一致性本身就是一個(gè)BUG。

而不和之前的 jQuery v1.x, v2.x 乃至于 v3.3.0 之前的版本兼容,對(duì)于一個(gè)廣泛使用的公共庫(kù)而言,更應(yīng)該算是一個(gè)BUG,或者至少提供兼容之間的方法。

考慮另一個(gè)更現(xiàn)實(shí)的問(wèn)題:在實(shí)際應(yīng)用中,我們?yōu)槭裁匆@取 td ,table 的 position() 呢?

不是為了拿獲取到的值去更新其他 td 或者 table 的 top/left 屬性!

而是為了拿獲取的值去更新其他相關(guān) div 節(jié)點(diǎn)的 top/left 屬性,而 div 節(jié)點(diǎn)的 position() 是相對(duì)于 position: relative / absolute / fixed 父節(jié)點(diǎn)的,而非 td ,table 節(jié)點(diǎn),這就一定會(huì)出問(wèn)題。

所以,我們還是認(rèn)定這是一個(gè)BUG。

但是 jQuery 官方貌似沒(méi)有修正這個(gè)問(wèn)題的意思,而是可能在將來(lái)版本修改  jQuery.offsetParent() 的定義:

但是jQuery你這樣做真的好么,都10多年了你都沒(méi)遵守標(biāo)準(zhǔn),突然來(lái)個(gè)小版本號(hào)稱支持標(biāo)準(zhǔn),搞的和之前的代碼不兼容,你讓之前的代碼情何以堪!

不管怎樣,我們有了自己的解決辦法。

小結(jié)

這篇文章描述了我們 FineUIPro 產(chǎn)品 更新中遇到的一個(gè)問(wèn)題,最終將問(wèn)題定位到 jQuery.position() 函數(shù),雖然jQuery的做法是依照HTML規(guī)范來(lái)的,但是 jQuery.offsetParent() 和 jQuery.position() 兩個(gè)函數(shù)有沖突,并且會(huì)導(dǎo)致之前的jQuery插件出錯(cuò),應(yīng)該算是一個(gè)BUG吧。

好在本文給出了一個(gè)補(bǔ)救的方法,如果這里的方法能夠?qū)δ阌兴鶈l(fā)或者幫助,就給個(gè)推薦唄。

相關(guān)文章

最新評(píng)論