jQuery + плагины: сортировка и редактирование списка

Владимир | | Ajax, HTML, JavaScript, PHP, Web разработка.

jquery sortable

Довольно давно я написал статью о том, как с помощью библиотек Prototype и Scriptaculous добавить возможность редактирования и удаления записей обычному html списку (вообще-то это был цикл статей 1, 2, 3, 4, 5).

С тех пор несколько читателей просили доработать пример и добавить возможность сортировки записей.

Переделывать тот пример я не буду, т.к. Prototype сейчас практически не использую, лучше покажу, как решить задачу с помощью jQuery.

Сформулируем требования.

Необходимо создать html список с возможностями:

— изменение записей;
— удаление записей;
— изменение порядка записей.

Кроме того, необходимо предусмотреть возможность создания новых записей.

В общем, работать это будет примерно так.

Demo

Сразу же даю ссылку на архив с исходниками.

Source

Теперь разберём принцип работы.

На сегодняшний день сохранить данные html списка можно двумя способами.

1) На стороне сервера в БД. При этом, после каждого изменения (удаления, создания новой) записи будет отправляться запрос серверу.

2) В браузере, при условии, что он поддерживает HTML5 или есть доступ к какому-нибудь другому хранилищу. Этот способ работает быстрее и не требует постоянного подключения к интернету, но поддерживается не всеми браузерами. В частности, в IE работать не будет.

Поэтому я остановился на первом варианте.

Итак, наше приложение состоит из двух частей: клиентской и серверной.

Клиентская часть представляет собой HTML страницу и набор JS скриптов, которые отправляют запросы, позволяют сортировать список и т.п.

Серверная – это набор PHP скриптов, которые обрабатывают запросы и работают с базой данных. Я не использовал фреймворк в этом примере, поэтому получился набор отдельных скриптов под каждую операцию. При использовании фреймворка эти скрипты станут методами контроллера и, естественно, вы сможете использовать встроенную библиотеку для работы с БД.

Кстати, для этого примера я выбрал базу sqlite, но, т.к. доступ к ней осуществляется с помощью PDO, то вы легко сможете заменить её, например, на MySQL (достаточно будет изменить параметры подключения).

Подключение к БД

Выполняется скриптом db.php, сяостоящим всего из одной строки.

$dbh = new PDO('sqlite:data/data.sqlite');

Тут предполагается, что база находится в файле data.sqlite, расположенном в папке data.

Структура БД.

Т.к. задача у нас достаточно простая, мы обойдёмся одной таблицей с тремя полями:

id – первичный ключ;
note – текст записи;
note_order – порядковый номер записи в списке (с помощью этого поля осуществляется сортировка).

Создание страницы со списком

Выполняется скриптом index.php

