[英]JavaScript function offsetLeft - slow to return value (mainly IE9)
我在調試新聞動態時遇到了很多困難-我是使用JavaScript從頭開始編寫的。
除IE9(以及一些移動瀏覽器,Opera Mobile)移動非常緩慢外,它在大多數瀏覽器上都可以正常工作。
使用開發人員工具>探查器使我能夠找到問題的根本原因。
這是對offsetLeft
的調用,以確定是否旋轉代號,即第一個元素成為最后一個元素。
function NeedsRotating() {
var ul = GetList();
if (!ul) {
return false;
}
var li = GetListItem(ul, 1);
if (!li) {
return false;
}
if (li.offsetLeft > ul.offsetLeft) {
return false;
}
return true;
}
function MoveLeft(px) {
var ul = GetList();
if (!ul) {
return false;
}
var li = GetListItem(ul, 0);
if (!li) {
return false;
}
var m = li.style.marginLeft;
var n = 0;
if (m.length != 0) {
n = parseInt(m);
}
n -= px;
li.style.marginLeft = n + "px";
li.style.zoom = "1";
return true;
}
返回值似乎要花費300毫秒以上,而股票行情應該每10毫秒向左移動1個像素。
是否有已知的解決方法?
謝謝
我同意@samccone的觀點,即如果GetList()
和GetListItem()
每次都在執行DOM操作,則應盡量保存對那些調用所檢索的元素的引用,並減少DOM操作。
那么我就可以操縱該變量,並希望它不會通過調用offsetLeft與“真實”值不同步。
您將只將對DOM元素的引用存儲在變量中。 由於它是參考,因此是真正的價值。 它是相同的對象。 例如:
var li = ul.getElementsByTagName( "li" )[ index ];
該存儲對DOM對象的引用。 您可以隨時從該對象讀取offsetLeft
,而無需執行另一個DOM操作(如getElementsByTagName
)來檢索該對象。
另一方面,以下內容只會存儲該值,而不會保持同步:
var offsetLeft = ul.getElementsByTagName( "li" )[ index ].offsetLeft;
如果offsetLeft
確實是一個瓶頸,是否有可能您可以改寫一下以減少閱讀量呢? 在這種情況下,每次輪換出第一項時,您可以為新的第一項讀取一次offsetLeft
,然后在每次調用MoveLeft()
將該值減一,直到它MoveLeft()
0
(或其他值)嗎? 例如
function MoveLeft( px ) {
current_offset -= px;
如果您想更加積極地避免offsetLeft
,也許可以做一些事情,一次讀取每個列表項的寬度,一次讀取第一項的offsetLeft
,然后使用這些值確定何時旋轉,而無需再次調用offsetLeft
。
我想我明白了...所以elms [“ foo”]必須是全局變量嗎?
我想我真的只需要使用全局變量,而不是每10毫秒調用一次offsetLeft。
您不需要使用全局變量,實際上您應該避免使用它-這是不好的設計。 在不使用全局變量的情況下,至少可以采用兩種好的方法:
您可以將整個程序包裝在閉包中:
( function () { var elements = {}; function NeedsRotating() { ... } function example() { // The follow var declaration will block access // to the outer `elements` var elements; } // Rest of your code here } )();
這些elements
的作用域僅限於包含它的匿名函數。 它不是全局變量,因此在匿名函數之外不可見。 只要您在內部函數中未聲明相同名稱的變量,匿名函數內的任何代碼(包括函數(在本例中為NeedsRotating()
都將可見。
您可以將所有內容封裝在一個對象中:
( function () { var ticker = {}; ticker.elements = {}; // Assign a method to a property of `ticker` ticker.NeedsRotating = function () { // All methods called on `ticker` can access its // props (eg `elements`) via `this` var ul = this.elements.list; var li = this.elements.list_item; // Example of calling another method on `ticker` this.do_something(); } ; // Rest of your code here // Something like this maybe ticker.start(); } )();
在這里,我再次將所有內容包裝在一個匿名函數中,這樣即使ticker
也不是全局變量。
首先,對於setTimeout
,最好這樣做:
t = setTimeout( TickerLoop, i );
而不是:
t = setTimeout("TickerLoop();", i);
在JS中,函數是一流的對象,因此您可以將實際的函數對象作為參數傳遞給setTimeout
,而不是傳遞字符串,就像使用eval
。
您可能需要考慮使用setInterval
而不是setTimeout
。
因為肯定在setTimeout中執行的任何代碼都將超出閉包的范圍嗎?
實際上並非如此。 在定義函數時形成閉包。 因此,通過setTimeout
調用函數不會干擾函數對封閉變量的訪問。 這是一個簡單的演示代碼段:
( function () {
var offset = 100;
var whatever = function () {
console.log( offset );
};
setTimeout( whatever, 10 );
} )();
但是, setTimeout
將干擾this
方法在您的方法中的綁定,如果將所有內容封裝在一個對象中,這將是一個問題。 以下內容不起作用:
( function () {
var ticker = {};
ticker.offset = 100;
ticker.whatever = function () {
console.log( this.offset );
};
setTimeout( ticker.whatever, 10 );
} )();
在ticker.whatever
內部, ticker.whatever
, this
都不會涉及ticker
。 但是,您可以在此處使用匿名函數來形成閉包來解決問題:
setTimeout( function () { ticker.whatever(); }, 10 );
如果我將其存儲在類變量(即
var ticker.SecondLiOffsetLeft = GetListItem(ul, 1).offsetLeft
則在旋轉列表時僅需要再次調用offsetLeft
。我認為這是全局變量的最佳替代方案?
關鍵是:
每次旋轉列表時,請訪問一次offsetLeft
。
如果將列表項存儲在變量中,則可以訪問其offsetLeft
屬性,而不必反復執行諸如getElementsByTagName()
類的DOM操作來獲取列表對象。
如果將所有內容包裝在一個對象中,則#2中的變量可以是對象屬性,也可以是可以通過其閉包作用域訪問的變量。 我可能會將其包裝在一個對象中。
我更新了“ DOM操作”部分以闡明,如果存儲對DOM對象的引用,它將是完全相同的對象。 您不想直接存儲offsetLeft
,因為那樣只會存儲值,並且不會保持同步。
但是,您決定存儲它們(例如,對象屬性或變量),則可能應該一次檢索所有li
對象並將它們存儲在類似數組的結構中。 例如
this.li = ul.getElementsByTagName( "li" );
每次旋轉時,都以某種方式指示當前項目,例如:
this.current_item = ###;
// or
this.li.current = this.li[ ### ];
// Then
this.li[ this.current_item ].offsetLeft
// or
this.li.current.offsetLeft
或者,如果您願意,可以將li
對象存儲在數組中,並針對每次旋轉進行此操作:
this.li.push( this.li.shift() );
// then
this.li[0].offsetLeft
您的代碼很慢,因為讀取offsetLeft
將迫使瀏覽器進行重排。 回流是使您減速的部分。 瀏覽器通常足夠聰明,可以將更改排隊以減少重排次數。 但是,考慮到在訪問offsetLeft
時需要最新的值,您將強制瀏覽器刷新該隊列並進行重排,以便為您計算正確的值。
在不了解您要執行的操作的所有詳細信息的情況下,很難知道為提高性能建議的建議。 http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/更詳細地說明了此問題,並提供了有關最小化回流的建議。
如果您不將選擇器緩存在var li = GetListItem(ul, 1);
那么性能將受到極大的影響..那就是您所看到的,因為您每10ms會觸發一個新的選擇器
您應該將選擇器緩存在像
elms["foo"] = elms["foo"] || selectElm(foo);
elms["foo"].actionHere(...)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.