Все это drag’n’drop!

Такой простой и эффектный метод работы с объектами пользовательского интерфейса, как перетаскивание их мышкой, давно используется веб-программистами. До настоящего времени наиболее удачно данный эффект реализуется посредством JavaScript-фрэмворков, таких как jQuery или ExtJS, или ручного манипулирования DOM-объектами.

В стандарт HTML5 поведение drag’n’drop включено изначально. Реализуется оно новым атрибутом draggable и рядом событий на каждый этап действий по перемещению объектов. Всего их семь:

— dragstart — событие начала перетаскивания объекта;

— drag — перемещение объекта;

— dragenter — событие вызывается, когда перетаскиваемый объект попадает на объект-приемник;

— dragleave — перетаскиваемый объект покидает объект-приемник;

— dragover — событие вызывается во время перемещения перетаскиваемого объекта над объектом-приемником;

— drop — событие вызывается, когда перемещаемый объект попадает на объект-приемник и пользователь отпускает кнопку мыши;

— dragend — пользователь перестает перетаскивать объект.

Это все, но давайте с этим набором попробуем сотворить что-нибудь полезное.

Прежде всего нам нужны визуальные объекты, а значит, html-разметка и сопоставленные ей стили. Создадим объекты:

<style>

.column { height: 36px: width: 36px: float: left:

Border: 2px solid #6666ff: background-color: #ccc; margin: 5px: border-radius: 10px; text-align: center;

}

.tr { height: 180px; width: 40px:

Border: 1px solid black; background-color: #ddd; margin-top: 120px;

Border-radius:

10px;

1

</style>

<div

Id="columns">

<div

N

M

U

Ol

C

S=

S

A

Cl

>1</div>

<div

N

M

U

Ol

C

S=

S

A

Cl

>2</div>

<div

Class="column

>3</div>

<div

Class="column

>4</div>

<div

Class="column

>5</div>

<div

Class="column

>6</div>

<div

Class="column

>7</div>

</div>

<div class="tr"> </div>

Результат можно посмотреть на рис. 78.

Наша задача — обеспечить возможность перемещать квадратики с цифрами внутрь большого прямоугольника в произвольном порядке (возможно, это будет что-нибудь вроде игры или головоломки). Начнем с того, что обеспечим саму возможность перетаскивать объекты мышкой (здесь и далее мы будем использовать javascript — фрэймворк jQuery — опять исключительно для сокращения объема кода):

<script src="Https://ajax. googleapis. com/ajax/libs/jquery/1.7.0/jquery. min. js"></ script>

<script>

$(document).ready(function () {

$(".column").attr(‘draggable’, ‘true’);

}

</script>

Такой же эффект даст <div class=”column” draggable=true>.

Рис. 78. Все готово к drag-n-drop

Теперь плашки с цифрами можно двигать мышкой (рис. 79), а в случае использования сенсорного монитора — пальцами (к слову сказать, свойство draggable установлено по умолчанию для таких объектов, как изображение или гиперссылка).

Рис. 79. Начинаем перетаскивание

Не будем останавливаться на достигнутом — будем двигаться к цели, используя вышеописанную событийную модель. Сначала займемся началом перемещения:

$(".column").each(function(){

This. add EventListener(‘dragstart’, handleDragStart, false); });

Пока просто сделаем перемещаемый объект полупрозрачным:

Function handleDragStart(e) { this. style. opacity = ‘0.4’; return false;

}

Теперь обозначим целевую область перемещения. У нас это div с id "tr". Снабдим его обработчиком событий onDragenter и onDagleave:

$(".tr").each(function(){

This. addEventListener(‘dragenter’, handleDragEnter, false); this. addEventListener(‘dragleave’, handleDragLeave, false); });

Теперь создадим класс over, определяющий целевую область в момент нахождения над ней перетаскиваемого объекта (ее необходимо «подсветить» просто для удобства):

. over {

Border: 2px dashed #000:

}

Очень простая: var inCont = 0; function handleDragEnter(e) { $(this).addClass(‘over’); return false; inCont = 1;

}

Function handleDragLeave(e) { $(this).removeClass(‘over’); inCont = 0;

}

Использование глобальной переменной inCont (это флаг, означающий нахождение перетаскиваемого объекта над объектом-приемником, он нам понадобится в дальнейшем) — конечно, не лучшее решение, но сейчас для нас важен не стиль JavaScript-программирования.

Теперь перемещение визуально оформлено (рис. 80), но пока от этого не очень много толку, нужно реальное перемещение.

Рис. 80. Обозначим целевую область

Тут нам на помощь придет новый объект dataTransfer, который хранит данные от перетаскиваемого объекта. Для его использования чуть-чуть модифицируем handleDragStar:

Function handleDragStart(e) {

E. dataTransfer. effectAllowed = ‘move’; e. dataTransfer. setData(‘text/html’, this. outerHTML); this. style. opacity = ‘0.4’; return false:

}

Теперь в dataTransfer у нас хранится код перетаскиваемого объекта. Сейчас нужно обработать событие drop:

$(».tr»).each(function(){

This. addEventListener(‘drop’, handleDrop, false); });

Function handleDrop(e) {

Var obj = e. dataTransfer. getData(‘text/html’); $(this).append(obj);

$(this).removeClass(‘over’);

}

И добавим обработку окончания перетаскивания:

$(".column").each(function(){

This. add EventListener(‘dragstart’, handleDragStart, false); this. addEventListener(‘dragend’, handleDragEnd, false);

});

Function handleDragEnd(e) {

E. srcElement. style. opacity = ‘1.0’; if(inCont == 1){

$(e. srcElement).remove(); inCont = 0;

}

$(this).removeClass(‘over’);

}

Собственно, все. Правда, скорее всего, ничего не работает, но это мы сейчас исправим. Дело в том, что в большинстве браузеров реализовано поведение при прекращении перетаскивания объекта по умолчанию (например, картинка, перетянутая с рабочего стола, раскроется, а div draggable вернется на прежнее место). Исправим это, используя preventDefault():

$(".tr").each(function(){

This. addEventListener(‘dragover’, handleDragOver, false); this. addEventListener(‘drop’, handleDrop, false);

});

Function handleDragOver(e) { if (e. preventDefault) { e. preventDefault():

}

E. dataTransfer. dropEffect = ‘move’;

Теперь объекты перемещаются (рис. 81), самое время задаться вопросом: зачем вообще все это, если с jQuery, MooTools или ExtJs все гораздо проще и красивее?

Рис. 81. Drag’n’drop в действии

Дело в том, что drag’n’drop, осуществляемый средствами атрибутов style и position DOM-элемента (а именно его используют вышеперечисленные библиотеки), и только что реализованное нами поведение имеют принципиальную разницу. То, что сделали мы, — это настоящее перемещение с задействованием буфера обмена, а не имитация его посредством стилей. Чтобы убедиться в этом, проведем небольшое испытание. Сначала чуть изменим handleDragEnd:

Function handleDragEnd(e) {

E. srcElement. style. opacity = ‘1.0’; if((inCont == 1)|| (e. x < 0)){

$(e. srcElement).remove():

Таким образом, мы добавили случай выхода за пределы браузера (правда, только с одной стороны, ну это исправимо). Теперь откроем наш пример в двух окнах браузера и попробуем перетащить плашки с одного на другое. У меня получилось (рис. 82), для наглядности я сделал копию странички с другим классом для плашек.

Рис. 82. Перетаскиваем объекты между разными окнами!

Чтобы дать окончательное понимание «подлинного» drag’n’drop и возможностей объектива dataTransfer, попробуем реализовать пример, посвященный FileAPI, через перетаскивание файлов мышкой. Для этого перепишем функцию handleDrop:

Function handleDrop(e) {

Files = e. dataTransfer. files; var reader = new FileReader(); reader. onload=function(e){ var span = document. createElement(‘span’);

Span. innerHTML = [‘<img class=”icon” src="’, e. target. result, title=’", files[0].name,

‘”/>’].join(»);

Document. get ElementById(‘gallery’).insert Before(span, null);

}

Reader. FeadAsDataURL(files[0]):

Теперь можно грузить изображения мышкой (рис. 83, я убрал плашки и добавил класс icon и div id="gallery"). Этот скрипт легко модифицировать для мультизагрузки, но это я оставлю читателю в качестве домашнего задания. Сделаем еще только одно действие — добавим загрузку этих изображений на сервер (иначе наша галерея исчезнет с нажатием кнопки F5):

Function upload(file) {

Var reader = new SileReader(); reader. onload = function() { var xhr = new XMLHttpRequest(); xhr. onreadystatechange = function () { if (this. readyState == 4) { if (this. status == 200) { alert("3arpy3Ka завершена!") return 1;

} else {

Alert’Oira! "); return 0;

}

}

};

Xhr. open("POST", "/upload. php"); var boundary = "testtest";

Xhr. setRequestHeader("Content-Type", "multipart/form-data, boundary=" + boundary);

Var body = "—" + boundary + "rn";

Body += "Content-Disposition: form-data; name=’myFile’; filename=’" + file. name + "’rn";

Рис. 83. Перетаскиваем картинки из проводника Windows

Body += "Content-Type: application/octet-streamrnrn"; body += reader. result + "rn"; body += "— " + boundary + "—"; xhr. send(body);

};

Reader. readAsBinaryString(file):

}

Эту функцию вызываем, например, здесь:

Function handleDrop(e) {

Files = e. dataTransfer. files; if(upload(file[0]) {

Ну а как реализовать upload. php, я думаю, объяснять нет необходимости.

Все — визуальный загрузчик готов, а мы продолжим наши странствия по HTML5 в следующей главе.

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

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