Динамическое меню на Ajax

сортировка меню

От автора: в данном уроке мы рассмотрим создание динамического меню, с помощью технологии AJAX. Если Вы работали с CMS WordPress, то, наверное, знакомы с такой штукой, как виджеты WordPress. В данном случае виджеты — это блоки, которые можно перемещать в режиме Drag and Drop («тащи и бросай»). При этом, после перемещения блока, он сохраняет свою позицию, т.е., к примеру, в сайдбаре сайта у нас имеется блок поиска, под которым размещен блок с произвольным текстом… мы захотели поменять их позиции… нет ничего проще — перетащили блок с поиском под блок с текстом и… вуаля — на сайте эти изменения расположения блоков вступили в силу. Согласитесь, динамическое меню — это быстро, удобно и эффектно.

План урока, по созданию динамического меню:

    1. Постановка задач. Создание БД (9:42)

    2. Выводим меню из БД (11:09)

    3. Реализуем сортировку без сохранения (11:48)

    4. Обращаемся к методу ajax (6:47)

    5. Реализуем сохранение сортировки (7:07)

    6. Логика работы скрипта (7:59)

    7. Добавляем юзабельности (15:38)

скачать исходникискачать урок

1. Постановка задач. Создание БД

Для начала сформулируем задачу. Вместо блоков у нас будет меню, пункты которого у нас и будут перетаскиваемыми блоками. Для чего вообще это нужно? Допустим, мы хотим изменить сортировку пунктов меню, т.е. сделать так, чтобы из БД пункты меню выводились не отсортированными по идентификатору пункта меню, а по, к примеру, полю position. Для этого, конечно же, можно написать несложный скрипт, который будет выводить текущую позицию каждого из пунктов меню в числовом виде (1, 2, 3 и т.д.). Далее мы сможем для конкретного пункта задать новую числовую позицию и сохранить изменения в БД. Но гораздо быстрее и красивее будет выглядеть реализация, когда мы просто возьмем пункт меню и перетащим его на новое место… все — он сохранит свою позицию.

Итак, мы создаем меню и через административную часть сайта сможем изменять позицию его пунктов. Давайте теперь определимся с тем, что нам понадобится. Прежде всего, нам понадобятся 2 библиотеки jQuery:

собственно сама библиотека jQuery;

библиотека jQuery User Inteface.

Взять их можно на офф сайте jQuery. Также Вы найдете эти скрипты в исходниках к уроку. Также нам понадобится файл стилей для оформления пунктов меню, который Вы можете сгенерировать на сайте jQuery в разделе jQueryUI или найти в исходниках к уроку (или написать стили для пунктов меню самостоятельно в зависимости от дизайна Вашего сайта).

У нас будет 2 файла. Первый файл (index.php) — это страница сайта для общего доступа. Второй файл (admin.php) — это страница админки, на которой мы сможем изменять позицию пунктов меню. Конечно же, доступ к админке должен быть ограничен (как это сделать Вы можете узнать из предыдущих уроков). Основное отличие файла admin.php состоит в том, что мы подключили в него 2 указанных скрипта:

<script src="scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>

В файл index.php эти библиотеки подключать не нужно.

Также в оба файла мы подключим файл стилей:

<link type="text/css" href="css/jquery-ui-1.8.14.custom.css" rel="stylesheet" />

И напишем несколько правил в стилях на странице index.php:

<style>
	#sortable { list-style-type: none; margin: 0; padding: 0; width: 200px; }
	#sortable li { margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 15px; cursor: pointer;}
	#sortable li span { position: absolute; margin-left: -1.3em; }
</style>

И на странице admin.php:

<style>
	#sortable { list-style-type: none; margin: 0; padding: 15px 40px 15px 0; width: 200px; }
	#sortable li { margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 15px; cursor:move}
	#sortable li span { position: absolute; margin-left: -1.3em; }
	.block{/*border:1px solid #ccc;*/ width:200px;}
</style>

