670
 

Реализация бесконечной прокрутки на jQuery

jQuery

Всем вам должны быть хорошо известны интерфейсы просмотра лент новостей, записей блогов, в которых новые записи подгружаются по мере чтения. Новые данные подгружаются в тот момент, когда полоса прокрутки достигает самого низа. Хорошим примером такого интерфейса является Google Reader, а также сайт DZone. Сегодня я покажу, как можно сделать такой интерфейс при помощи jQuery. Приложение будет имитировать работу блога, в котором при загрузке страницы, посредством AJAX будут загружаться первые 10 записей, а остальные будут подгружаться по мере чтения.



Начнем с создания простой HTML страницы, в теле которой имеется всего лишь один div – в него будут подгружаться записи. В этот div добавим элемент <div id=”loader”>, для того, чтобы использовать его как индикатора ожидания.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
	<script type="text/javascript" src="jquery.js"></script>

	<style type="text/css">
	#blog {border:1px solid #aaf;background:#f9f9f9;padding:4px}
	.post {border:1px solid #aaa;background:#fff;padding:4px;margin: 2px 0}
	#loading {height: 40px}
	</style>
</head>

<body id="hello_body">

	<div id="blog">
		<div id="loading" style="display:none">Loading....</div>
	</div>
</body>
</html>

Здесь также подключаем скрипт jquery.js, а также стили для блога и записей.

Далее переходим к созданию скрипта, вся функциональность которого будет заключена в объект engine:

var engine = {
	posts : [],
	target : null,
	busy : false,
	count : 5
}

В переменных объекта engine хранятся различные параметры работы скрипта – количество подгружаемых за один раз записей (count), элемент, в который эти записи должны добавляться (target), состояние скрипта (busy), и массив записей (post).

Инициализация скрипта будет производиться функцией init:

init : function(posts, target){
	if (!target)
		return;

	this.target = $(target);

	this.append(posts);

	var that = this;
	$(window).scroll(function(){
		if ($(document).height() - $(window).height() <= $(window).scrollTop() + 50) {
			that.scrollPosition = $(window).scrollTop();
			that.get();
		}
	});
}

Функция принимает два параметра - массив записей и элемент, в который записи должны быть добавлены. В конце функции добавляется обработчик события onscroll для объекта window. Его задача - определять положение полосы прокрутки, и если она находится в крайнем нижнем положении, то вызывать функцию подгрузки записей get(). Фунция get() отправляет AJAX запрос на сервер и, получая в ответ записи блога, добавляет их в элемент target:

get : function() {
	if (!this.target || this.busy) return;

	if (this.posts && this.posts.length) {
		var lastId = this.posts[this.posts.length-1].id;
	} else {
		var lastId = 0;
	}

	this.setBusy(true);

	var that = this;
	$.getJSON('getposts.php', {count:this.count, last:lastId},
		function(data){
			if (data.length > 0) {
				that.append(data);
			}
			that.setBusy(false);
		}
	);
}

Данные о всех записях, которые загружаются с сервера, хранятся в переменной engine.posts. Перед отправкой очередного запроса на сервер, функция get() получает id последнего элемента в этом списке (lastId), для того чтобы передать его в параметрах запроса. Это нужно для того, чтобы серверный скрипт знал, какие записи уже получены, и какие элементы нужно отправить.

Как в функции init(), так и в функции get(), для отображения записей используется функция append(), которая в связке с render() создает HTML код записи и добавляет его в целевой элемент (target):

append : function(posts){
	posts = (posts instanceof Array) ? posts : [];
	this.posts = this.posts.concat(posts);

	for (var i=0, len = posts.length; i < len; i++) {
		this.target.append(this.render(posts[i]));
	}

	if (this.scrollPosition !== undefined && this.scrollPosition !== null) {
		$(window).scrollTop(this.scrollPosition);
	}
},

render : function(obj){
	var xhtml = '<div class="post" id=post_'+obj.id+'>';
	if (obj.title) {
		xhtml += '<h2>'+obj.title+'</h2>';
	}
	if (obj.posted_at) {
		xhtml += '<div class="posted_at">Posted on: '+obj.posted_at+'</div>';
	}
	if (obj.comments_count) {
		xhtml += '<div class="comments_count">Comments: ' + obj.comments_count + '</div>';
	}
	xhtml += '<div class="content">' + obj.content + '</div>';
	xhtml += '</div>';

	return xhtml;
}

Еще стоит упомянуть одну небольшую деталь: в обработчике события onscroll я сохраняя позицию полосы прокрутки, чтобы потом, после добавления новых записей, восстановить её. Это нужно для того, чтобы положение полосы прокрутки не изменялось, при добавлении новых записей в конец списка:

// сохраняем значение в обработчике onscroll
that.scrollPosition = $(window).scrollTop();

.......

// восстанавливаем значение в функции append
if (this.scrollPosition !== undefined && this.scrollPosition !== null) {
	$(window).scrollTop(this.scrollPosition);
}

Теперь, после того, как скрипт создан, можно использовать его. Так же как и для большинства скриптов jQuery, добавляем код инициализации в фукнцию ready():

$(document).ready(function(){
	engine.init(null, $("#blog"));
	engine.get();
});

Вот пожалуй и все описание скрипта, как видите он совсем не сложный. Демо приложения можно посмотреть здесь: смотреть демо. Полностью скрипт доступен на github.

Добавить в закладки:
Maklay.com - Большой каталог товаров для спорта и активного отдыха

Комментарии на “Реализация бесконечной прокрутки на jQuery”

  1. [...] Реализация бесконечной прокрутки на jQuery New [...]

  2. Отличный скрипт, спасибо! Я бы, правда, заменил сакраментальное 50 в <= $(window).scrollTop() + 50 (при большой длине новостной ленты ползунок будет почти у самой нижней границы) на процент от высоты всего документа (например, можно взять 10% от $(document).height() ).

  3. @masster
    Да, хорошая идея.

  4. Автору:
    Поразительно, с какой любовью вы относитесь к своему делу и вашим четателям. Спасибо за множество отличных статей и наглядных примеров.

    Этот скрипт чуть перепишу под себя и использую на сайте. Как идея, кому то может пригодится:

    Если у вас выставлен длинный текст сразу после которого идут комментарии пользователей, читатель вводится в заблуждение о размере материала, пока его читает, ему кажется, что он еще на середине текста, а уже через 2 абзаца начнуться комментарии. Чтобы этого не происходило, комментарии можно подгружать аяксом, как описано в статье.

    Еще раз большое спасибо.

  5. Чудесно! Спасибо. Скажите, можно ли адаптировать этот скрипт к ситуации с горизонтальным скроллом?

  6. @Игорь
    Думаю можно, зависит от того как реализован контейнер со скролом. Нужно поменять логику определения позиции скролла и подгрузки нового содержимого.

  7. Серёжа Есюнин 30 марта 2010 в 21:36

    Непоянто

  8. Отличное решение многих проблем. Только, чтобы с полпинка разобраться, что к чему мозгов не хватает. Вот если бы Вы для примера, еще и код подгружаемых блоков привели, было бы супер. По крайней мере, если не получается, то уже будет более понятно где именно.
    С уважением…

  9. Однако, чтобы реализовать это и доработать до своих нужд, нужен достаточно высокий уровень. Я немного не так выразился в предыдущем комментарии. Нужны исходники getposts.php.
    Вообще не получается, как и что только не вставлял и не прописывал. Наверное весь интернет обшарил ничего нет, видимо это тайна, реализованная, только на сайтах гуру JavaScript’a.

  10. @Анатолий
    Я выложил исходник getposts.php здесь: http://www.jstoolbox.com/demo/getposts.html

  11. Вам бесконечное Спасибо, к реализации еще не приступил, но как это устроено разобрался, более того, этот пример пролил свет на мои познания как в JS, так и PHP. Еще раз спасибо.

Оставить комментарий

JSToolbox создан на основе WordPress