Пример toggle на svelte.
Меняет состояние по клику на противоположное
Создаем переменную active:
let active = true;
</script>
Добавляем конструкцию class:active={active} в то место, которое хотим изменять:
class="другие классы"
class:active="{active}"
on:click="{() => active = !active}"
>
Скрытый текст
</div>
Добавляем стиль:
.active {
display: none;
}
</style>
Выделяет текущую кнопку из множества
Если кнопок (любых других элементов) много на странице, то подойдет другая конструкция. Для каждой кнопки мы назначаем по клику присвоение своего значения. А если оно присвоено, то добавляется класс.
let current = '';
</script>
<div
class="другие классы"
class:active="{current === 'btn-1'}"
on:click="{() => current = 'btn-1'}"
>
btn-1
</div>
<div
class="другие классы"
class:active="{current === 'btn-2'}"
on:click="{() => current = 'btn-2'}"
>
btn-2
</div>
<style lang="scss">
.active {
background-color: #ff0000;
}
</style>
Расширенный вариант предыдущего
Получаем статус выделенной кнопки из БД. Т.е. при загрузке страницы наша кнопка с выбранной настройкой всегда будет выделена.
let current = '';
let settingButtonActive;
function __getSettingCategoryButtonActive() {
getFetch('settings/get-setting-value/' + 'button-active').then((r) => {
settingButtonActive = r
});
}
function __change_button(button) {
let data = {
'settings': button
}
postFetch('settings/change-settings-to-button/', data).then((r) => {
__getData();
});
}
</script>
<div class="btn-1"
class:active="{settingButtonActive === 'all'}"
on:click="{() => __change_button('all')}"
on:click="{() => settingButtonActive = 'all'}"
>Все
</div>
<div class="btn-1"
class:active="{settingButtonActive === 'not-all'}"
on:click="{() => __change_button('not-all')}"
on:click="{() => settingButtonActive = 'not-all'}"
>Не все
</div>
<style lang="scss">
.active {
background-color: #ff0000;
}
</style>
Стоит обратить внимание на то, чтобы функция по on:click (если она есть), стояла выше присвоения настройки переменной. Иначе функция может не работать.
Как сделать toggle для блока each
В этом примере будет показано как можно сделать toggle на каждый элемент блока each, чтобы он работал независимо от других блоков.
Для этого воспользуемся уникальным идентификатором.
{:then elements}
{#each elements as el}
<div class="btn"
on:click="{() => el.id = !el.id}"
>
Кнопка открытия/закрытия
</div>
<div class="block-hidden" class:hide="{el.id}">
скрытый блок
</div>
{/each}
{/await}
<style lang="scss">
.hide {
display: none !important;
}
</style>
Минус такого подхода в том, что мы перезапишем наш идентификатор. Если он нам не нужен в дальнейшем для взаимодействия, то ничего страшного.
Другой вариант вместо уникального идентификатора использовать выдуманное свойство. В этом случае его не будет, а значит нужно изменить показ класса на отрицание. Плюс такого подхода, что уникальный идентификатор, не будет затираться.
{:then elements}
{#each elements as el}
<div class="btn icon-when-close"
class:icon-when-open="{el.temp}"
on:click="{() => el.temp = !el.temp}"
>
Кнопка открытия/закрытия
</div>
<div class="block-hidden" class:hide="{!el.temp}">
скрытый блок
</div>
{/each}
{/await}
<style lang="scss">
.hide {
display: none !important;
}
</style>
Последний рабочий вариант от 15 мая 2022
В предыдущем варианте, если открыть элемент, а затем подтянуть асинхронные изменения к текущему блоку, то он снова закроется. Это неудобно, когда хочется держать изменения перед глазами в развернутом виде.
В текущей реализации будет:
— каждый toggle работает независимо от других блоков;
— при открытии одного блока закрывается другой;
— при изменении асинхронных данных текущий блок не закрывается.
Содержимое файла store.js
export const store = writable({
numbers: []
})
В другом файле импортируем:
Далее в этом же файле делаем функцию:
// Добавим ID при первом открытии
if ($store.numbers.length === 0) {
$store.numbers.push(id);
// Удалим ID при повторном нажатии
} else if ($store.numbers.includes(id)) {
// Удаление из массива
let index = $store.numbers.indexOf(id);
if (index !== -1) {
$store.numbers.splice(index, 1);
}
// Добавим ID при втором и следующем открытии
} else {
$store.numbers.push(id);
}
}
И сам блок, которые требуется разворачивать:
on:click="{() => writeToStore(done.id)}"
on:click="{() => done.temp = !done.temp}"
>
<div class="icon-when-close"
class:icon-when-open="{done.temp}"
>
</div>
</div>
<div class="block-hidden hide" class:show="{
$store.numbers.includes(done.id)
}">
Здесь то, что будет разворачиваться.
</div>
</div>
Старый вариант (есть ошибка, но оставил на память)
Для текущей реализации воспользуемся хранилищем в Svetle.
Создадим файл store.js с содержимым:
export const store = writable()
Импортируем его в нужном нам файле. В нём же создадим функцию записи в хранилище:
import {store} from '../../assets/js/store.js'
let currentItem;
function writeToStore(id) {
// Создадим булевое свойство, если оно не существует.
if (!$store.bool) {
$store.bool = true;
}
// Если свойство для хранения ID, если в нём еще не хранится текущий ID.
if ($store.id !== id) {
$store.id = id;
// Если же в свойство ID хранится текущий ID, то изменим булевое свойство.
} else {
$store.bool = !$store.bool;
}
}
</script>
В этом же файле основной блок данных:
{:then elements}
{#each elements as el}
<div class="block-container" class:current="{currentItem === el.id}">
<div class="btn icon-when-close"
class:icon-when-open="{el.temp}"
on:click="{() => writeToStore(el.id)}"
>
Кнопка открытия/закрытия
</div>
<div class="block-hidden hide" class:show="{
$store.id === el.id &&
$store.bool === true
}">
скрытый блок
</div>
</div>
{/each}
{/await}
<style lang="scss">
.hide {
display: none;
}
.show {
display: block !important;
}
.current {
background-color: #f9f9f9;
}
</style>
Из кода выше всё должно быть понятно, разве что разберем этот участок:
$store.id === done.id &&
$store.bool === true
}">
По умолчанию в блоке стоит класс hide, который прячет элемент. Мы добавим класс show, который покажет спрятанный элемент, если будут соблюдены ряд условий.
Мы также можем у всего контейнера изменить свойство (например, выделить его цветом при открытии). Для этого мы добавили в контейнер класс current.