Fix infinite scroll when the scrollbar is not visible (#5575)
Ever since the 4.0.0 release of Select2, there has been a bug where if you enabled infinite scrolling but did not return enough results on the first load of AJAX to show a scrollbar, then infinite scrolling would not be enabled and you could not view anything other than the first page of results. The solution for this was first proposed in #3888 but it was closed off because of inactivity and missing tests. This fixes the issue by performing the check to see if more results should be loaded both on scroll and also when the results are first loaded. This solves the issue that we were seeing before, because the plugin knows it needs to load in more results, just it did not receive the scroll event before and thus was not able to actually load in the new results. This has the potential to trigger multiple AJAX requests to load in multiple pages of results if the user has the ability to see many options, but only a few are being loaded at a time. This also adds tests for infinite scrolling, both to ensure that it will attempt to load additional pages, even without the scrollbar, and to ensure that the regular behaviour of not loading additional pages when the scrollbar is visible is preserved. Fixes #3088
This commit is contained in:
parent
8a5aeabcce
commit
d9260254c1
21
src/js/select2/dropdown/infiniteScroll.js
vendored
21
src/js/select2/dropdown/infiniteScroll.js
vendored
@ -18,6 +18,7 @@ define([
|
||||
|
||||
if (this.showLoadingMore(data)) {
|
||||
this.$results.append(this.$loadingMore);
|
||||
this.loadMoreIfNeeded();
|
||||
}
|
||||
};
|
||||
|
||||
@ -36,25 +37,27 @@ define([
|
||||
self.loading = true;
|
||||
});
|
||||
|
||||
this.$results.on('scroll', function () {
|
||||
this.$results.on('scroll', this.loadMoreIfNeeded.bind(this));
|
||||
};
|
||||
|
||||
InfiniteScroll.prototype.loadMoreIfNeeded = function () {
|
||||
var isLoadMoreVisible = $.contains(
|
||||
document.documentElement,
|
||||
self.$loadingMore[0]
|
||||
this.$loadingMore[0]
|
||||
);
|
||||
|
||||
if (self.loading || !isLoadMoreVisible) {
|
||||
if (this.loading || !isLoadMoreVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentOffset = self.$results.offset().top +
|
||||
self.$results.outerHeight(false);
|
||||
var loadingMoreOffset = self.$loadingMore.offset().top +
|
||||
self.$loadingMore.outerHeight(false);
|
||||
var currentOffset = this.$results.offset().top +
|
||||
this.$results.outerHeight(false);
|
||||
var loadingMoreOffset = this.$loadingMore.offset().top +
|
||||
this.$loadingMore.outerHeight(false);
|
||||
|
||||
if (currentOffset + 50 >= loadingMoreOffset) {
|
||||
self.loadMore();
|
||||
this.loadMore();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
InfiniteScroll.prototype.loadMore = function () {
|
||||
|
126
tests/results/infiniteScroll-tests.js
Normal file
126
tests/results/infiniteScroll-tests.js
Normal file
@ -0,0 +1,126 @@
|
||||
module('Results - Infinite scrolling');
|
||||
|
||||
test('loadingMore is triggered even without a scrollbar', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
var $ = require('jquery');
|
||||
|
||||
var $select = $('<select></select>');
|
||||
|
||||
var $container = $('<span></span>');
|
||||
var container = new MockContainer();
|
||||
|
||||
var Utils = require('select2/utils');
|
||||
var Options = require('select2/options');
|
||||
|
||||
var Results = require('select2/results');
|
||||
var InfiniteScroll = require('select2/dropdown/infiniteScroll');
|
||||
|
||||
var InfiniteScrollResults = Utils.Decorate(Results, InfiniteScroll);
|
||||
|
||||
var results = new InfiniteScrollResults($select, new Options({}));
|
||||
|
||||
// Fake the data adapter for the `setClasses` method
|
||||
results.data = {};
|
||||
results.data.current = function (callback) {
|
||||
callback([{ id: 'test' }]);
|
||||
};
|
||||
|
||||
$('#qunit-fixture').append(results.render());
|
||||
|
||||
results.bind(container, $container);
|
||||
|
||||
results.on('query:append', function () {
|
||||
assert.ok(true, 'It tried to load more immediately');
|
||||
});
|
||||
|
||||
container.trigger('results:all', {
|
||||
data: {
|
||||
results: [
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
more: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('loadingMore is not triggered without scrolling', function (assert) {
|
||||
assert.expect(0);
|
||||
|
||||
var $ = require('jquery');
|
||||
|
||||
var $select = $('<select></select>');
|
||||
|
||||
var $container = $('<span></span>');
|
||||
var container = new MockContainer();
|
||||
|
||||
var Utils = require('select2/utils');
|
||||
var Options = require('select2/options');
|
||||
|
||||
var Results = require('select2/results');
|
||||
var InfiniteScroll = require('select2/dropdown/infiniteScroll');
|
||||
|
||||
var InfiniteScrollResults = Utils.Decorate(Results, InfiniteScroll);
|
||||
|
||||
var results = new InfiniteScrollResults($select, new Options({}));
|
||||
|
||||
// Fake the data adapter for the `setClasses` method
|
||||
results.data = {};
|
||||
results.data.current = function (callback) {
|
||||
callback([{ id: 'test' }]);
|
||||
};
|
||||
|
||||
var $results = results.render();
|
||||
|
||||
$('#qunit-fixture').append($results);
|
||||
$results.css('max-height', '100px');
|
||||
|
||||
results.bind(container, $container);
|
||||
|
||||
results.on('query:append', function () {
|
||||
assert.ok(false, 'It tried to load more immediately');
|
||||
});
|
||||
|
||||
container.trigger('results:all', {
|
||||
data: {
|
||||
results: [
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
},
|
||||
{
|
||||
id: 'test',
|
||||
text: 'Test'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
more: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@ -82,6 +82,7 @@
|
||||
<script src="options/width-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="results/focusing-tests.js" type="text/javascript"></script>
|
||||
<script src="results/infiniteScroll-tests.js" type="text/javascript"></script>
|
||||
<script src="results/option-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="selection/allowClear-tests.js" type="text/javascript"></script>
|
||||
|
@ -82,6 +82,7 @@
|
||||
<script src="options/width-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="results/focusing-tests.js" type="text/javascript"></script>
|
||||
<script src="results/infiniteScroll-tests.js" type="text/javascript"></script>
|
||||
<script src="results/option-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="selection/allowClear-tests.js" type="text/javascript"></script>
|
||||
|
@ -82,6 +82,7 @@
|
||||
<script src="options/width-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="results/focusing-tests.js" type="text/javascript"></script>
|
||||
<script src="results/infiniteScroll-tests.js" type="text/javascript"></script>
|
||||
<script src="results/option-tests.js" type="text/javascript"></script>
|
||||
|
||||
<script src="selection/allowClear-tests.js" type="text/javascript"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user