Следующее, что нам понадобится — это БД, в которой будут храниться названия каждого из пунктов меню и их текущая позиция на сайте. Чтобы не создавать БД вручную, Вы можете импортировать дамп БД из исходников (файл test.sql).

БД я назову test и создам в ней таблицу sortable, для которой понадобится всего 3 поля:

id, тип INT, первичный ключ, автоинкремент;

name, тип VARCHAR, длина 255 символов;

position, тип INT.

Теперь занесем в созданную таблицу 3 тестовых пункта меню:

Пункт 1 с позицией 1;

Пункт 2 с позицией 2;

Пункт 3 с позицией 3;

Отлично! БД готова.

2. Выводим меню из БД

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

<?php

mysql_connect("localhost", "root", "") or die("No connect to server");
mysql_select_db("test") or die("No select DB");

?>

Теперь подключим этот файл в самом верху индексной и админской страницы:

<?php require_once 'db.php'; ?>

Отлично! Теперь выведем пункты меню на сайт. Для этого создадим заявку (запрос), которой выберем из таблицы sortable все, что там есть, и отсортируем выбранное по полю position в порядке возрастания (таким образом, пункты меню будут выведены традиционно — 1, 2, 3). С запросами мы уже также работали, поэтому ничего нового здесь быть для Вас не должно.

Открываем конструкцию PHP в том месте, где должно быть выведено меню и пишем код:

<?php

$res = mysql_query("SELECT * FROM `sortable` ORDER BY `position`") or die(mysql_error());
echo "<ul id='sortable'>\r\n";
while($row = mysql_fetch_assoc($res)){
	echo "<li id='{$row['id']}' class='ui-state-default'>{$row['name']}</li>\r\n";
}
echo "</ul>";

?>

Здесь мы выводим пункты меню списком. При этом стоит обратить внимание на то, что каждый из пунктов имеет свой идентификатор, значением которого является идентификатор конкретного поля в БД. Этот идентификатор нам понадобится для того, чтобы уникализировать каждый из пунктов меню.

Отлично!

Пункты меню выбраны из БД и появились на сайте.

3. Реализуем сортировку без сохранения

С файло index.php мы закончили и теперь можно перейти к админке. Для начала просто скопируем код вывода меню из индексного файла. Теперь, если посмотреть в браузере страницу admin.php, то увидим аналогичное меню за одним исключением — при наведении на пункты меню курсор приобретает крестовидную форму, что говорит о том, что данные пункты можно перетаскивать. Реализуется такая форма курсора правилом в стилях:

cursor:move

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

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		
	});
});
</script>

Все…

Теперь мы уже можем перетаскивать пункты меню. Это реализовано благодаря работе тех двух библиотек, которые мы подключили ранее. Здесь же мы просто взяли в набор jQuery элемент с идентификатором sortable и вызвали для него метод sortable. Если Вы не знакомы с основами jQuery, то рекомендую Вам обратиться к предыдущим урокам на сайте, где мы знакомились с основами работы jQuery. Сам метод sortable, к которому мы обратились, описан в библиотеке jQuery UI. Он имеет множество параметров, событий и методов, некоторыми из которых мы сейчас и воспользуемся. Прежде всего, давайте ограничим перемещение блоков только осью Y (только по вертикали). Для этого обратимся к опции axis:

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		axis: 'y'
	});
});
</script>

Теперь давайте уменьшим непрозрачность перетаскиваемого элемента до 50%:

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		axis: 'y',
		opacity: 0.5
	});
});
</script>

Отлично!

Теперь надо сделать так, чтобы мы могли получить текущую позицию каждого из элементов после того, как мы завершим перемещение. Для этого обратимся к событию stop и методу (функции) toArray:

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		axis: 'y',
		opacity: 0.5,
		stop: function(){
			var arr = $('#sortable').sortable("toArray");
			alert(arr);
		}
	});
});
</script>

