Chr*_*non 5 javascript internet-explorer unobtrusive-javascript internet-explorer-9
我在调试新闻自动收报机时遇到了困难 - 我是从头开始使用JavaScript编写的.
除了IE9(以及一些移动浏览器 - Opera Mobile)之外,它在大多数浏览器上都能正常运行.
使用Developer Tools> Profiler使我能够找到问题的根本原因.
这是一个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;
}
Run Code Online (Sandbox Code Playgroud)
它似乎需要超过300毫秒来返回值,而自动收报机假设每10毫秒向左移动1个像素.
这有一个已知的解决方案吗?
谢谢
我@samccone同意,如果GetList()和GetListItem()每一次都进行DOM操作,你应该尽量保存到尽可能减少对DOM操作这些调用检索到的元素的引用。
然后我可以操纵该变量,希望它不会通过调用 offsetLeft 与“真实”值不同步。
您只需在变量中存储对 DOM 元素的引用。既然是参考,那就是真正的价值。它是完全相同的对象。例如:
var li = ul.getElementsByTagName( "li" )[ index ];
Run Code Online (Sandbox Code Playgroud)
它存储了对 DOM 对象的引用。您可以offsetLeft随时读取该对象,而无需执行另一个 DOM 操作(如getElementsByTagName)来检索该对象。
另一方面,以下内容只会存储值而不会保持同步:
var offsetLeft = ul.getElementsByTagName( "li" )[ index ].offsetLeft;
Run Code Online (Sandbox Code Playgroud)
如果offsetLeft真的是一个瓶颈,您是否可以重新编写它以减少阅读量?在这种情况下,每次轮换第一个项目时,您是否可以offsetLeft为新的第一个项目读取一次,然后在每次调用中递减该值,MoveLeft()直到达到0(或其他)?例如
function MoveLeft( px ) {
current_offset -= px;
Run Code Online (Sandbox Code Playgroud)
如果你想更加积极地避免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
} )();
Run Code Online (Sandbox Code Playgroud)
这里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 (e.g. `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();
} )();
Run Code Online (Sandbox Code Playgroud)
在这里,我再次将所有内容包装在一个匿名函数中,以便 eventicker不是全局变量。
首先,关于setTimeout,你最好这样做:
t = setTimeout( TickerLoop, i );
Run Code Online (Sandbox Code Playgroud)
而不是:
t = setTimeout("TickerLoop();", i);
Run Code Online (Sandbox Code Playgroud)
在 JS 中,函数是一等对象,因此您可以将实际的函数对象作为参数setTimeout传递给,而不是传递字符串,就像使用eval.
您可能要考虑setInterval代替setTimeout.
因为在 setTimeout 中执行的任何代码肯定会超出闭包的范围?
实际上并非如此。闭包是在定义函数时形成的。因此通过调用函数setTimeout不会干扰函数对封闭变量的访问。这是一个简单的演示片段:
( function () {
var offset = 100;
var whatever = function () {
console.log( offset );
};
setTimeout( whatever, 10 );
} )();
Run Code Online (Sandbox Code Playgroud)
setTimeout但是,会干扰this方法中的 绑定,如果您将所有内容都封装在一个对象中,这将是一个问题。以下将不起作用:
( function () {
var ticker = {};
ticker.offset = 100;
ticker.whatever = function () {
console.log( this.offset );
};
setTimeout( ticker.whatever, 10 );
} )();
Run Code Online (Sandbox Code Playgroud)
里面ticker.whatever,this就不提了ticker。但是,这里可以使用匿名函数形成闭包来解决问题:
setTimeout( function () { ticker.whatever(); }, 10 );
Run Code Online (Sandbox Code Playgroud)
我应该将它存储在一个类变量中,即
var ticker.SecondLiOffsetLeft = GetListItem(ul, 1).offsetLeft我只需要offsetLeft在旋转列表时再次调用。我认为这是全局变量的最佳替代方案?
关键是:
offsetLeft每次旋转列表时访问一次。
如果将列表项存储在变量中,则可以访问它们的offsetLeft属性,而无需重复执行 DOM 操作(例如getElementsByTagName()获取列表对象)。
#2 中的变量可以是一个对象属性,如果你把所有东西都包装在一个对象中,或者只是一个可以通过函数的闭包范围访问的变量。我可能会把它包装在一个对象中。
我更新了“DOM 操作”部分以阐明如果您存储对 DOM 对象的引用,它将是完全相同的对象。您不想offsetLeft直接存储,因为那只会存储值并且不会保持同步。
无论您决定存储它们(例如对象属性或变量),您可能应该检索所有li对象一次并将它们存储在类似数组的结构中。例如
this.li = ul.getElementsByTagName( "li" );
Run Code Online (Sandbox Code Playgroud)
每次旋转时,以某种方式指示当前项目,例如:
this.current_item = ###;
// or
this.li.current = this.li[ ### ];
// Then
this.li[ this.current_item ].offsetLeft
// or
this.li.current.offsetLeft
Run Code Online (Sandbox Code Playgroud)
或者,如果您愿意,您可以将li对象存储在一个数组中,并为每次旋转执行此操作:
this.li.push( this.li.shift() );
// then
this.li[0].offsetLeft
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1762 次 |
| 最近记录: |