css, html, php, javascript, jQuery, ajax … – решения, примеры, рецепты
24 Янв
Внимание! Эта статья устарела. Новая статья по этой теме: «jQuery UI – плагин Droppable«. Библиотека jQuery предоставляет возможность не только легко и просто написать код, который позволит свободно перемещать элементы по странице. В ее состав, вернее в состав ее расширений входит функция droppable, которая позволяет также довольно просто написать код, который будет обрабатывать события, которые происходят в момент окончания перемещения элемента.
Что полезного с практической точки зрения можно сделать, воспользовавшись такой возможностью. Первое, что приходит в голову – это какая-нибудь интерактивная корзина для интернет-магазина. Вот и попробуем сделать такой пример.
Как всегда сначала попробуем испытать готовый пример, а затем разберемся как работает этот код. Берем мышкой нужный продукт и тащим его в корзину. Над корзиной отпускаем и смотрим, что получилось…
Чтобы не усложнять код примера, не применяются некоторые проверки, которые должны быть использованы в целях безопасности в реальных интернет-магазинах.
Вы можете сохранить исходный код примера и при наличии у Вас библиотеки jQuery, воспроизвести его на своем сайте.
Чтобы все это работало, нам потребуется подключить к нашей странице, в разделе HEAD довольно много файлов (за исключением самой библиотеки эти файлы не очень большие):
<script type="text/javascript" src="js/jquery-1.2.1.js"></script> <script type="text/javascript" src="js/jquery.dimensions.js"></script> <script type="text/javascript" src="js/ui.mouse.js"></script> <script type="text/javascript" src="js/ui.draggable.js"></script> <script type="text/javascript" src="js/ui.draggable.ext.js"></script> <script type="text/javascript" src="js/ui.droppable.js"></script> <script type="text/javascript" src="js/ui.droppable.ext.js"></script>
Большинство их уже знакомы по предыдущим примерам, добавились только ui.droppable.js и ui.droppable.ext.js, которые и обеспечат нам те возможности, которые мы собираемся рассмотреть.
Не будем очень уж внимательно рассматривать таблицу стилей, поскольку здесь в основном это дело вкуса. Остановлюсь только на ее отдельных моментах.
.products {
float:left;
}
#basket {
background-color:#e9b96e;
border:3px double #c17d11;
width:150px;
height:120px;
margin:10px;
padding:3px;
position:absolute;
top:5px;
right:5px;
}
В работе примера используются элементы с классом .products и идентификатором #basket (понятно, что products – это товары интернет-магазина, а basket – корзина). Никаких подводных камней тут нет, можно использовать любой стиль отображения этих элементов. Единственное, что необходимо учитывать – наш скрипт будет обращаться к этим элементам по имени класса и идентификатора.
Можно упомянуть еще два класса из таблицы стилей:
.droppable-active {
outline:1px dotted #F00;
}
.droppable-hover {
outline:1px dotted #00F;
}
Класс .droppable-active будет использован скриптом в тот момент, когда процесс перемещения элемента уже начался, но перемещаемый элемент еще не находится «в зоне действия» целевого элемента. А класс .droppable-hover будет применен, когда перемещаемый элемент окажется над целевым.
Сам HTML-код вообще не представляет собой ничего особенного: несколько картинок, целевой блок, блок вывода результатов. Все это Вы сможете посмотреть, если скачаете файл примера. А вот сам JavaScript, который все это обрабатывает, разберем подробнее. Код получился немножко длинноват, но очень хотелось сделать более-менее красивый пример, а не полуфабрикат…
<script type="text/javascript">
$(document).ready(function(){
// ---- DRAGGABLES & DROPPABLES -----
$("div.products").draggable({
helper: clone,
opacity: 0.5
});
$("#basket").droppable({
accept: ".products",
activeClass: "droppable-active",
hoverClass: "droppable-hover",
drop: function(ev, ui) {
var code = $(ui.draggable.element)
.find("img").attr("id");
if($(this).find("div[@id="+ code +"]")
.html()==null) {
var name = $(ui.draggable.element)
.find("img").attr("alt");
$(this).append("
<div class="basket" id=" + code + ">
<input type="text" class="qnt"/> " + name +
"<img src="droppable-delete.gif" class="delete"
alt="Удалить" /></div>
");
}
// *******************
$(".delete").click(function () {
$(this).parent().remove();
});
// *******************
// *******************
$("#buy").click(function () {
var databox = new Array();
$("#basket").find("input[@type=text]")
.each(function(){
var qnt = $(this).attr("value");
if(qnt===undefined) { qnt = 0; }
var text = $(this).parent().text();
databox[databox.length] = text + " - " + qnt;
});
data = databox.join(" кг, ");
$("#buyResult").html("Вы заказали:" + data +
" кгСпасибо за покупку!");
});
// ****************
}
});
// ------ DRAGGABLES & DROPPABLES ------
});
</script>
Как обычно используем $(document).ready(function(){…}); которая отслеживает момент готовности объектной модели документа (DOM). В нее «завернуто» все, что мы написали. И начнем разбираться дальше.
$("div.products").draggable({
helper: clone,
opacity: 0.5
});
Это уже должно быть немножко знакомо: выбираем набор элементов DIV, имеющих класс .products и делаем любой из них свободно перемещаемым, задавая при этом некоторые опции. helper: clone – при перемещении элемента он останется на месте, а перемещать с помошью мыши мы будем его клон, opacity: 0.5 – прозрачность которого во время перемещения будет 0,5.
Дальше буду приводить код небольшими кусочками, чтобы было легче разбираться.
$("#basket").droppable({
accept: ".products",
activeClass: "droppable-active",
hoverClass: "droppable-hover"
Выбираем элемент с идентификатором basket и делаем его droppable – туда будем «сбрасывать» перемещаемые элементы. Далее устанавливаем необходимые опции:
accept: «.products» – «сбросить» мы можем только элементы, имеющие класс products.
activeClass: «droppable-active» – имя класса, который будет применен к #basket во время перемещения любого из элементов с классом .products, но до того, как перемещаемый элемент окажется «в зоне действия» элемента #basket.
hoverClass: «droppable-hover» – все тоже самое, только класс будет применен тогда, когда перемещаемый элемент попадет в «зону действия» элемента #basket.
В опции drop – устанавливается функция, которая должна будет выполниться в момент, когда мы «сбросили» перемещаемый элемент. Простор для фантазии – обрабатывайте данные как угодно. Я кратко немного прокомментирую, что написано дальше.
var code = $(ui.draggable.element)
.find("img").attr("id");
Мы обращаемся к перемещаемому элементу, находим в нем (именно в нем) элемент img и получаем значение его атрибута id. Сохраняем в переменной code.
if($(this).find("div[@id="+ code +"]").html()==null)
Здесь будем проверять, нет ли уже в нашей корзине товара с таким же id. Для этого обратимся к элементу, который представляет собой нашу корзину (this в данном случае тоже самое, что и #basket), найдем элемент div с id равным id перемещаемого элемента (хранится в переменной code) и получим его HTML-код. Сравним с null. Если условие верное, значит такого продукта в корзине еще нет и мы можем выполнять следующие операторы, чтобы добавить товар в корзину.
var name = $(ui.draggable.element)
.find("img").attr("alt");
Обращаемся к перемещаемому элементу (это уже делали, правда?), ищем img, получаем значение атрибута alt, где у нас хранится название товара.
Теперь осталось только сформировать нужный HTML-код и добавить его в корзину, т.е. в элемент #basket.
$(this).append('<p class="basket" id=' + code + '>
<input class="qnt" type="text" /> ' + name +
'<img src="droppable-delete.gif" class="delete" alt="Удалить" />');</p>
Тут вроде бы все достаточно понятно. И осталось написать еще пару вспомогательных функций, чтобы пример был хоть немного действующим. Приводить их еще раз тут не буду (можете посмотреть их в полном коде), просто поясню, что первая обрабатывает клик мышкой на картинке удаления товара из корзины и собственно удаляет выбранный элемент div. А вторая обходит все имеющиеся текстовые поля ввода, собирает их значения в массив, объединяет массив в строку и выводит результат в виде HTML-кода в элемент #buyResult.
Отзывов (28) на «Пользовательские интерфейсы jQuery: перемещаемый и «сбрасываемый» контент.»
Спасибо большое за отличный пример! Я как раз делаю сборку дизайна в CMS-ке путем перетаскивания блоков на скелет шаблона, статья очень пригодилась! Спасибо!
Спасибо
Gennady, огромное спасибо за содержательный блог.
С английским языком проблемы, поэтому с изучением jQuery были проблемы.
С помощью Вашего блога надеюсь разобраться будет значительно проще.
Еще раз спасибо!
Спасибо большое! все получилось, единственное, не получается перетаскивать клон. «clone» почемуто выдает ошибку, фаербаг пишет:
clone is not defined
dragdrop()script.js (line 50)
handle()()jquery-1.2.6.js (line 2076)
(?)()()jquery-1.2.6.js (line 1858)
helper: clone,
Спасибо за статью.
Файлы draggable.ext.js, ui.droppable.ext.js избыточны.
И что-то круто поменяли в jquery 1.3.1 – у меня с новой библиотекой пример не пошёл – фрукты в корзине не захотели оставаться.
Marcus
19.01.2009 в 16:50
clone возьми в одинарные кавычки.
To Bumchik:
Это вполне естественно – посмотрите на дату статьи: прошло больше года. С версией 1.3.1 надо использовать файл ui.core.js и свежие версии drggable и droppable.
Их можно скачать http://ui.jquery.com/download
Удобно – настраиваемая закачка. Делаешь сам себе то, что нужно в одном файле.
спасибо Bumchik
Вынужден констатировать, что в новых библиотеках что-то поменялось таким образом, что теперь ui.draggable.element это не текущий перетаскиваемый элемент, а то-ли набор то-ли всегда первый элемент. Соответственно обработка события сброса элемента ( drop: function(ev, ui) {
var code = $(ui.draggable.element)…
)
теперь работает некорректно – всегда выбирается первый из фруктов.
Если кто-то разобрался с этой проблемой дайте решение (и очень желательно – объяснение).
См. коммент №7
[Gennady]
Именно после того как поменял во всех возможных комбинациях библиотеки написал свой коммент.
При чём всё как бы и нормально – компоненты перетаскиваются, drop обрабатывается. Но вот именно фишка с ui.draggable.element не пропадает.
Мои опыты:
jquery-1.2.1. + ui версии дпредставленными в примере работают.
jquery-1.2.6 + ui – 1.5.3. (всё взято одним комплектом с указанного сайта разработчика) – не работает ui.draggable.element
jquery-1.3 + ui – 1.6rc5. (всё взято одним комплектом с указанного сайта разработчика) – не работает ui.draggable.element.
Вот собственно сам пример:
Моделирование форм
$(document).ready(function(){
$(«.drag»).draggable({
helper: ‘clone’,
opacity: 0.5
});
$(«#drop»).droppable({
accept: «.drag»,
drop: function (ev, ui) {
alert(‘Id:’ + $(ui.draggable.element).find(«div»).attr(«id»));
}
});
})
В данном примере после сбрасывания квадрата всегда будет сообщение: «Id: 2″
Может я не все библиотеки подключаю?
html-код затёрся : Итак использованные библиотеки:
jquery-1.3.js
ui.core.js
ui.draggable.js
ui.droppable.js
Это потому, что нет уже ui.draggable
Есть ui.helper – объект jQuery, который и представляет перетаскиваемый элемент.
И еще – лучше все-таки использовать не 1.3, а 1.3.1 – там 23 бага исправлено.
Спасибо, Gennady!
Помогло!
Хочу осуществить следующую идею: изображения из одного блока при перетаскивании в другой блок клонируются и абсолютно позиционируются в нём. После этого клонированные изображения можно свободно перемещать внутри блока.
Всё работает нормально, но после первого перемещения внутри блока клонированное изображение начинает клонироваться вместо перемещения, как будто вызвано с опцией helper: ‘clone’. Ни у кого не было подобных проблем?
Надеюсь, я понятно объяснил?
Всё, сам решил проблему.
Тысяца извинений!
Использую библиотеки которые указаны в данной статье. Но функция each отказывается корректно работать. Он перебирает только первый элемент в корзине. Причем, если в корзине, допустим два элемента, то она перебирает 4 раза, а бывает и больше.
Подскажите, пожалуйста, как решить эту проблему.
Библиотека тут только одна – jQuery. Остальное – файлы расширений для нее, которые реализуют функциональность «перетаскивания» и «сбрасывания», но суть не в этом. Если используете точно те файлы, которые приведены в примере – все должно работать, и не стоит грешить на each() – лучше проверить свой код (согласитесь, пример на этой странице вполне работоспособен?).
Если же используете новую версию библиотеки – да, может и не работать. По той причине, что к соответствующей версии нужны и соответствующие версии расширений.
Все самое свежее и с настраиваемой закачкой http://jqueryui.com/download
Хорошо, приведу пример. Мне необходимо извлекать из тега значение атрибута alt.
Я интегрировал след. код:
var alt = $(«img.ext»).attr(«alt»);
alert(alt);
У первого элемента в корзине, alt равен слову Red. У второго элемента слово Orange. Но скрипт упрямо 4 (или больше) раза подряд выводит слово Red.
// *******************************
$(this).append(» » + name + «»);
}
// *******************************
$(«.delete»).click(function () {
$(this).parent().remove();
});
// *******************************
$(«#buy»).click(function () {
var databox = new Array();
$(«div.basket»).find(«input[@type=text]«).each(function(){
qnt = $(this).attr(«value»);
var id = $(«img.ext»).attr(«alt»);
alert(id);
…….
// *******************************
Работает все верно. Давайте немного разберем Ваш код.
Надо понимать следующее – метод each() работает с набором элементов, предоставленным селектором. В Вашем случае – это все элементы input типа text, которые находятся внутри элементов div с классом basket.
this внутри each() ссылается на текущий элемент из набора, поэтому в qnt например, каждый раз перезаписывается значение из текущего элемента.
В следующей строке, Вы при каждой итерации снова и снова заставляете свой код выбирать все элементы img класса ext на странице и получать содержимое атрибута alt, но каждый раз только для первого элемента в наборе.
Надеюсь, что объяснил более-менее понятно. Если что-то осталось неясным – напишите.
Gennady, подскажите тогда, пожалуйста, как сделать так, чтобы перебирался не только первый элемент, а все поочередно. Я как только уже не пробовал, не получается.
Я не знаю всей логики Вашего приложения, но если отталкиваться от того кода, что мы обсуждаем, можно воспользоваться методом add(), чтобы расширить тот набор элементов, который обрабатывается методом each(), но тогда внутри each(), нужно будет предусматривать дополнительную проверку – если элемент input, делаем что-то одно, если это img, делаем что-то другое. Примерно так:
$('div.basket') .find('input[@type=text]') .add('img.ext') .each(function(){ if($(this).attr('value')){ qnt = $(this).attr('value'); } else { alert($(this).attr('alt')); } }Только при добавлении add(‘img.ext’) внимательно подойдите к написанию самого селектора, т.е. в каком контексте Вам надо будет отыскать все эти img.ext
В общем я сделал так. В тег input вставил атрибут id. И теперь извлекаю данные таким образом:
qnt = $(this).attr(‘id’);
alert(qnt);
Теперь все работает, за один исключением.
Добавляю в корзину 2 элемента. А сообщение выводится 8 раз. Если добавить 1 элемент, то сообщение будет выводиться 4 раза.
Как это можно исправить?
Простите встряну в обсуждение. Такой вопрос. А если фруктов в примере будет много и и они помещены в DIV с overflow: scroll? В FF все работет как надо, а в IE клон появляется совсем не в том месте, где ожидалось.
Кто нибудь с таким багом встречался?
Здравствуйте.
Вопрос – а если мне нужно перемещать элементы ещё и внутри корзины, например менять их последовательность? Мне бы хотелось получить возможность не только добавлять перетаскиванием, но и иметь возможность перетащить из одной корзину в другую.
Значит надо писать еще обработчик перемещения элементов внутри корзины….
Статья-то уж больно старая – Вы бы посмотрели современные примеры на
http://jqueryui.com/demos/droppable/
Хорошо, спасибо за помощь!
Пожалуйста, объясните почему так происходит!
Если добавить alert(«hello») в функцию удаления, то в IE после удаление элемента n количество раз будет выскакивать hello (тоесть функция будет еще несколько раз срабатывать), а остальных браузерах все ок(удалил – функция один раз выдала алерт). Почему так срабатывает! мне это необходимо для подсчета минимального кол-ва элементов в корзине.
Оставьте отзыв