• Время чтения ~5 мин
  • 18.03.2023

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.


Читать далее...

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297