<?php
try {
	//подключаемся к базе
	require_once('db.php');
	//получаем все записи
	$query = $dbh->query('SELECT * FROM notes ORDER BY note_order DESC');
	$result = $query->fetchAll();
	$dbh = null;
} catch(Exception $e) {
	$error = 'Ошибка: '.$e->getMessage();
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
	<title>jQuery Sortable Notes</title>
	
	<link rel="stylesheet" type="text/css" href="css/style.css" media="all" />
	
	<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
	<script type="text/javascript" src="js/jquery-ui-1.8.custom.min.js"></script>
	<script type="text/javascript" src="js/jquery.jeditable.mini.js"></script>
	<script type="text/javascript" src="js/jquery.json-2.2.min.js"></script>
	<script type="text/javascript" src="js/init.js"></script>
</head>
<body>
	<h1>Заметки</h1>
	<form action="addnew.php" method="post" id="addNewNote">
		<p>
		<input type="text" name="notetext" id="notetext" size="40" />
		<input type="submit" value="Добавить заметку" />
		</p>
	</form>
<?php
	//выводим сообщение об ошибке или формируем список записей
	if (isset($error)) {
		echo '<h2>'.$error.'</h2>';
	} else {
		if (count($result) > 0) {
			echo '<ul id="sortable">';
			foreach ($result as $row) {
				echo '<li id="note_'.$row['id'].'" class="editable"><span class="note" id="n_'.$row['id'].'">'.$row['note'].'</span><a href="#"><img src="images/delete.png" alt="Удалить" /></a></li>';
			}
			echo '</ul>';
?>
	<form id="changeOrder" method="post" action="changeorder.php">
		<p><input type="submit" value="Сохранить сортировку" /></p>
	</form>
<?php
		} else {
			echo '<h2>Записи отстутствуют</h2>';
		}
	}
?>
</body>
</html>

Прежде всего, мы подключаемся к базе и получаем список записей. Затем, формируем страницу. На странице размещаем:

— форму добавления новой записи;
— список записей (или сообщение о том, что они отсутствуют);
— форму обновления порядка записей.

Обратите внимание на разметку списка.

<ul id="sortable">
	<li id="note_58" class="editable">
		<span class="note" id="n_58">Заметка 1</span>
		<a href="#"><img src="images/delete.png" alt="Удалить" /></a>
	</li>
</ul>

В атрибутах id хранится значения поля id соответствующих записей в БД. Приставки note_ и n_ добавлены для совместимости со стандартом (значение атрибута id не может начинаться с цифры).

Каждый тег li содержит текст записи и ссылку «Удалить».

Сортировка списка

Для того, чтобы пользователь смог перемещать записи списка мы подключили библиотеку jQuery и плагин jQuery UI.

Сделать список сортируемым можно одной строчкой кода (в файле init.js)

$('#sortable').sortable();

Добавление новых записей

Рассмотрим скрипт addnote.php

try {
	//проверяем, пришел текст записи или нет
	if (!isset($_POST['notetext']) || '' == $_POST['notetext']) {
		throw new Exception('Не указан текст записи');
	}
	//подключаемся к БД
	require_once('db.php');
	//находим запись с максимальным значением в поле order
	$query = $dbh->query('SELECT note_order FROM notes ORDER BY note_order DESC LIMIT 1');
	$result = $query->fetchAll();
	if (0 == count($result)) {
		$order = 1;
	} else {
		$order = $result[0]['note_order'] + 1;
	}
	//вставляем новую запись, поле note_order новой записи будет на единицу больше,
	//чем у найденной записи
	$notetext = htmlspecialchars($_POST['notetext']);
	$dbh->exec('INSERT INTO notes (note, note_order) VALUES ('.$dbh->quote($notetext).', '.$order.')');
	$result = array();
	//получаем id созданной записи
	$result['id'] = $dbh->lastInsertId();
	$result['note'] = $notetext;
	$result['note_order'] = $order;
	$dbh = null;
	//отправляем результаты браузеру
	echo json_encode($result);
} catch(Exception $e) {
	echo json_encode(array('err'=>'Ошибка: '.$e->getMessage()));
}

Прежде чем добавлять в базу запись, нам нужно определиться с её номером сортировки. Форма находится над списком, поэтому будет логично, что запись появится сразу под ней, т.е. в начале списка.

Чтобы не создавать отрицательные номера сортировки, я решил выводить список в порядке убывания этих номеров (значений в поле note_order). Поэтому первая запись списка будет иметь максимальный номер.

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

Затем выполняем обычный запрос создания записи, получаем её id и отправляем его браузеру.

Теперь рассмотрим клиентскую часть.

Записи у нас будут создаваться с помощью AJAX запросов, поэтому назначаем форме обработчик события submit.

$('#addNewNote').submit(function() {
	var note = $('#notetext').val();
	if (note != '') {
		//отправка запроса
		$.post('addnew.php', {'notetext':note}, function(data) {
			var response = eval('('+data+')');
			if (response['err'] != null) {
				alert(response['err']);
			} else {
				var list = $('#sortable');
				//если список отсутствует
				if (list.length == 0) {
					//удаляем текст "Записи отстутствуют"
					$('h2').detach();
					//создаем список
					$('<ul id="sortable"></ul>').sortable().insertAfter($('#addNewNote'));
					//создаем форму "Сохранить сортировку"
					createChangeOrderForm().insertAfter($('ul#sortable'));
				}
				//вставляем новую запись в список
				createLi(response['id'], response['note']).prependTo('#sortable');
			}
		});
	} else {
		alert('Вы забыли ввести текст заметки');
	}
	return false;
});

В нём мы проверяем, заполнено ли поле с текстом записи, и отправляем AJAX запрос скрипту addnew.php.

После получения ответа сервера мы либо выводим сообщение об ошибке, либо добавляем новую запись. При добавлении записи проверяем, существует ли список вообще и, если нет, создаём его. Обратите внимание, что при создании списка мы делаем его сортируемым (метод sortable()).

Отдельно рассмотрим функцию создания элемента списка (тег li).

function createLi(id, note) {
	var note = $('<span class="note" id="n_' + id + '">' + note + '</span>').editable('update.php', editableOptions);
	var link = $('<a href="#"><img src="images/delete.png" alt="Удалить" /></a>').click(removeLi);
	return $('<li id="note_' + id + '" class="editable"></li>').append(note).append(link);
}

Здесь мы устанавливаем классы и id элементов и назначаем обработчики событий. Всего обработчиков два:

— обработчик двойного клика по тексту записи (он создаётся методом .editable, подробнее о нём чуть позже);

— обработчик клика по ссылке «Удалить» (позже мы его тоже подробно рассмотрим).

На этом я сегодня остановлюсь. Остальные функции мы рассмотрим в следующий раз.

Если есть вопросы или замечания, пишите, постараюсь ответить.

До встречи!

UPD. Дописал продолжение jQuery + плагины: сортировка и редактирование списка (часть вторая)