Итак, что мы здесь сделали? После наступления события stop (когда завершено перетаскивание элемента) мы обращаемся к элементу с id sortable (это наше меню) и вызываем функцию toArray. Данная функция получает id каждого из отсортированных элементов и помещает его в массив в виде строки, где значения разделены запятой. Результат помещаем в переменную arr. Таким образом, если мы изменим изначальную позицию пунктов 1 и 2, то в переменной arr будет содержаться вот такая строка — 2, 1, 3. Для наглядности можно вывести методом alert значение переменной arr после каждого из перетаскиваний.

4. Обращаемся к методу ajax

Мы выполнили часть задачи — пункты меню можно теперь перетаскивать. Но они не сохраняют своей позиции в БД и после обновления страницы, все встает на круги своя. Для того, чтобы обновления были внесены в БД, необходимо передать значение переменной arr в скрипт PHP, который бы занес обновленные данные в БД. Сделать это, обратившись к методу jQuery под названием ajax(). Итак, пропишем его со всеми необходимыми нам параметрами:

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		axis: 'y',
		opacity: 0.5,
		stop: function(){
			var arr = $('#sortable').sortable("toArray");
			//alert(arr);
			$.ajax({
				url: 'save.php',
				type: 'POST',
				data: {masiv:arr},
				error: function(){
					$('#res').text("Ошибка!");
				},
				success: function(){
					$('#res').text("Сохранено!");
				}
			});
		}
	});
});
</script>

Пройдемся по параметрам:

url: указываем имя файла, который примет данные из скрипта (аналог атрибута action в форме);

type: тип передаваемых данных (аналог атрибута method в форме);

data: передаваемые данные в формате {переменная:значение};

error и success: события ошибки и успеха запроса соответственно.

Таким образом, мы передаем значение переменной masiv методом POST в файл save.php. В случае успеха в блок с идентификатором res будет добавлен текст "Сохранено!", в случае ошибки — «Ошибка!».

Все просто…

5. Реализуем сохранение сортировки

Осталось написать скрипт для файла save.php, который бы принимал данные. Создадим файл save.php и запишем в него следующий код:

<?php 

require_once 'db.php';

$pos_new = 1;
foreach($_POST['masiv'] as $item){
	$res = mysql_query("UPDATE `sortable` SET `position`='{$pos_new}' WHERE `id`='{$item}'");
	$pos_new++;
}

?>

Теперь давайте попробуем разобраться в работе того, что мы написали только что.

6. Логика работы скрипта

Поскольку мы принимаем массив данных — используем цикл foreach для того, чтобы пройтись по каждому из элементов массива. До начала цикла создадим переменную $pos_new со значением 1, которая будет новым значением позиции для каждого из пунктов меню. В теле цикла мы будем с каждым шагом цикла увеличивать значение переменной на единицу. Таким образом, ее значение последовательно будет изменяться от 1 до 3. Также в теле цикла указываем запрос, которым устанавливаем текущее значение переменной $pos_new значением поля position. Но делаем мы это для того поля, в котором значение поля id будет равно текущему значению перебираемого элемента массива, который мы получили. Здесь важно понять саму логику работы запроса. Поскольку мы передаем в массиве идентификаторы каждого из пунктов меню, то логично, что они будут соответствовать идентификаторам пунктов меню в БД. Именно поэтому для полученного идентификатора мы установим текущее значение переменной $pos_new в БД.

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

7. Добавляем юзабельности

Осталось добавить некоторые улучшения, которые сделают более приятной работу со скриптом. Например, мы можем после каждого сохранения только на время показывать текст "Сохранено!", а затем сделать так, чтобы он исчезал. Также сделаем так, чтобы место блока, который мы захватили, подсвечивалось:

<script type="text/javascript">
$(document).ready(function(){
	$('#sortable').sortable({
		axis: 'y',
		opacity: 0.5,
		placeholder: 'ui-state-default',
		containment: '.block',
		stop: function(){
			var arr = $('#sortable').sortable("toArray");
			//alert(arr);
			$.ajax({
				url: 'save.php',
				type: 'POST',
				data: {masiv:arr},
				error: function(){
					$('#res').text("Ошибка!");
				},
				success: function(){
					$('#res').show().text("Сохранено!").fadeOut(1000);
				}
			});
		}
	});
});
</script>

