问题
I have this infinite scroll function set up on several of my pages. It works perfectly fine, but the Ajax call to load more items makes several database calls and it has to load a lot of images each time, and it usually takes several seconds to load. I've timed it at between 3 and 7 seconds depending on my connection. Because of this, it can turn into a real train wreck when the browser decides to fire the scroll event several times. How could I go about throttling or debouncing it so that the browser doesn't run the Ajax call several times in the span of a few seconds and grind everything to a halt?
$(document).ready()
{
//Infinite scroll
$(window).on('scroll', _.debounce( function(){
var height = $(window).height();
var scrollTop = $(window).scrollTop();
var dHeight = getDocHeight();
if( height + scrollTop >= dHeight - 100)
{
//Display items that were previously hidden
showAllItems();
if(!vloaded < START_NUM && !done){
//Load next set of items
$.ajax
({
type: "POST",
url: "/organizer/getMore",
data: { filter: vfilter, loaded: vloaded },
dataType: "html",
success: function( responseText, textStatus, XHR )
{
if(responseText.indexOf("// GRID ENTRY //") < 0){ //Out of items to load
done = true;
}
else{
//Display items that were previously hidden
showAllItems();
// select the element with the ID grid and insert the HTML
$( "#grid" ).append( responseText );
//alert("Loaded "+vloaded+" items");
}
}
});
// global variable
vloaded +=LOAD_NUM;
} // if
}
}
}, 500, true)); // on
} // ready
EDIT:
I downloaded underscore and added the debounce function, but now the Ajax call doesn't seem to be running at all. Even if I hadn't set immediate to true it should execute pretty quickly after I stop scrolling, but it doesn't execute at all.
回答1:
The Underscore library has methods for this:
- _.throttle()
- _.debounce()
You probably want _.debounce
. If you don't want to add Underscore as an additional dependency, you may be able to make a standalone method from the source for the method you want.
回答2:
Take a look at underscore. They have a great little debounce wrapper that can take any function and return a debounced version.
You should be able to take that scroll callback function and substitute it with _.debounce(function(){ ... your code ... }, 100)
.
If you're curious about the actual implementation, the source code is nice and concise.
回答3:
What works fine for me is the following code:
setupPaginationOnScroll: function() {
var self = this;
var loadNextPageIfNotLoadingThrottled = _.throttle(function() {
if ( !self.isLastPageLoading() ) {
self.loadNextPage()
}
},1000);
this.onScrollbarNearBottom(loadNextPageIfNotLoadingThrottled);
},
You'll note that I use throttle and not debounce, and when there is a pending request, I simply ignore the scroll event
This part can also interest you:
onScrollbarNearBottom: function(callback) {
var target = this.getDOMNode();
$(target).scroll(function(e) {
if ( ScrollUtils.isScrollbarNearBottom(target) ) {
callback(e);
}
});
}
module.exports = {
debug: false,
/**
* We consider someone is near the bottom if he has scrolled more than 90%
*/
scrollNearThreshold: 90,
/**
* Useful function to detect for exemple when to load more content (ie another page)
* if the user has almost scrolled to the bottom
*
* @return {boolean}
*/
isWindowScrollbarNearBottom: function() {
return this.isScrollbarNearBottom(window);
},
isScrollbarNearBottom: function(scrollableElementSelector) {
return this.getScrollPercentage(scrollableElementSelector) > this.scrollNearThreshold;
},
/**
* Returns the percentage of scroll in a given container.
* If the scrollbar is at the beginning it should return 0.
* If the scrollbar is at the end it should return 100 (almost :s)
*
* See http://stackoverflow.com/questions/22867584/get-scroll-percentage-in-a-dom-container
* For an unknown reason it sometimes returns a value > 100 (like 103 if you are at the bottom)
* It is not very precise but at least it should work rather well for most usecases.
*
* @return {number}
*/
getScrollPercentage: function(scrollableElementSelector) {
var scrollableElement = $(scrollableElementSelector);
// This is the number of hidden pixels of the scrollable element inside its container
var hiddenHeigth;
if ( scrollableElementSelector === window ) {
hiddenHeigth = $(document).height() - $(window).height();
} else {
hiddenHeigth = scrollableElement[0].scrollHeight - scrollableElement.outerHeight();
}
// This is the number of scrolled pixels
var scrolledHeight = scrollableElement.scrollTop();
//if ( hiddenHeigth < scrolledHeight ) {
//throw new Error("hiddenHeigth "+hiddenHeigth+" < scrolledHeight " +scrolledHeight + " -> Impossible unless you didn't use this function correctly");
//}
var scrollPercent = ( scrolledHeight / hiddenHeigth ) * 100;
if ( this.debug ) {
console.debug("hiddenHeigth=",hiddenHeigth,"scrolledHeight=",scrolledHeight,"scrollPercent=",scrollPercent);
}
return scrollPercent;
}
}
来源:https://stackoverflow.com/questions/11477179/javascript-infinite-scroll-throttling-debouncing