Alpine.js - это новый легкий JavaScript-фреймворк, который использует подход добавления поведения непосредственно в html-разметку. Согласно документации;
Alpine.js предлагает вам реактивный и декларативный характер больших фреймворков, таких как Vue или React, по гораздо более низкой цене. Вы можете сохранить свой DOM и посыпать поведение так, как считаете нужным.
После включения кода — я использую метод npm — вы можете посыпать нужное вам поведение. Они обеспечивают аккуратность пример для вкладок;
<div x-data="{ tab: 'foo' }">
<button class="button" :class="{ 'button--active': tab === 'foo' }" x-on:click="tab = 'foo'">Foo</button>
<button class="button" :class="{ 'button--active': tab === 'bar' }" x-on:click="tab = 'bar'">Bar</button>
<div x-show="tab === 'foo'">Tab Foo</div>
<div x-show="tab === 'bar'">Tab Bar</div>
</div>
Если вы понимаете базовые HTML и JavaScript, вы, надеюсь, сможете понять, что происходит довольно легко.
События щелчка добавляются к <button>
элементам с помощью атрибутаx-on:click
.
Атрибут x-show
вычисляет, какой элемент отображать.
И :class
используется для добавления 'активного' класса к кнопке.
Набор элементов становится компонентом с помощью атрибутаx-data
, который в приведенном выше примере по умолчанию имеет видимую вкладку.
Создание списка
перетаскивания мне недавно понадобилось для создания интерфейса перетаскивания. Обычно я бы потянулся к готовому решению, исследованию какие пакеты доступны и решать, что использовать. Это было сделано для того, чтобы избежать межбраузерных проблем и заново изобретать колесо.
Тем не менее, поддержка JavaScript значительно улучшилась за эти годы, и браузеры добавили встроенная поддержка событий перетаскивания. Это относительно небольшой компонент, поэтому не имело особого смысла использовать большую библиотеку для достижения того, что было необходимо.
Первым шагом в создании любого такого компонента является определение HTML, который мы хотим использовать. Я использовал методологию БЭМ, чтобы помочь соглашению об именовании.
Нам нужны два списка, каждый со своим заголовком, контейнером и разделителем.
<div class="drag-and-drop">
<div class="drag-and-drop__container drag-and-drop__container--from">
<h3 class="drag-and-drop__title">From</h3>
<ul class="drag-and-drop__items">
<li class="drag-and-drop__item"></li>
</ul>
</div>
<div class="drag-and-drop__divider">⇄</div>
<div class="drag-and-drop__container drag-and-drop__container--to">
<h3 class="drag-and-drop__title">To</h3>
<ul class="drag-and-drop__items">
<li class="drag-and-drop__item"></li>
</ul>
</div>
</div>
Создайте компонент
Alpine.js Теперь нам нужно начать добавлять наш Alpine.js. Начните с добавления атрибута x-data
для настройки нашей новой области компонентов.
<div class="drag-and-drop" x-data>
...
</div>
Далее нам нужно обозначить, какие элементы можно перетащить. Мы хотим, <li>
чтобы его можно было перетащить, поэтому добавляем к нему атрибут.
<li class="drag-and-drop__item" draggable="true"></li>
Привязка событий
Нам нужно определить, куда можно перетащить перетаскиваемый элемент. Нам нужно привязать событие к обоим <ul>
элементам.
Здесь мы можем использовать атрибут Alpine.jsx-on
'.
<ul class="drag-and-drop__items" x-on:drop="">
...
</ul>
Есть и другие события, которые мы можем слушать, чтобы добавить возможности для взаимодействия с пользователем.
<ul class="drag-and-drop__items"
x-on:drop=""
x-on:dragover.prevent=""
x-on:dragleave.prevent="">
...
</ul>
Настройка данных и переключение
На данный момент это просто оболочки событий и фактически ничего не делают. Чтобы сделать их полезными, мы можем начать манипулировать данные о компоненте. Сначала нам нужно настроить, о каких свойствах должен заботиться компонент. Мы хотим знать при добавлении или удалении элемента. Мы можем настроить эти значения по умолчанию следующим образом;
<div class="drag-and-drop" x-data="{ adding: false, removing: false }">
...
</div>
Теперь мы можем манипулировать этими атрибутами внутри наших событий. Для нашего списка «Кому» мы хотим знать
когда мы добавляем новый элемент, мы обновляем свойствоadding
. Аналогично, мы бы обновили removing
свойство в списке "От". Вот как сейчас выглядит список «Кому».
<ul class="drag-and-drop__items"
x-on:drop=""
x-on:dragover.prevent="adding = true"
x-on:dragleave.prevent="adding = false">
...
</ul>
Мы изменили некоторые значения, но при этом нет видимой обратной связи с пользовательским интерфейсом. Мы можем сделать это, изменив стили,
путем изменения классов в списке. Когда свойство adding
есть, true
мы можем добавить
новый класс с использованием :class
атрибута. Этот класс удаляется, если свойство adding
имеет вид false
.
<ul class="drag-and-drop__items"
:class="{ 'drag-and-drop__items--adding': adding }"
x-on:drop=""
x-on:dragover.prevent="adding = true"
x-on:dragleave.prevent="adding = false">
...
</ul>
Мы будем использовать тот же синтаксис при удалении элементов, переключая removing
свойство и изменяя класс.
Я использовал классdrag-and-drop__items--removing
, но мы могли бы использовать одно и то же имя класса для обоих списков.
Мы можем применить ту же идею к стилизации элемента, который мы хотим перетащить. На данный момент у нас есть draggable
только
применен атрибут. Давайте установим каждый элемент в качестве компонента, привяжем некоторые события и обновим класс.
<li
class="drag-and-drop__item"
:class="{ 'drag-and-drop__item--dragging': dragging }"
draggable="true"
x-data="{ dragging: false }"
x-on:dragstart.self="dragging = true"
x-on:dragend="dragging = false">
...
</li>
Обратите внимание, что мы добавили .self
модификатор к привязке события. В документации говорится:
Добавление
.self
в прослушиватель событий активирует обработчик только в том случае,$event.target
если это сам элемент.
Обработка перетаскивания
До сих пор мы связали несколько событий и использовали эти события, чтобы обеспечить доступность пользовательского интерфейса путем обновления стиль перетаскивания элемента и отображение того, куда он может быть отброшен. Далее нам нужно фактически переместить элемент.
Нам нужно использовать dragstart
событие для обновления dataTransfer
объекта. Этот объект хранит информацию
который доступен для drop
мероприятия. При перемещении элемента свойство устанавливается в effectAllowed
move
значение .
Во-вторых, нам нужно добавить информацию о нашем элементе в событие. Поскольку каждый элемент имеет , мы можем id
использовать
это в качестве ссылки.
x-on:dragstart.self="
dragging = true;
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', event.target.id);
"
Теперь событие, которое drop
мы привязали к целевому контейнеру, имеет доступ к этой информации. Вместо использования setData()
метод, мы можем getData()
использовать метод из dataTransfer
объекта. Это вернет id
предмет, который мы отбрасываем. Мы хотим добавить наш элемент к цели. Используя стандартный JavaScript, мы можем это сделать;
x-on:drop.prevent="
const id = event.dataTransfer.getData('text/plain');
const target = event.target.closest('ul');
const element = document.getElementById(id);
target.appendChild(element);
"
Код
Теперь у нас есть все отдельные части для построения компонента перетаскивания. Сложив все это вместе, это должно выглядеть так;
<div class="drag-and-drop" x-data="{ adding: false, removing: false }">
<div class="drag-and-drop__container drag-and-drop__container--from">
<h3 class="drag-and-drop__title">From</h3>
<ul
class="drag-and-drop__items"
:class="{ 'drag-and-drop__items--removing': removing }"
x-on:drop="removing = false"
x-on:drop.prevent="
const id = event.dataTransfer.getData('text/plain');
const target = event.target.closest('ul');
const element = document.getElementById(id);
target.appendChild(element);
"
x-on:dragover.prevent="removing = true"
x-on:dragleave.prevent="removing = false">
<li
id="item-1"
class="drag-and-drop__item"
:class="{ 'drag-and-drop__item--dragging': dragging }"
x-on:dragstart.self="
dragging = true;
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', event.target.id);
"
x-on:dragend="dragging = false"
x-data="{ dragging: false }"
draggable="true">
Your Item #1
</li>
</ul>
</div>
<div class="drag-and-drop__divider">⇄</div>
<div class="drag-and-drop__container drag-and-drop__container--to">
<h3 class="drag-and-drop__title">To</h3>
<ul
class="drag-and-drop__items"
:class="{ 'drag-and-drop__items--adding': adding }"
x-on:drop="adding = false"
x-on:drop.prevent="
const id = event.dataTransfer.getData('text/plain');
const target = event.target.closest('ul');
const element = document.getElementById(id);
target.appendChild(element);
"
x-on:dragover.prevent="adding = true"
x-on:dragleave.prevent="adding = false">
</ul>
</div>
</div>
Резюме
Я надеюсь, что вы нашли этот учебник полезным, особенно если вы новичок в мире Alpine.js.
Это очень простая реализация перетаскивания с минимальным JavaScript благодаря Alpine.js. Он по-прежнему нуждается в улучшении, например, для поддержки переупорядочивания в каждом списке. Но это похоже на хорошую отправную точку.
Лично я бы сейчас начал переносить JavaScript в отдельный файл. Я бы сохранил привязки событий и переключение классов в HTML, но более длинные блоки JavaScript будут более удобными для обслуживания в конкретном файле JavaScript.
Предварительный просмотр
Вы можете просмотреть этот код в действии на Codepen demo.