Теперь гораздо лучше. Вот и все. Наша задача реализована.

Заключение урока по созданию динамического меню для сайта

Пункты динамического меню после перетаскивания сохраняют свою позицию в БД. Стоит только напомнить о безопасности. В принципе, об этом я уже неоднократно говорил в предыдущих уроках, но напомню основное правило безопасности: все данные, принимаемые от пользователя, должны соответствующим образом быть обработаны. Здесь, конечно, в этом особого смысла нет, поскольку работа с БД осуществляется на уровне админки, но если бы мы принимали данные из пользовательской части, то каждый из элементов массива необходимо было бы привести к явному целочисленному типу функцией (int) или intval(), чтобы в запрос гарантированно попадало число.

На этом наш урок по созданию динамического меню завершен. Ваши вопросы и отзывы всегда будем рады видеть в комментариях. Удачи в Ваших разработках и до новых встреч!

Автор: Кудлай Андрей. Команда webformyself.

JavaScript&jQuery с нуля до профи

Пройдите пошаговый видеокурс по JavaScript&jQuery

Научиться

Метки:

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (32)

  1. Руслан

    Спасибо, как всегда пошаговая и подробная инструкция. Урок пригодится.

  2. Эдуард

    Очень интересно!
    Спасибо!!!
    Теперь не успокоюсь, пока где-нибудь не опробую!

  3. skrabus

    Спасибо за очень добротно сделанный и полезный урок.

  4. Любовь

    Понравилось, как всегда всё понятно, но пока ещё придётся разбираться, как я поняла.

  5. fortinbras

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

    • Андрей Кудлай

      Если не ошибаюсь, то в этом Вам поможет плагин WordPress Related Posts. Также можете посмотреть плагин WordPress Popular Posts и другие похожие.

    • Andrey Bernacki

      Да, как сказал Андрей, я использовал плагин WordPress Popular Posts, но не в читом виде, так как он картинки не выводит, а дописывал его немного, чтобы он выводил картинки. Картинки которые выводятся нужно делать миниатюрами – в эту сторону копать нужно, если хотите подобное сделать.

  6. Святослав

    Супер урок!!! Побольше бы таких про ajax!!!

  7. Сергей

    Спасибо за урок! Автору ОГРОМНЫЙ респект!!!

  8. Виктор

    О да.. Давно хотел сделать, но с аяксом не особа знаком.. Сделал сортировку вотографий в фотоальбоме, потом ужаснулся… Так низя :) Если в альбоме больше 100 фоток, представляете что будет? Сколько будет инсертов в БД внутри цикла?

    • Андрей Кудлай

      Логично :)
      Этот способ хорош для незначительного количества информации — блоки, пункты меню и т.п.

  9. Виктор

    Скажем так, раз они перемещаются, сохраняя позицию и без записи, то впринципе сабмит можно повесить на кнопку. Хотя наверное логичнее менять местами 2 цифры каждый раз.. Вот это было бы идеально. Где бы ещё пример реализации найти для ознакомления..

  10. Виктор

    Кстати, как бы проверить откуда пришёл запрос? Куда всавить переменну, или идентификатор, чтобы он и массиву не мешал и можно было проверить его наличие в скрипте сохраниения.

  11. Иван

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

    • Андрей Кудлай

      Пожалуйста.
      А алгоритм будет тем же. Только нужно передать еще ID родителя, чтобы в БД изменять порядок у соответствующих потомков. Тут все упирается только в вопрос построения древовидного меню. А в остальном — все также, сортировка то производится внутри родителя, т.е. внутри родителя меняется позиция дочерних пунктов и, соответственно, меняем значения позиции этих дочерних пунктов в БД.

      • Иван

        Спасибо за быстрый ответ, а что на счет jquery части?

        • Андрей Кудлай

          А что с частью jQuery? Не совсем понял этот вопрос… Подозреваю, что имелось в виду, как применить метод sortable именно к дочерним элементам текущего родителя… если так, то это не должно быть сложно. При построении меню каждый родитель будет иметь ID типа 1, 2 и т.д. Соответственно, остается получить эту цифру. Ну а метод sortable вызывается не для элемента с конкретным ID, а динамически. К примеру, таким образом — $(«#»+ID).sortable… ID в данном случае — это переменная, в которой получаем ID родителя…
          Как-то так выглядит общая схема реализации… дальше осталось попробовать насколько она реализуема.

          • Иван

            Спасибо, не понятно как реализовать перетаскивание из дочернего элемента в корневой и на оборот, так же как это работает в wordpress в произвольном меню.

          • Андрей Кудлай

            На нашем форуме можете выложить свои наработки кода и будем решать конкретно те моменты, которые вызывают затруднения.

  12. Мария

    Здравствуйте! Подскажите пожалуйста как сделать чтобы на конкретной странице в сайдбаре выводились подпункты меню, к которым относится данная категория или страница. Смысл в том, чтобы в главном меню отображался только первый уровень меню. А в сайдбаре — второй и третий

    • Андрей Кудлай

      Здравствуйте, Мария.
      Не совсем понял, что Вам нужно получить. Создайте тему на нашем форуме, как можно подробнее опишите задачу… если затрудняетесь с объяснением, то можно приложить рисунки или объяснить на конкретном примере.

  13. Артём

    Лучше использовать не событие «stop», а событие «update» ибо смысл сохранять в БД информацию если изменений не произошло, лишняя нагрузка. А событие «update» сработает только если пользователь двигал двигал и всёж передвинул а не вернул на место

    • Андрей Кудлай

      Согласен.
      На момент написания урока (2 года назад) в документации был пример именно с использованием события stop(), поэтому stop() использовался в уроке.

  14. Руслан

    Урок действительно помог, сам код в своей работе уже реализовывал. Но сильно не разбираясь всё, а тут всё по полочкам.
    Действительно на данный момент стоит задача реализовать создание динамического меню с большим количеством параметров, и главная идея в том, чтобы можно было перетаскивать пункты меню не только между собой, но и менять родителя простым перетаскиванием

  15. Алексей

    У меня что-то не получается сделать сортировку для элементов таблицы

    элемент 1
    элемент 2
    элемент 3
    элемент 4

    элемент5
    элемент 6
    элемент 7
    элемент 8

    $(«#sort-ell tr td»).sortable({

    });
    Сортировка происходит только когда обращаюсь к
    таблицы, а мне нужно сортировать . что я не правильно делаю

  16. Алексей

    Спасибо за урок! Прикрутил, получилось. Но вот… подвязать код к еще одному списку ну выходит (( файл save.php прописан только под конкретную таблицу. Не подскажете как можно сделать код универсальным, так чтобы не было привязки только к ‘sortable’? Заранее благодарен!

    • Андрей Кудлай

      Создайте для каждой из таблиц свою функцию и просто вызывайте нужную функцию для той или иной таблицы.

      • Алексей

        Вы имеете ввиду, в файле который принимает данные из скрипта (save.php)? А как мне в этом файле «словить» идентификатор места в котором я хочу произвести сортировку? Пробовал принимать GET страницы в которой хочу сортировать.. вот так:
        if ($_GET['module'] == ‘menu’){
        mysql_query («SELECT * FROM menu…
        }elseif($_GET['module'] == ‘calalog’)|{
        mysql_query («SELECT * FROM catalog…
        }
        чтобы посылать разные запросы, под каждую таблицу, но ничего не выходит.

        • Андрей Кудлай

          Функция не привязывается к конкретному идентификатору, функция — это в некоторой степени абстрактный код, который можно использовать многократно для различных элементов. Соответственно, можно описать функцию, которая будет работать с абстрактным элементом, а конкретный элемент уже подавать этой функции параметром при вызове.

  17. Влад

    Спасибо большое. очень полезная статья! надо опробовать)

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree