Advertisement
  1. Web Design
  2. HTML/CSS
  3. HTML

Использование функции "Перетащи и Брось" в HTML5

Scroll to top
Read Time: 14 min

() translation by (you can also view the original English article)

Одной из новых функций в HTML5 является встроенный функционал "Перетащи и Брось". Удивительно, но Internet Explorer поддерживает встроенную функцию "Перетащи и Брось" начиная с версии 5.5; Фактически HTML5 основан на поддержке IE. В сегодняшнем уроке мы рассмотрим то, как реализовать нативный функционал "Перетащи и Брось" чтобы создать простой интерфейс корзины покупателя.


Шаг 0. Что мы собираемся сделать

Вот что мы собираемся создать: простая корзина покупателя с панелью товаров и панелью для корзины. Чтобы "купить" продукт, вы можете перетащить его с панели на значок корзинки; мы будем отслеживать количество покупок и удалять товары из панели, когда их нет в наличии.

Обратите внимание, что мы на самом деле не создаем полнофункциональную корзину покупателя; сегодня мы не будем работать с серверной частью. Это будет только интерфейс; наша цель - функционал "Перетащи и Брось" с помощью HTML5.


Шаг 1. HTML

Конечно, мы начнем с HTML кода; вот наш каркас:

1
 
2
<!DOCTYPE html> 
3
<html> 
4
<head> 
5
    <meta charset="utf-8" /> 
6
    <title>Drag and Drop Shopping Cart</title> 
7
    <link rel="stylesheet" href="default.css" /> 
8
</head> 
9
<body> 
10
 
11
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
12
    <script src="jquery.ndd.js"></script> 
13
    <script src="dragdrop.js"></script> 
14
</body> 
15
</html>

Все довольно просто: мы добавляем ссылки на таблицу стилей и jQuery; мы будем использовать jQuery только для удобной обработки событий и манипуляций с DOM; функционал "Перетащи и Брось" будет нативным. Однако мы можем столкнуться с трудностями, потому что функционал "Перетащи и Брось" в HTML5 добавляет несколько свойств к объекту события. А так как JQuery не использует объект события по-умолчанию; он создает свой собственный эквивалент. И из-за этого мы не можем получить доступ к специальным свойствам с помощью jQuery. Но не беспокойтесь; для этого есть специальный плагин; мы подключаем плагин Native Drag and Drop, чтобы все это работало. И, наконец, мы подключаем наш скрипт: dragdrop.js.

Теперь мы готовы добавить наш список товаров; в качестве изображений товаров я использую значки из Apple Icon Superpack, созданные SvenVath. (Я переименовал значки упростив имена и изменил их размер до 180 пикселей.)

Добавьте ul #products в качестве первого элемента для тега body. Как только вы это сделаете, мы рассмотрим первый элемент списка:

1
 
2
<li><a class="item" href="#" id="imac" draggable="true"> 
3
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/imac.png" /> 
4
    <div> 
5
        <p><strong>iMac</strong></p> 
6
        <p><strong>Price</strong>: <span>$1199.00</span></p> 
7
        <p><strong>Quantity</strong>: <span>10</span></p> 
8
    </div> 
9
</a></li>

Мы завершаем элемент списка якорем внутри; обратите внимание, что каждый элемент имеет класс элемента (это будет важно, когда мы перейдем к CSS) и пользовательский идентификатор (это будет важно, когда мы перейдем к JavaScript). Также, якорь имеет атрибут draggable = "true"; это все, что нам нужно, чтобы сделать элемент перетаскиваемым (мы скоро обнаружим несколько предостережений). Так как мы используем тег якоря, вы должны сделать что-то для браузеров без встроенной поддержки перетаскивания (хотя мы и не будем этого делать здесь). Затем у нас есть изображение продукта и слой с информацией о продукте. И да, необходимо оформить цену и значение количества с помощью тега span

Вот остальные элементы списка:

1
 
2
<li><a class="item" href="#" id="iphone" draggable="true"> 
3
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/iphone.png" /> 
4
    <div> 
5
        <p><strong>iPhone</strong></p> 
6
        <p><strong>Price</strong>: <span>$199.00</span></p> 
7
        <p><strong>Quantity</strong>: <span>16</span></p> 
8
    </div> 
9
</a></li> 
10
<li><a class="item" href="#" id="appletv" draggable="true"> 
11
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/appletv.png" /> 
12
    <div> 
13
        <p><strong>AppleTV</strong></p> 
