Skip to content

Commit

Permalink
Fix infinite scroll when the scrollbar is not visible (#5575)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kevin-brown committed Jul 20, 2019
1 parent 8a5aeab commit d926025
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 16 deletions.
35 changes: 19 additions & 16 deletions src/js/select2/dropdown/infiniteScroll.js
Expand Up @@ -18,6 +18,7 @@ define([

if (this.showLoadingMore(data)) {
this.$results.append(this.$loadingMore);
this.loadMoreIfNeeded();
}
};

Expand All @@ -36,25 +37,27 @@ define([
self.loading = true;
});

this.$results.on('scroll', function () {
var isLoadMoreVisible = $.contains(
document.documentElement,
self.$loadingMore[0]
);
this.$results.on('scroll', this.loadMoreIfNeeded.bind(this));
};

InfiniteScroll.prototype.loadMoreIfNeeded = function () {
var isLoadMoreVisible = $.contains(
document.documentElement,
this.$loadingMore[0]
);

if (self.loading || !isLoadMoreVisible) {
return;
}
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();
}
});
if (currentOffset + 50 >= loadingMoreOffset) {
this.loadMore();
}
};

InfiniteScroll.prototype.loadMore = function () {
Expand Down
126 changes: 126 additions & 0 deletions tests/results/infiniteScroll-tests.js
@@ -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
}
}
});
});
1 change: 1 addition & 0 deletions tests/unit-jq1.html
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions tests/unit-jq2.html
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions tests/unit-jq3.html
Expand Up @@ -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>
Expand Down

0 comments on commit d926025

Please sign in to comment.