How is the endless scrolling done? I know that through AJAX, but how does it indicate from what for which post it is necessary to pull it out, etc.
3 answers
Well, in general, people transmit the page number, via POST, or GET it does not matter, they just remember which page is currently open and do ++ when they reach the bottom with a scroller.
- and if without pages to do? - Zow
- here apparently there is a view that the theoretical output is transferred to the javascript and when the bottom of the page is reached, a request is sent <current quantity> + <next packet> - thunder
This is called the Cursor Pagination.
Example:
You have a server and a database in which there are 100 posts. The server gives 10 posts per page. Numbered pagination makes a request to the server and in the request we pass the page number for which we want to get 10 posts. In the cursor pagination, everything is exactly the same, only we transfer not any page in the request, but one after the other:
- In the client application, you should have a list of posts:
postsList
and current page number:currentPage
. - When loading the page, a request is made for a dozen posts. When we got the answer with the posts, we put them in the
postsList
and memorize the pagecurrentPage = 1
. - We catch the event that the user has scrolled the list to the last post and make a request to receive a dozen posts from the
currentPage + 1
page. - We expand the
postsList
data (add to the end of the list). Remember the page:currentPage=2
.
That's all. Items 3 and 4 are executed until all 100 posts are loaded.
- oneI note that instead of
currentPage
may be better to use something likelastPostId
with a query to thewhere id < lastPostId
type databasewhere id < lastPostId
, (or a similar variant with the date of publication), so duplicates are excluded (if a new post appears between the requests) less - andreymal - @andreymal One could make a similar request, but it would not be enough to specify
id < lastPostId
. You are programming in Python . Django ? Just why reinvent the wheel when there are already ready-made solutions. Using the Django REST framework with a simple request with the page number in the parameters, you can get the necessary data:https://my_host/posts/?page=123
- Max - Then, that the proposed bike is simpler and more efficient, of course. Query to database with
offset (page-1)*count, count
is not the best solution for infinite scrolling. - andreymal - Give a detailed answer to this question with your decision. This is not a discussion forum. - Max
- oneI do not want, it is similar to yours, only
lastPostId
instead ofcurrentPage
:) - andreymal
An example implementation with detailed comments:
/* * ΠΠ° ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΡΠ°Π½ΠΈΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΏΡΠΎΠΊΡΡΡΠΈΠ²Π°Π΅ΠΌ Π΄ΠΎ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ */ var maxPages = 3; /* * ΠΠ° ΡΠΊΠΎΠ»ΡΠΊΠΎ Π΄ΠΎ ΠΊΠΎΠ½ΡΠ° ΡΡΡΠ°Π½ΠΈΡΡ Π²ΠΊΠ»ΡΡΠ°Π΅ΠΌ ΠΏΡΠΎΠΊΡΡΡΠΊΡ */ var scrollBufferRatio = 1 / 3; /* * ΠΠ΄Π΅ Π½Π°Ρ
ΠΎΠ΄ΠΈΡΡΡ ΠΎΠ±Π»Π°ΡΡΡ Π΄Π»Ρ Π΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ (ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅) */ var elementContainerSelector = '.messages'; /* * Π§ΡΠΎ Π΄Π΅Π»Π°ΡΡ Π΅ΡΠ»ΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ Π½ΠΎΠ²ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ */ var elementFindingErrorHandler = function() { throw "Could not find " + elementContainerSelector; }; /* * ΠΠ°Π·Π²Π°Π½ΠΈΡ ΡΠΎΠ±ΡΡΠΈΡ ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ * Π΅ΡΠ»ΠΈ Π΅ΡΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΎΠ±Π»Π°ΡΡΠ΅ΠΉ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ */ var eventName = 'scroll.toInfinity'; /* * ΠΠ΄Π΅ Π½Π°Ρ
ΠΎΠ΄ΠΈΡΡΡ Π²ΡΠ±ΠΎΡ ΡΡΡΠ°Π½ΠΈΡ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π° */ var pagerSelector = '.pager'; /* * ΠΠ΄Π΅ Π½Π°ΠΉΡΠΈ ΡΡΡΠ»ΠΊΡ Π½Π° ΡΠ»Π΅Π΄ΡΡΡΡΡ ΡΡΡΠ°Π½ΠΈΡΡ */ var nextPageSelector = '.pager .nextpage'; var $window = $(window); // ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠ΅ΠΌ ΠΏΡΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ var $elementContainer; var topOffset = 0, scrollBuffer = 1, pageCounter = 1; var eventProcessor = function(finished) { // ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ ΠΊΠΎΠ³Π΄Π° Π΄ΠΎΠ»ΠΆΠ½Π° ΡΡΠ°Π±ΠΎΡΠ°ΡΡ Π·Π°Π³ΡΡΠ·ΠΊΠ° // ΡΡΠΈΡΠ°Π΅ΡΡΡ ΠΎΡ ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠΉ Π²ΡΡΠΎΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ var triggerPosition = $elementContainer.height() + topOffset - scrollBuffer; // ΡΠ΅ΠΊΡΡΠ°Ρ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ ΠΏΠΎ Π½ΠΈΠΆΠ½Π΅ΠΉ ΡΠ°ΡΡΠΈ ΠΎΠΊΠ½Π° var scrollPosition = $window.scrollTop() + $window.height(); // Π΅ΡΠ΅ Π½Π΅ Π΄ΠΎΡΠ»ΠΈ Π΄ΠΎ Π½ΡΠΆΠ½ΠΎΠΉ ΡΠΎΡΠΊΠΈ, Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π΄Π΅Π»Π°Π΅ΠΌ if (scrollPosition < triggerPosition) { // Π½ΠΎ ΠΆΠ΄ΡΠΌ Π΄Π°Π»ΡΡΠ΅ finished(); return true; } // ΠΏΡΠΎΠ»ΠΈΡΡΠ°Π»ΠΈ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎΠ΅ ΡΠΈΡΠ»ΠΎ ΡΡΡΠ°Π½ΠΈΡ if (pageCounter >= maxPages) { // Π΄Π°Π΄ΠΈΠΌ Π²ΡΠ±ΠΎΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π»ΠΈΡΡΠ°ΡΡ Π»ΠΈ Π΄Π°Π»ΡΡΠ΅ $(pagerSelector).show(); // Π½Π΅ ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΡΡΠΊΠ°Π΅ΠΌ Π·Π°Π³ΡΡΠ·ΠΊΡ return true; } // Π΅ΡΠ»ΠΈ ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΡ Π½Π΅Ρ... var $nextPage = $(nextPageSelector); if ($nextPage.length == 0) { // ΡΠΎ ΠΈ Π·Π°Π³ΡΡΠΆΠ°ΡΡ Π΄Π°Π»ΡΡΠ΅ Π½Π΅ΡΠ΅Π³ΠΎ return true; } $.get($nextPage.attr('href'), function(data) { var $data = $(data); // Π½Π°ΠΉΠ΄Π΅ΠΌ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π² ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΠ΅ var $newContent = $data.find(elementContainerSelector); if ($newContent.length == 0) { // Π΅ΡΠ»ΠΈ ΡΠ»Π΅ΠΌΠ΅Π½Ρ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½, ΠΎΠ½ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΊΠΎΡΠ½Π΅Π²ΡΠΌ $newContent = $data.filter(elementContainerSelector); } // Π΅ΡΠ»ΠΈ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ, ΡΠΎ ΡΡΠΎ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎ ΠΎΡΠΈΠ±ΠΊΠ° if ($newContent.length == 0) { elementFindingErrorHandler(); } // ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ ΠΈΠ· ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΡ Π΄ΠΎΠΏΠΈΡΠ΅ΠΌ // ΠΊ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠΌΡ ΡΡΡΠ°Π½ΠΈΡΡ Π² ΠΎΠΊΠ½Π΅ $elementContainer.append($newContent.first().html()); // Π½Π°ΠΉΠ΄Π΅ΠΌ Π²ΡΠ±ΠΎΡ ΡΡΡΠ°Π½ΠΈΡ Π½Π° ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΡΡΠ°Π½ΠΈΡΠ΅ var $newPager = $data.find(pagerSelector); if ($newPager.length == 0) { // ΠΊΠΎΡΠ½Π΅Π²ΠΎΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΡΠΎΠΆΠ΅ ΠΏΠΎΠΈΡΠ΅ΠΌ $newPager = $data.filter(pagerSelector); } // Π·Π°ΠΌΠ΅Π½ΠΈΠΌ Π²ΡΠ±ΠΎΡ ΡΡΡΠ°Π½ΠΈΡ Π½ΠΎΠ²ΡΠΌ var newPager = $newPager.first().html(); // ΠΈ ΡΠΊΡΠΎΠ΅ΠΌ Π΅Π³ΠΎ ΠΏΠΎΠΊΠ° Π½Π΅ Π±ΡΠ΄Π΅Ρ Π½ΡΠΆΠ΅Π½ $(pagerSelector).html(newPager).hide(); }).always(function() { pageCounter += 1; // ΠΏΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΡΠ½ΠΎΠ²Π° ΡΠ»Π΅Π΄ΠΈΠΌ Π·Π° ΠΏΡΠΎΠΊΡΡΡΠΊΠΎΠΉ finished(); }); }; var eventTimeout = 0; var eventHandler = function() { // ΡΠΎΠ±ΡΡΠΈΠ΅ Π½Π° ΠΏΡΠΎΠΊΡΡΡΠΊΡ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΡΡΠ°Π±ΠΎΡΠ°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π· $window.unbind(eventName); clearTimeout(eventTimeout); eventTimeout = setTimeout(function() { // Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ Π½Π΅Π±ΠΎΠ»ΡΡΡΡ ΠΏΠ°ΡΠ·Ρ ΡΡΠΎΠ±Ρ Π½Π΅ ΡΡΠΈΡΠ°ΡΡ ΠΎΡΡΠ°Π²ΡΠΈΠ΅ΡΡ // ΠΏΠΈΠΊΡΠ΅Π»ΠΈ ΠΏΡΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΡΠΎΠ±ΡΡΠΈΠΈ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ eventProcessor(function() { // ΠΏΠΎΡΠ»Π΅ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΡ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΡΠ½ΠΎΠ²Π° ΠΆΠ΄Π΅ΠΌ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ $window.bind(eventName, eventHandler); }); }, 100); }; $(function() { $elementContainer = $(elementContainerSelector); // ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΡ Π΄ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° ΠΎΡ Π²Π΅ΡΡ
Π° ΡΠΊΡΠ°Π½Π° topOffset = $elementContainer.offset().top; // Π·Π°Π΄Π°Π΄ΠΈΠΌ ΠΏΡΠΎΠΏΠΎΡΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎ ΠΈΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΉ Π²ΡΡΠΎΡΠ΅ scrollBuffer = $elementContainer.height() * scrollBufferRatio; $window.bind(eventName, eventHandler); });
All this is better to call in the closure so as not to litter in the window
.