14
        <p><strong>Price</strong>: <span>$299.00</span></p> 
15
        <p><strong>Quantity</strong>: <span>9</span></p> 
16
    </div> 
17
</a></li> 
18
<li><a class="item" href="#" id="dislpay" draggable="true"> 
19
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/display.png" /> 
20
    <div> 
21
        <p><strong>Cinema Display</strong></p> 
22
        <p><strong>Price</strong>: <span>$899.00</span></p> 
23
        <p><strong>Quantity</strong>: <span>4</span></p> 
24
    </div> 
25
</a></li> 
26
<li><a class="item" href="#" id="nano" draggable="true"> 
27
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/nano.png" /> 
28
    <div> 
29
        <p><strong>iPod Nano</strong></p> 
30
        <p><strong>Price</strong>: <span>$149.00</span></p> 
31
        <p><strong>Quantity</strong>: <span>20</span></p> 
32
    </div> 
33
</a></li> 
34
<li><a class="item" href="#" id="macbook" draggable="true"> 
35
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/macbook.png" /> 
36
    <div> 
37
        <p><strong>Macbook</strong></p> 
38
        <p><strong>Price</strong>: <span>$1199.00</span></p> 
39
        <p><strong>Quantity</strong>: <span>13</span></p> 
40
    </div> 
41
</a></li> 
42
<li><a class="item" href="#" id="mini" draggable="true"> 
43
    <img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/064_drag_drop_html5/images/mini.png" /> 
44
    <div> 
45
        <p><strong>Mac Mini</strong></p> 
46
        <p><strong>Price</strong>: <span>$599.00</span></p> 
47
        <p><strong>Quantity</strong>: <span>18</span></p> 
48
    </div> 
49
</a></li>

Вот финальный вариант HTML: корзина для покупок:

1
 
2
<div id="cart"> 
3
    <h1>Shopping Cart</h1> 
4
    <ul></ul> 
5
    <p id="total"><strong>Total:</strong> $<span>0.00</span></p> 
6
    <hr /><h2>Drop here to add to cart</h2> 
7
</div>

И это наш весь код HTML!


Полный креатив



Шаг 2. CSS

В идеале, все, что вам нужно сделать, чтобы сделать элемент перетаскиваемым, установить значение для атрибута draggable true; тем не менее, есть еще кое-что для этого. Чтобы все работало правильно, вы должны установить несколько вещей с помощью CSS. Во-первых, подумайте об этом: что происходит при нажатии и перетаскивании "нормального" (неперетаскиваемого) элемента? Обычно происходит выделение текста. Также мы хотим убедиться, что мы перетаскиваем элемент, а не только его содержимое. Чтобы справиться с этим, необходимо использовать следующий код CSS:

1
 
2
[draggable=true] { 
3
    -moz-user-select:none; 
4
    -webkit-user-select: none; 
5
    -webkit-user-drag: element; 
6
}

Для нашего удобства встроенный плагин drag-drop, который мы используем, устанавливает эти свойства для нас, поэтому мы можем не менять его, если хотим. Однако мы сделаем это:

1
 
2
[draggable=true] { 
3
    cursor : move; 
4
}

Давайте начнем работать над оформлением:

1
 
2
html { 
3
    height:100%; 
4
} 
5
body { 
6
    background: #ececec; 
7
    margin:0; 
8
    padding:0; 
9
    font: 13px/1.5 helvetica, arial, san-serif; 
10
    height: 100%; 
11
} 
12
h1, h2 { text-align:center; } 
13
h2 { 
14
    position:absolute; 
15
    bottom: 20px; 
16
    color:#fff; 
17
    text-shadow:0 0 10px rgba(0, 0, 0, 0.75); 
18
    display:none; 
19
} 
20
p { margin:0; }

Поскольку мы не используем полный сброс стиля, вот наш псевдо-сброс. Все должно быть понятно. Мы устанавливаем высоту на знание 100% для элементов html и body, потому что мы хотим, чтобы #cart был на всю высоту экрана; для этого каждый родительский элемент должен иметь высоту, равную 100%. Также обратите внимание, что мы используем rgba для установки цвета тени; если браузер не поддерживает это, для элемента h2 не будет тени. И мы скроем этот h2, установив значение display: none. Помните, что h2 говорит "Бросьте сюда, чтобы добавить в корзину", так что мы должны будем его плавно скрыть, когда перетаскивание начнется и полностью убрать, когда закончится перетаскивание.

