簡體   English   中英

JavaScript函數offsetLeft-緩慢返回值(主要是IE9)

[英]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個像素。

是否有已知的解決方法?

謝謝

DOM操作

我同意@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確實是一個瓶頸,是否有可能您可以改寫一下以減少閱讀量呢? 在這種情況下,每次輪換出第一項時,您可以為新的第一項讀取一次offsetLeft ,然后在每次調用MoveLeft()將該值減一,直到它MoveLeft() 0 (或其他值)嗎? 例如

function MoveLeft( px ) {

  current_offset -= px;

如果您想更加積極地避免offsetLeft ,也許可以做一些事情,一次讀取每個列表項的寬度,一次讀取第一項的offsetLeft ,然后使用這些值確定何時旋轉,而無需再次調用offsetLeft

全局變量

我想我明白了...所以elms [“ foo”]必須是全局變量嗎?

我想我真的只需要使用全局變量,而不是每10毫秒調用一次offsetLeft。

您不需要使用全局變量,實際上您應該避免使用它-這是不好的設計。 在不使用全局變量的情況下,至少可以采用兩種好的方法:

  1. 您可以將整個程序包裝在閉包中:

     ( 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()都將可見。

  2. 您可以將所有內容封裝在一個對象中:

     ( 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.whateverthis都不會涉及ticker 但是,您可以在此處使用匿名函數來形成閉包來解決問題:

setTimeout( function () { ticker.whatever(); }, 10 );

如果我將其存儲在類變量(即var ticker.SecondLiOffsetLeft = GetListItem(ul, 1).offsetLeft則在旋轉列表時僅需要再次調用offsetLeft

我認為這是全局變量的最佳替代方案?

關鍵是:

  1. 每次旋轉列表時,請訪問一次offsetLeft

  2. 如果將列表項存儲在變量中,則可以訪問其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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM