我有一个“无限滚动”的页面。它会计算页面末尾与当前页面之间的差异,如果此差异足够小,则会加载更多内容。使用jQuery代码是这样的:
$(window).on('scroll', function() {
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
# load more content via ajax
}
现在,问题是每次滚动时,每个滚动都会触发多次此事件。我想每隔x毫秒发射一次。我该怎么做?
答案 0 :(得分:37)
解决此问题的一种方法是定义时间间隔,并仅在该时间间隔内处理一次滚动事件。如果在该时间间隔内有多个滚动事件进入,则忽略它并仅在该时间间隔过去时处理它。
var scrollTimer, lastScrollFireTime = 0;
$(window).on('scroll', function() {
var minScrollTime = 100;
var now = new Date().getTime();
function processScroll() {
console.log(new Date().getTime().toString());
}
if (!scrollTimer) {
if (now - lastScrollFireTime > (3 * minScrollTime)) {
processScroll(); // fire immediately on first scroll
lastScrollFireTime = now;
}
scrollTimer = setTimeout(function() {
scrollTimer = null;
lastScrollFireTime = new Date().getTime();
processScroll();
}, minScrollTime);
}
});
这将立即触发第一个滚动事件,然后在滚动条移动时每100ms大约一次滚动事件,然后在滚动条停止移动后返回一个最终事件。您可以通过将参数更改为setTimeout
(当前设置为100)来调整事件的频率。
这里有一个演示:http://jsfiddle.net/jfriend00/EBEqZ/,您需要打开调试控制台窗口,开始在内容窗口中移动滚动条,然后在调试控制台窗口中观察每个滚动事件的时间。在我的Chrome版本中,它们的最小间距设置为100毫秒,它们似乎每100-200毫秒就会发生一次。
答案 1 :(得分:36)
答案 2 :(得分:10)
来自John Resig的很酷的解释是jQuery的创建者来解决这种情况。
CREATE OR REPLACE FUNCTION timelog()
RETURNS trigger AS
$BODY$
DECLARE
t_ix real;
n int;
BEGIN
IF NEW.time_type = 'Start' THEN
SELECT t.time_index FROM table_ebscb_spa_log04 t WHERE t.fn_name = NEW.fn_name AND t.time_type = 'Start' ORDER BY t.stmtserial DESC LIMIT 1 INTO t_ix;
GET DIAGNOSTICS n = ROW_COUNT;
IF (n = 0) THEN
t_ix := 1; --this is ok, here it's really necessary to set it to 1
ELSE
t_ix := t_ix + 1;
END IF;
ELSE
IF NEW.time_type = 'Lap' THEN
SELECT t.time_index FROM table_ebscb_spa_log04 t WHERE t.fn_name = NEW.fn_name AND (t.time_type = 'Start' OR time_type = 'Lap') ORDER BY t.stmtserial DESC LIMIT 1 INTO t_ix;
GET DIAGNOSTICS n = ROW_COUNT;
IF (n = 0) THEN
t_ix := 1; --!!!HERE I MADE A MISTAKE (SORRY) IT SHOULDN'T BE SET TO 1, IT SHOULD THROW AN ERROR MESSAGE 'There isn't any previous same fn_name with time_type Start';
ELSE
t_ix := t_ix + 0.1;
END IF;
END IF;
END IF;
NEW.time_index = t_ix;
return NEW;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION timelog()
OWNER TO postgres;
答案 3 :(得分:8)
var isWorking = 0;
$(window).on('scroll', function()
{
if(isWorking==0)
{
isWorking=1;
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
# load more content via ajax
setTimeout(function(){isWorking=0},1000);
}
}
答案 4 :(得分:5)
var now = new Date().getTime();
$(window).scroll( function () {
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
{
if (new Date().getTime() - now > 1000)
{
console.log("Task executed once per second");
now = new Date().getTime();
}
}
});
或强>
您可以使用限制功能调用: throttling-function-calls
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
您可以这样称呼它:
$('body').on('mousemove', throttle(function (event) {
console.log('tick');
}, 1000));
答案 5 :(得分:4)
这是一个不需要使用额外JS库或插件的解决方案,旨在简化。它可能没有其他实现那么高效,但它绝对是每次滚动时触发主事件的一步。
这是由Danny Van Kooten从blog post获取的。在我的博客上,我用它来延迟我的onscroll()
事件以获得我的回到顶部按钮。
var timer;
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
// actual code here. Your call back function.
console.log( "Firing!" );
}, 100);
});
您还可以通过将变量移出回调函数来进一步提高性能,以避免不必要的重新计算,例如$(window).height()
的值或某些静态div元素的高度,一旦页面加载就不会改变。
这是一个根据我的用例改编的例子。
var scrollHeight = $("#main-element").height(); //never changes, no need to recalculate.
$(window).on('scroll', function() {
if (timer)
window.clearTimeout(timer);
timer = window.setTimeout(function() {
var scrollPosition = $(window).height() + $(window).scrollTop();
if ($(window).scrollTop() < 500)
$(".toggle").fadeIn(800);
else
$(".toggle").fadeOut(800);
}, 150); //only fire every 150 ms.
});
这将实际功能限制为仅每150ms执行一次,或者如果150ms未通过则将定时器重置为0。调整价值以满足您的需求。
答案 6 :(得分:0)
滚动火焰多次是正确的,你应该每次都能以不同的方式获得滚动位置。我认为你需要设置一个定时器,当你第一次进入滚动事件时,如你提到的x毫秒,并记录时间戳,然后下一次滚动事件触发,检查最后一次触发时间并忽略它,如果它在x毫秒内,并在你的定时器动作中做真正的工作。
答案 7 :(得分:0)
对于合适的油门功能,人们不需要大量的局部变量。节流功能的目的是减少浏览器资源,而不是应用你正在使用的更多开销。作为这种说法的证据的证据,我设计了一种油门功能,其范围内只有5个“悬挂”变量参考。另外,我对油门功能的不同用途要求它们有许多不同的情况。以下是我认为油门功能需要“好”的事情清单。
而且,我相信以下节流功能可以满足所有这些。
function throttle(func, alternateFunc, minimumInterval) {
var executeImmediately = true, freshEvt = null;
return function(Evt) {
if (executeImmediately) { // Execute immediatly
executeImmediately = false;
setTimeout(function(f){ // handle further calls
executeImmediately = true;
if (freshEvt !== null) func( freshEvt );
freshEvt = null;
}, minimumInterval);
return func( Evt );
} else { // Delayed execute
freshEvt = Evt;
if (typeof alternateFunc === "function") alternateFunc( Evt );
}
};
}
然后,围绕DOM事件侦听器包装此限制函数:
var ltCache = [];
function listen(obj, evt, func, _opts){
var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) break a;
lF = throttle(func, options.alternate||null, options.interval||200);
ltCache.push(func, options.alternate||null, options.interval||200, lF);
}
obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
for (var i = 0, Len = ltCache.length; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) return obj.removeEventListener(evt, ltCache[i+3], options);
}
使用示例:
function throttle(func, alternateFunc, minimumInterval) {
var executeImmediately = true, freshEvt = null;
function handleFurtherCalls(f){
executeImmediately = true;
if (freshEvt !== null) func( freshEvt );
freshEvt = null;
};
return function(Evt) {
if (executeImmediately) { // Execute immediatly
executeImmediately = false;
setTimeout(handleFurtherCalls, minimumInterval);
return func( Evt );
} else { // Delayed execute
freshEvt = Evt;
if (typeof alternateFunc === "function") alternateFunc( Evt );
}
};
}
var ltCache = [];
function listen(obj, evt, func, _opts){
var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) break a;
lF = throttle(func, options.alternate||null, options.interval||200);
ltCache.push(func, options.alternate||null, options.interval||200, lF);
}
obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
for (var i = 0, Len = ltCache.length; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) return obj.removeEventListener(evt, ltCache[i+3], options);
}
var numScrolls = 0, counter = document.getElementById("count");
listen(window, 'scroll', function whenbodyscrolls(){
var scroll = -document.documentElement.getBoundingClientRect().top;
counter.textContent = numScrolls++;
if (scroll > 900) {
console.log('Body scrolling stoped!');
mute(window, 'scroll', whenbodyscrolls, true);
}
}, true);
<center><h3>\/ Scroll Down The Page \/</h3></center>
<div style="position:fixed;top:42px"># Throttled Scrolls: <span id="count">0</span></div>
<div style="height:192em;background:radial-gradient(circle at 6em -5em, transparent 0px, rgba(128,0,0,.4) 90em),radial-gradient(circle at 10em 40em, rgba(255,255,255,.8) 0px, rgba(128,0,0,.02) 50em),radial-gradient(circle at 4em 80em, rgba(0,192,0,.75) 0px,rgba(0,128,0,.56) 10em,rgba(255,0,96,.03125) 30em),radial-gradient(circle at 86em 24em, rgba(255,0,0,.125) 0px,rgba(0,0,255,.0625) 60em,transparent 80em);"></div>
<style>body{margin:0}</style>
默认情况下,此功能限制为每200ms最多一次调用。要将间隔更改为不同的毫秒数,请在options参数中传递名为“interval”的键,并将其设置为所需的毫秒数。