Переходим к нашему списку товаров…

1
 
2
#products { 
3
    float:left; 
4
    list-style:none; 
5
    width:65%; 
6
    padding:0; 
7
} 
8
#products li { 
9
    display:inline; 
10
}

Опять, тут всё понятно. Важным в этом фрагменте является то, что элементы списка будут отображаться в одну строчку. Поскольку мы установим свойства display: block; float: left для якорей, IE добавит к элементам списка эффект «лестничных ступеней»; мы можем обойти эту ошибку, установив свойство display: inline для родительского элемента.

Говоря о якорях, давайте продолжим их оформлять дальше.

1
 
2
.item { 
3
    display:block; 
4
    float:left; 
5
    width:180px; 
6
    height:180px; 
7
    margin:10px; 
8
    border:1px solid #494949; 
9
    text-align:center; 
10
    text-decoration:none; 
11
    color: #000; 
12
    overflow:hidden; 
13
} 
14
.item img { 
15
    border:0; 
16
    margin:10px auto; 
17
    width:160px; 
18
    height:160px; 
19
} 
20
.item div { 
21
    background:rgb(0, 0, 0); 
22
    background: rgba(0, 0, 0, 0.5); 
23
    position:relative; 
24
    bottom:69px; 
25
    color:#f3f3f3; 
26
    padding:5px 0; 
27
    display:none; 
28
}

Каждый якорь будет оформлен как блок 180x180px; он будет заполнен изображением продукта. Слой с информацией о товаре будет размещён над изображением в нижней части квадрата. Обратите внимание, что мы должны установить цвет фона, а затем сбросить его для современных браузеров, потому что IE не поддерживает RGBa. Мы устанавливаем display: none для информационного слоя, чтобы мы могли его плавно скрыть и показать, когда "покупатель" наводит курсор и убирает его, соответственно.

Все, что осталось оформить, это корзина для покупок; мы рассмотрим её здесь, но, помните, элементы списка будут добавлены с помощью jQuery позже, так что вы пока не увидите, как это происходит.

1
 
2
#cart { 
3
    float:right; 
4
    background-color:#ccc; 
5
    width:25%; 
6
    padding:0 5%; 
7
    height:100%; 
8
} 
9
#cart ul { 
10
    padding:0; 
11
} 
12
#cart li { 
13
    list-style:none; 
14
    border-bottom:1px solid #494949; 
15
    padding:5px; 
16
} 
17
#cart .quantity { 
18
    font-weight:bold; 
19
    padding-right:10px; 
20
    margin-right:10px; 
21
    border-right:1px solid #494949; 
22
    display:inline-block; 
23
    width:15px; 
24
    text-align:right; 
25
} 
26
#cart .price { 
27
    float:right; 
28
} 
29
#total { 
30
    float:right; 
31
}

Элементы с классами оформления quantity и price будут находиться внутри динамически вставленных элементов списка.

Это для CSS; прежде чем перейти к звезде этого шоу, давайте посмотрим на проделанную работу.



Шаг 3. JavaScript

Мы добрались до JavaScript; согласно HTML5 doctor:

HTML 5 DnD основан на первоначальной реализации Microsoft, которая была доступна уже в Internet Explorer 5! В настоящее время поддерживается в IE, Firefox 3.5 и Safari 4.

Вот список событий, которые предлагает "Перетащи и Брось" в HTML5:

  • drag
  • dragstart
  • dragover
  • dragenter
  • dragleave
  • dragend
  • drop

Мы не будем использовать их все, но посмотрим, как они работают.

Сначала, мы поработаем с нашими продуктами:

1
 
2
$('.item') 
3
    .bind('dragstart', function (evt) { 
4
        evt.dataTransfer.setData('text', this.id); 
5
        $('h2').fadeIn('fast'); 
6
    }) 
7
    .hover( 
8
        function () { $('div', this).fadeIn(); },  
9
        function () { $('div', this).fadeOut(); } 
10
    );

Мы начинаем с выбора всех объектов; затем мы привяжем функцию к событию dragstart; это событие срабатывает, когда мы начнем перетаскивать объект. Первое, что мы будем делать при перетаскивании объекта, - это установим некоторые данные; на самом деле, если данные не установлены, firefox не позволит элементу перемещаться. Специальное перетаскивание событий - это свойство объекта в объекте события, называется dataTransfer; мы будем использовать два метода этого свойства: setData и getData. Здесь мы используем метод setData, который принимает два параметра: формат данных и данные. Мы будем использовать тип данных 'text.' Затем мы установим данные идентификатора элемента, который пользователь перетаскивает. Затем мы плавно покажем элемент h2 в качестве подсказки для клиента.

Мы также используем метод наведения jQuery для постепенного появления информации о продукте, когда мы наводим указатель мыши и исчезновения, когда мы отводим указатель мыши. Обратите внимание, что мы передаем узел, который мы зависаем как контекст, поэтому мы получаем только слой соответствующего продукта.

Теперь добавим обработчики событий для #cart. Мы будем взаимодействовать с событиями dragover, dragenter и drop:

1
 
2
$('#cart') 
3
    .bind('dragover', function (evt) { 
4
        evt.preventDefault(); 
5
    }) 
6
    .bind('dragenter', function (evt) { 
7
        evt.preventDefault(); 
8
    }) 
9
    .bind('drop', function (evt) { 
10
 
11
    });

Чтобы запустить событие drop, нам нужно отменить действие по умолчанию для события dragover; это событие непрерывно срабатывает при достижении цели перетаскивания, когда перетаскиваемый элемент перемещается по ней. Только для IE нам нужно отменить действие по умолчанию на dragenter event, которое происходит только тогда, когда элемент перетаскивания входит в целевую точку. Причины отмены действия по умолчанию несколько не конкретные; если честно, я их не понимаю. Вот что говорит Remy Sharp:

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

Я должен отметить, что jQuery немного помогает нам здесь; обычно мы также должны возвращать значение return false, чтобы он работал в IE; однако исправленное предупреждение jQuery preventDefault делает это для нас.

Теперь событие drop; это событие также запускается для целевого объекта, которым является слой div#cart в нашем случае. Прежде чем мы посмотрим на функцию, давайте поговорим о том, что должна делать эта функция:

  • получить продукт, который мы бросили
  • если продукт уже куплен, добавить его к количеству купленного; в противном случае добавить товар в корзину
  • уменьшение количества продукта
  • обновить общую стоимость

Вот первая часть:

1
 
2
var id = evt.dataTransfer.getData('text'), 
3
    item = $('#' + id), 
4
    cartList = $("#cart ul"), 
5
    total = $("#total span"), 
6
    price = $('p:eq(1) span', item).text(), 
7
    prevCartItem = null, 
8
    notInCart = (function () { 
9
        var lis = $('li', cartList), 
10
            len = lis.length, 
11
            i; 
12
 
13
        for (i = 0; i < len; i++ ) { 
14
            var temp = $(lis[i]); 
15
            if (temp.data("id") === id) { 
16
                prevCartItem = temp; 
17
                return false; 
18
            } 
19
        } 
20
        return true; 
21
    } ()), 
22
    quantLeftEl, quantBoughtEl, quantLeft;

Я знаю; здесь много переменных, но мы будем использовать их все. Давайте разберёмся; во-первых, мы получаем текстовые данные, которые мы передали с событием; мы передали id таким образом, потому что в объекте события нет ничего, что сообщает нам, какой элемент был сброшен на нашу цель; мы получим идентификатор, а затем будем использовать его, чтобы найти элемент, который был перенесён. Далее мы получаем список корзины и блок с ценой. Затем мы получим цену отдельного товара; мы знаем, что это блок во втором абзаце элемента, поэтому мы можем использовать этот элемент в качестве параметра контекста. Мы установим prevCartItem на null, но мы будем использовать его, чтобы узнать, есть ли элемент, который мы перетаскиваем в корзину, в корзине. Значение notInCart - самозапускающаяся анонимная функция; она будет проверяться для каждого элемента в cartList (опять же, мы используем контекстный параметр) и проверяем, является ли id данных такими же, как идентификатор переменной id. Чтобы понять это, вам нужно будет знать, что когда мы добавим элементы списка в корзину покупок, мы будем использовать метод data jQuery для установки идентификатора продукта для элемента. В этой функции мы проверяем элемент списка с правильными данными; если мы найдем один, и элемент уже находится в корзине, поэтому мы устанавливаем значение notinCart в значение false; если он не находится в корзине, мы установим переменную в значение true. Наконец, мы будем использовать quantLeftEl, quantBoughtEl и quantLeft при обновлении количеств.

Теперь, оформим некоторые действия:

1
 
2
$("h2").fadeOut('fast'); 
3
 
4
if (notInCart) { 
5
    prevCartItem = $('<li />', { 
6
        text : $('p:first', item).text(), 
7
        data : { id : id } 
8
    }).prepend($('<span />', { 
9
        'class' : 'quantity', 
10
        text : '0' 
11
    })).prepend($('<span />', { 
12
        'class' : 'price', 
13
        text : price 
14
    })).appendTo(cartList); 
15
}

Во-первых, мы скроем уведомление элемента h2. Затем, если элемент не находится в корзине, мы добавим его в корзину. Для этого мы создадим элемент списка; затем мы можем передать литерал объекта в качестве второго параметра для задания свойств нашего нового элемента списка. Мы установим текст в название продукта; который берем из первого абзаца продукта. Затем мы устанавливаем данные, о которых говорилось выше.

Затем мы добавим блок к этому элементу списка; мы назначим ему класс ‘quantity’ (не забудьте поставить класс в кавычки, так как это зарезервированное слово) и установите текст на ноль; да, я знаю, что число должно быть единицей, поскольку они только что поместили товар в корзину, но мы добавим это позже ... и вы поймете, почему.

Мы добавим еще один блок к элементу списка, на этот раз время цены. Мы делаем обтекание цены справа, поэтому было бы логично добавить его; но это вызовет ошибку с обтеканием в IE; блок будет обтекать текст справа и под элементом списка.

Наконец, мы добавим элемент списка в список корзины покупок.

Последняя часть этой функции будет работать независимо от того, находится ли элемент в корзине или нет:

1
 
2
quantLeftEl = $('p:last span', item); 
3
quantLeft   = parseInt(quantLeftEl.text(), 10) - 1; 
4
quantLeftEl.text(quantLeft); 
5
quantBoughtEl = $('.quantity', prevCartItem); 
6
quantBoughtEl.text(parseInt(quantBoughtEl.text(), 10) + 1); 
7
 
8
if (quantLeft === 0) { 
9
    item.fadeOut('fast').remove(); 
10
} 
11
 
12
total.text((parseFloat(total.text(), 10) + parseFloat(price.split('$')[1])).toFixed(2)); 
13
 
14
evt.stopPropagation(); 
15
return false;

Сначала мы получим количество товара; это количество, оставшееся, после того как покупатель перетащит товар в корзину; мы получим количество, которое действительно осталось; но это строка, поэтому мы используем нативную функцию parseInt, чтобы преобразовать ее в число (используйте 10 в качестве основы для того, чтобы мы получили десятичное число) и вычтите единицу из него. Затем мы сбрасываем количество, используя метод jQuery text.

Затем мы получаем количество, покупок пользователя; это элемент с классом ‘quantity'; мы используем prevCartItem как контекст; это работает, потому что если элемент уже был в корзине, в этой анонимной функции был установлен prevCartItem; если его не было в корзине, мы устанавливаем его, когда мы создаем корзину. Затем мы можем установить текстовое значение, получив текущее значение, преобразуя его в число и добавив к нему единицу.

Что происходит, когда количество достигает нуля? Если он равен нулю, мы скроем элемент и удалим его.

Наконец, мы должны обновить общую цену. У нас есть блок с итоговой ценой, поэтому мы можем просто сбросить текст;  что мы сейчас делаем, это получаем текущий текст, преобразуем его в число (на этот раз мы используем функцию parseFloat для сохранения центов), разделяем знак доллара с ценой и преобразуем его в число и добавляем к двум значениям. Наконец, мы используем функцию toFixed, чтобы убедиться, что мы всегда показываем правильное значение в центах.

Наконец, мы не хотим, чтобы событие drop drop наслаивалось, поэтому мы прекратим его и вернем значение return false;


Шаг 4. Завершенный проект

Отличная работа, мы закончили; вот обзор нашей корзины в действии:


Если вы хотите проверить, что делают другие события перетаскивания, добавьте это в ваш скрипт:

1
 
2
$('#cart').bind('dragleave', function (evt) { 
3
  console.log('dragleave'); 
4
}); 
5
 
6
$('.item') 
7
	.bind('dragend', function (evt) { 
8
		console.log('dragend'); 
9
	}) 
10
	.bind('dragstart', function (evt) { 
11
		console.log('dragstart'); 
12
	}) 
13
	.bind('drag', function (evt) { 
14
		console.log('drag'); 
15
	});

Хотя нативный HTML5 пока не готов к прайм-тайм (Opera не поддерживает его), определенно интересно узнать, куда всё движется!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.