Фильтрация и проверка данных PHP. Частые ошибки. CSS фильтры изображений Подготовка sql-запроса и извлечение данных из базы
Используя псевдо-класс:checked, можно переключать состояния таких элементов, как checkbox или radio-кнопки. На этом уроке мы будем изучать это свойство CSS3, создав экспериментальный фильтр для портфолио, который будет переключать состояния элементов определенного типа.
На этот урок меня вдохновил блестящей эксперимент Романа Комарова "Фильтрация элементов без JS ", в котором он использует флажки и переключатели для фильтрации цветных форм.
HTML-разметка
Давайте начнем с разметки. Наша цель создать четыре кнопки-фильтра, после нажатия на которые, соответствующие элементы портфолио будут появляются или исчезать. Итак, мы будем использовать несколько переключателей, все они имеют одинаковое имя, так как они должны принадлежать к одной группе (поэтому только один переключатель будет иметь состояние "checked"). По умолчанию, мы хотим, чтобы все переключатели были выбраны или отмечены. Мы добавим несколько тегов label для радио-кнопок, которые мы будем использовать, чтобы скрыть переключатели. Нажатие на label выберет радио кнопку с соответствующими id:
All>
Web Design>
Illustration>
Icon Design>
>
Неупорядоченный список будет содержать все элементы портфолио, с ссылками на изображения. Каждый элемент списка будет иметь класс, на основе которого и будет производится фильтрация элементов, когда мы нажмем одну из радио-кнопок.
CSS
Мы создадим три примера этого эффекта, но сначала давайте посмотрим на общие стили.
Я опускаю все префиксы браузеров, но вы можете найти их в исходниках.
Основной контейнер будет иметь фиксированную ширину:
Ff-container{
width
:
564px
;
margin
:
10px
auto
30px
auto
;
}
Теги label будут скрывать радио-кнопки и мы зададим им градиент и небольшие тонкие тени:
Ff-container
label{
font-family
:
"BebasNeueRegular"
,
"Arial Narrow"
,
Arial,
sans-serif
;
width
:
25%
;
height
:
30px
;
cursor
:
pointer
;
color
:
#777
;
text-shadow
:
1px
1px
1px
rgba(255
,
255
,
255
,
0.8
)
;
line-height
:
33px
;
font-size
:
19px
;
background
:
linear-gradient(top
,
#ffffff
1%
,
#eaeaea
100%
)
;
float
:
left
;
box-shadow:
0px
0px
0px
1px
#aaa
,
1px
0px
0px
0px
rgba(255
,
255
,
255
,
0.9
)
inset
,
0px
1px
2px
rgba(0
,
0
,
0
,
0.2
)
;
}
Для первого и последнего тега label мы создадим закругленные уголки:
Ff-container
label.ff-label-type-all{
border-radius:
3px
0px
0px
3px
;
}
.ff-container
label.ff-label-type-3
{
border-radius:
0px
3px
3px
0px
;
}
Для каждой отмеченной радио-кнопки, мы создадим стили имитирующие эффект "нажатия":
Ff-container
input.ff-selector-type-all
:
checked ~ label.ff-label-type-all,
.ff-container
input.ff-selector-type-1
:
checked ~ label.ff-label-type-1
,
.ff-container
input.ff-selector-type-2
:
checked ~ label.ff-label-type-2
,
.ff-container
input.ff-selector-type-3
:
checked ~ label.ff-label-type-3
{
background
:
linear-gradient(top
,
#646d93
0%
,
#7c87ad
100%
)
;
color
:
#424d71
;
text-shadow
:
0px
1px
1px
rgba(255
,
255
,
255
,
0.3
)
;
box-shadow:
0px
0px
0px
1px
#40496e
,
0
1px
2px
rgba(0
,
0
,
0
,
0.1
)
inset
;
}
Так как у нас все наши элементы находятся на одном уровне, мы используем обобщенный родственный комбинатор (the general sibling combinator), который обозначается символом "тильда" (~). Селектор в ввиде input.ff-selector-type-3:checked ~ label.ff-label-type-3 значит, что элемент input.ff-selector-type-3:checked следует за элементом label.ff-label-type-3 в иерархии документа, исключая различные комментарии и т.п., и они обязательно имеют одного родителя. Этот «трюк» нам также позволит получить различные типы элементов в портфолио.
Элементы input могут быть скрыты, поскольку у нас есть label , которые будут делать всю работу:
Ff-container
input{
display
:
none
;
}
Теперь давайте перейдем к элементам списка:
Ff-items{
position
:
relative
;
margin
:
0px
auto
;
padding-top
:
20px
;
}
Ff-items
a{
display
:
block
;
position
:
relative
;
padding
:
10px
;
background
:
#fff
;
box-shadow:
0
1px
2px
rgba(0
,
0
,
0
,
0.1
)
;
margin
:
4px
;
width
:
160px
;
height
:
120px
;
}
.ff-items
a span{
display
:
block
;
background
:
rgba(113
,
123
,
161
,
0.9
)
;
font-style
:
italic
;
color
:
#fff
;
font-weight
:
bold
;
padding
:
20px
;
position
:
absolute
;
bottom
:
10px
;
left
:
10px
;
width
:
120px
;
height
:
0px
;
overflow
:
hidden
;
opacity:
0
;
text-align
:
center
;
text-shadow
:
1px
1px
1px
#303857
;
transition:
all 0.3s ease-in-out;
}
.ff-items
a:hover
span{
height
:
80px
;
opacity:
1
;
}
.ff-items
li img{
display
:
block
;
}
Вот и все "общие" стили. Теперь давайте посмотрим стили для фильтрации элементов!
В первом примере мы сделаем, чтобы выбранные элементы (т.е. когда соответствующую кнопку радио "отметили") имели самую высокую яркость.
Мы добавим transition к элементу списка для создания непрозрачности:
Ff-items
li{
margin
:
0px
;
float
:
left
;
opacity:
0
;
width
:
188px
;
height
:
148px
;
transition:
opacity 0.6s ease-in-out;
}
Затем мы будем использовать обобщенный родственный комбинатор, чтобы установить непрозрачность для соответствующих пунктов:
Ff-container
input.ff-selector-type-all
:
checked ~ .ff-items
li,
opacity:
1
;
}
Так как у нас все радио-кнопки изначально отмечены, то все элементы будут изначально имеют непрозрачность равную 1.
Теперь, мы будем использовать селектор:not(), чтобы указать список элементов, которые не имеют выбранного класса и должны иметь прозрачность 0.1:
opacity:
0.1
;
}
Описания для этих элементов списка не должны показываться при наведении:
display
:
none
;
}
Это был первый пример. Давайте взглянем на следующий.
В этом примере мы сделаем выбранные элементы увеличенными, в то время как другие уменьшим и сделаем более прозрачными. Итак, давайте добавим transition к элементам списка:
Ff-items
li{
margin
:
0px
;
float
:
left
;
width
:
188px
;
height
:
148px
;
transition:
all 0.6s ease-in-out;
}
По умолчанию, мы будем иметь все элементы списка нормального размера и с полной непрозрачностью. Когда выбираем один тип, мы хотим, чтобы эти пункты увеличились и также остались полностью непрозрачными:
Ff-container
input.ff-selector-type-1
:
checked ~ .ff-items
.ff-item-type-1
,
.ff-container
input.ff-selector-type-2
:
checked ~ .ff-items
.ff-item-type-2
,
.ff-container
input.ff-selector-type-3
:
checked ~ .ff-items
.ff-item-type-3
{
opacity:
1
;
transform:
scale(1.1
)
;
}
Другие элементы портфолио мы будем уменьшать и применять к ним низкий уровень прозрачности:
Ff-container
input.ff-selector-type-1
:
checked ~ .ff-items
li:
not(.ff-item-type-1
)
,
.ff-container
input.ff-selector-type-2
:
checked ~ .ff-items
li:
not(.ff-item-type-2
)
,
.ff-container
input.ff-selector-type-3
:
checked ~ .ff-items
li:
not(.ff-item-type-3
)
{
opacity:
0.1
;
transform:
scale(0.5
)
;
}
И мы опять будем скрывать описание для элементов, которые не выбраны:
Ff-container
input.ff-selector-type-1
:
checked ~ .ff-items
li:
not(.ff-item-type-1
)
span,
.ff-container
input.ff-selector-type-2
:
checked ~ .ff-items
li:
not(.ff-item-type-2
)
span,
.ff-container
input.ff-selector-type-3
:
checked ~ .ff-items
li:
not(.ff-item-type-3
)
span{
display
:
none
;
}
Последний пример всего лишь эксперимент. Мы хотим сделать что-то немного более сложное здесь: при выборе типа, мы хотим масштабировать все элементы, уменьшив их, а затем увеличить только элементы с выбранным типом.
Мы хотим, чтобы невыбранные элементы исчезли, но так как мы не можем анимировать свойство display, мы используем одну маленькую хитрость: когда мы будем уменьшать элементы, мы изменим ширину и сделаем её равной 0.
Итак, давайте зададим изначально ширину списка равную 0:
Ff-items
li{
margin
:
0px
;
float
:
left
;
height
:
148px
;
width
:
0px
;
transform:
scale(0
,
0
)
;
}
Когда выбран пункт "all", мы изменим масштаб равный 1 и ширину установим равную 188px:
Ff-container
input.ff-selector-type-all
:
checked ~ .ff-items
li{
width
:
188px
;
transform:
scale(1
,
1
)
;
transition:
transform 0.3s linear;
}
Помните, что это состояние на начальном этапе, так как у нас "all" отмечено по умолчанию.
Теперь, когда мы отмечаем один определенной тип, элементы с этим типом класса будут сначала исчезать вместе с остальными элементами, а затем появляться снова.
Ff-container
input.ff-selector-type-1
:
checked ~ .ff-items
.ff-item-type-1
,
.ff-container
input.ff-selector-type-2
:
checked ~ .ff-items
.ff-item-type-2
,
.ff-container
input.ff-selector-type-3
:
checked ~ .ff-items
.ff-item-type-3
{
transition:
transform 0.3s linear,
width
0s linear 0.3s;
animation:
scaleUp 0.3s linear 0.4s forwards;
}
.ff-container
input.ff-selector-type-1
:
checked ~ .ff-items
li:
not(.ff-item-type-1
)
,
.ff-container
input.ff-selector-type-2
:
checked ~ .ff-items
li:
not(.ff-item-type-2
)
,
.ff-container
input.ff-selector-type-3
:
checked ~ .ff-items
li:
not(.ff-item-type-3
)
{
animation:
scaleDown 0.3s linear forwards;
}
@keyframes scaleUp {
50%
{
width
:
188px
;
transform:
scale(0
,
0
)
;
}
100%
{
width
:
188px
;
transform:
scale(1
,
1
)
;
}
}
@keyframes scaleDown {
0%
{
width
:
188px
;
transform:
scale(1
,
1
)
;
}
99%
{
width
:
188px
;
transform:
scale(0
,
0
)
;
}
100%
{
width
:
0px
;
transform:
scale(0
,
0
)
;
}
}
Обратите внимание, что этот пример экспериментальный и он будет корректно работать только в браузерах, поддерживающих CSS анимацию. В Firefox 9.0.1 поведение не то, которое ожидалось (при наведении на label анимация срабатывает еще раз), но все работает в Авроре 11.0a2, так что может быть это баг браузера.
Перевод статьи Mary Lou с tympanus.net/codropsЕсли у Вас возникли вопросы, то для скорейшего получения ответа рекомендуем воспользоваться нашим
Но еще HTML поддерживает работу с Фильтрами . Применяя различные Фильтры к тексту, можно добиться интересных эффектов. Но будьте осторожны, не все браузеры одинаково отображают эффекты которые должны давать фильтры , некоторые браузеры откровенно игнорируют фильтры. Поэтому тестируйте свои web-странички в различных браузерах. Все Фильтры корректно работают Internet Exhlorer. Итак давайте рассмотрим работу фильтров.
Например мы хотим выделить фразу: "Доброго времени суток!!! " Давайте попробуем поизвращать эту фразу:-) , применяя к ней различные фильтры.
Фильтр Mask.
Выделяет текст, точнее фон на котором написан текст, как будто бы Вы выделили текст мышью.
Синтаксис фильтра: STYLE="filter:Mask(Color="Color")
Color - цвет выделения в шестнадцатиричном виде (напр. #000FFF) или название цвета на английском, например, Red, Blue, Green. Такое определение цвета применяется во всех фильтрах, поэтому дальше это повторяться не будет.
Листинг 19.1.
Фильтр DropShadow.
Добавляет тень к тексту.
Синтаксис фильтра:
STYLE="filter:DropShadow(Color="Color", OffX="Offx", OffY="Offy", Positive="Positive")"
Color
- Цвет тени
OffX
- Смещение тени по X
OffY
- Смещение тени по Y
Positive
- Тень слева или справа (0 или 1 соответственно)
Листинг 19.3.
Вот как это смотрится web-странице:
Фильтр FlipV.
Переворачивает текст вертикально.
Синтаксис фильтра: STYLE="filter:FlipV"
Листинг 19.5.
Вот как это смотрится web-странице:
Фильтр Wave.
Делает текст волнистым.
Синтаксис фильтра:
STYLE="filter: Wave(Freq="Freq", Add="Add", LightStrength="LightStrength", Phase="Phase", Strength="Strength")"
Freq
- колличество волн
Add
- показать/скрыть окантовку (1 или 0 соответственно)
LightStrength
- сила волн
Phase
- угол волн
Strength
- интенсивность волн
Листинг 19.7.
Доброго времени суток!!! |
Вот как это смотрится web-странице:
Фильтр Blur.
Размывает текст в определенную сторону.
Синтаксис фильтра:
STYLE="filter:Blur(Add="Add", Direction="Direction", Strength="Strength")"
Add
- умеренное или сильное размытие (соответственно 1 или 0)
Direction
- в какую сторону произойдет размытие (oт 0 до 315)
Strength
- смещение размытия
Мы научились собирать данные на клиенте и отправлять их на сервер. А на сервере написали заглушку в том месте, где должны возвращаться товары, отфильтрованные по введенным параметрам. Сейчас мы избавимся от заглушки и напишем пару методов и запросов, которые вытаскивают из базы нужные товары и возвращают их клиенту. Урок достаточно короткий. Приступаем
Что будем делать?
Нам нужно выполнить всего 3 пункта:
- 1. Получить данные с клиента и обработать их под нужды сервера. Например, проставить параметры по умолчанию
- 2. Написать, собственно, сам код для извлечения товаров из базы. В первую очередь, подготовить sql-запрос
- 3. Вернуть клиенту полученные данные
Получение данных с клиента
Вы можете спросить: для чего нужно выделять эту простую операцию отдельно, если все данные мы легко вытащим из массива $_GET?
Во-первых, для того, чтобы проставить значения по умолчанию. Нельзя полагаться на то, что клиент сам позаботится об этом.
Во-вторых, не все данные находятся в $_GET в пригодном для использования виде. Например, сортировку с клиента нам удобнее передавать одним параметром в виде поле_направление, например, price_asc. Но в sql-запросе это отдельные сущности, поэтому их нужно предварительно обработать.
Похожая ситуация и с брендами. На клиенте мы отправляем их в виде массива brands, и php их получает тоже как массив. Но для sql-запроса нужна строка - список брендов через запятую. Поэтому бренды тоже нужно дополнительно обрабатывать.
Итак, напишем функцию getOptions(), которая вытащит данные из $_GET и преобразует их в удобный нам вид. Почти все вводные я уже сообщил, поэтому сразу смотрим на готовый код.
// Получение данных из массива _GET function getOptions() { // Категория и цены $categoryId = (isset($_GET["category"])) ? (int)$_GET["category"] : 0; $minPrice = (isset($_GET["min_price"])) ? (int)$_GET["min_price"] : 0; $maxPrice = (isset($_GET["max_price"])) ? (int)$_GET["max_price"] : 1000000; // Бренды $brands = (isset($_GET["brands"])) ? implode($_GET["brands"], ",") : null; // Сортировка $sort = (isset($_GET["sort"])) ? $_GET["sort"] : "price_asc"; $sort = explode("_", $sort); $sortBy = $sort; $sortDir = $sort; return array("brands" => $brands, "category_id" => $categoryId, "min_price" => $minPrice, "max_price" => $maxPrice, "sort_by" => $sortBy, "sort_dir" => $sortDir); }
Здесь мы видим, что сначала получаем id категории. Если категория не передана, мы считаем category_id = 0. Минимальная цена будет 0, максимальная - 1 миллион. Если Ваш интернет-магазин продает плутоний (нефть китайцам, муравьев поштучно), то Вы всегда можете добавить нулей в нужную строку или на худой конец вести расчеты в евро.
Сортировку преобразуем по-другому. Отдельно вытаскиваем поле сортировки и параметр: asc или desc.
Обратите внимание, что во всех случаях мы не забываем подставлять значение по умолчанию, если нужный параметр не приехал с клиента. И теперь, когда все данные преобразованы, осталось только вернуть их из функции в ассоциативном массиве через return array(...)
Подготовка sql-запроса и извлечение данных из базы
Все данные подготовлены в нужном нам виде, теперь напишем запрос и выполним его. Этим будет заниматься функция getGoods($options, $conn). В параметрах она принимает $options - данные, подготовленные предыдущей функцией, и $conn - объект подключения к БД, который мы создали в предыдущем уроке . Наша задача - написать sql-запрос. В общем виде он выглядит так:
Select g.id as good_id, g.good as good, b.brand as brand, g.price as price, g.rating as rating, g.photo as photo from goods as g, brands as b where g.category_id = выбранная_категория and g.brand_id in (список_брендов_через_запятую) and g.brand_id = b.id and (g.price between минимальная_цена and максимальная_цена) order by поле_сортировки направление_сортировки
Мы извлекаем нужные поля, применив ряд условий where и указав нужную сортировку. С ценами и сортировкой вопросов нет, просто подставляем в соответствующие места запроса нужные значения. Но с категорией и брендами нужно быть повнимательнее и вот почему.
Каждый товар у нас всегда имеет категорию.
Понятия нулевой категории в нашей базе данных нет - мы это сделали для своего же удобства, чтобы понимать, что пользователь в браузере не выбрал никакую категорию
(или выбрал все - для нас это одно и то же).
И в этом случае мы не должны включать в запрос строчку
g.category_id = выбранная_категория and
То же самое и с брендами, если они не выбраны, то соответствующую строку пропускаем.
Вот как это выглядит в коде.
// Получение товаров function getGoods($options, $conn) { // Обязательные параметры $minPrice = $options["min_price"]; $maxPrice = $options["max_price"]; $sortBy = $options["sort_by"]; $sortDir = $options["sort_dir"]; // Необязательные параметры $categoryId = $options["category_id"]; $categoryWhere = ($categoryId !== 0) ? " g.category_id = $categoryId and " : ""; $brands = $options["brands"]; $brandsWhere = ($brands !== null) ? " g.brand_id in ($brands) and " : ""; $query = " select g.id as good_id, g.good as good, b.brand as brand, g.price as price, g.rating as rating, g.photo as photo from goods as g, brands as b where $categoryWhere $brandsWhere g.brand_id = b.id and (g.price between $minPrice and $maxPrice) order by $sortBy $sortDir "; $data = $conn->query($query); return $data->fetch_all(MYSQLI_ASSOC); }
Сначала мы извлекаем из массива $options переменные цен и сортировок - они просто вставляются в запрос без изменений. А для категории и брендов мы формируем строки $categoryWhere и $brandsWhere по принципу: нужное условие для секции where, если данные есть, и пустая строка если данных нет. Таким образом получился достаточно вменяемый sql-запрос, учитывающий все наши пожелания. Две последние строчки выполняют оный запрос и возвращают из функции массив из объектов с нужными полями. Осталось собрать все в кучу и отправить полученные товары обратно уже заждавшемуся клиенту/браузеру.
Возвращаем товары клиенту
Это самая простая часть урока. Посмотрим на заглушку, написанную в предыдущем уроке.
// Подключаемся к базе данных $conn = connectDB(); // Возвращаем клиенту успешный ответ echo json_encode(array("code" => "success", "data" => $_GET));
Заменим этот код на
// Подключаемся к базе данных $conn = connectDB(); // Получаем данные от клиента $options = getOptions(); // Получаем товары $goods = getGoods($options, $conn); // Возвращаем клиенту успешный ответ echo json_encode(array("code" => "success", "options" => $options, "goods" => $goods));
Мы добавили пару строк: функцией getOptions извлекли данные в переменную $options. Тут же использовали ее в получении товаров getGoods, результаты сохранили в $goods. И расширили ответ клиенту. Параметр data переименовали в options и вернули в него не содержимое $_GET, а уже преобразованные значения. И в параметре goods вернули массив полученных товаров.
На этом урок закончен. Пока мы не можем использовать эти данные на клиенте, отрисовать их в браузере - этим мы займемся на следующем уроке. Но всегда можем открыть консоль, потыкать кнопочки и галочки и убедиться, что сервер возвращает нам правильные товары.
Проверяем результаты работы
Выберем категорию Смартфоны и отметим бренды Apple и Samsung.
В ответе увидим, что сервер вернул 3 товара, отсортированных по возрастанию цены
Теперь поставим минимальную цену в 20 тысяч и сменим сортировку на убывание цены
Как видно, теперь всего 2 товара - один самсунг отбросился из-за неподходящей по фильтрам цены в 17 тысяч.
И отсортированы товары уже наоборот. Если Вы все сделали правильно, то увидите точно такую же картинку.
Вы можете еще поиграть с консолью и убедиться, что данные возвращаются корректно. В конце концов, самое важное - это добиться правильной работы фильтров, возврата правильного списка товаров. Раскидать полученные данные по странице, с учетом уже готовой верстки - дело внешне самое интересное, но с точки зрения разработки достаточно простое. Но не буду забегать вперед - подробности в следующем уроке.
Хорошо спроектированный фильтр - это мощный инструмент, которым могут воспользоваться пользователи. На самом деле это важная функция, если на вашем сайте (интернет-магазине) есть много товара, распределенного по разным категориям.
ИСХОДНИКИ
Для электронной коммерции это способ повысить коэффициент конверсии за счет сокращения времени, необходимого пользователю, чтобы найти то, что он ищет.
Создание таких функций никогда не бывает легким: фильтры сильно зависят от содержимого веб-сайта; Кроме того, панель фильтров не должна отвлекать, основное внимание должно быть уделено контенту / продуктам. Поэтому мы попытались немного упростить вашу жизнь, построив для вас легко настраиваемую и легко интегрируемую панель фильтров CSS.
Он использует преимущества CSS Transitions, CSS Transformations и jQuery для плавного перехода в случае необходимости.
Создание структуры
Структура HTML немного сложнее, чем обычно. Прежде всего, есть два основных блока контента: элементы header и main , второй используется для обертывания как галереи.cd-gallery , так и фильтра.cd-filter . В дополнение к этому у нас есть вкладная навигация (вложенная в 2 div элемента, из-за эффекта выпадающего эффекта, видимого на мобильных устройствах) и триггер фильтра.cd-filter-trigger .
Вы также можете заметить множество имен классов (например, в элементах списка галереи) и фильтры данных: они используются для фильтрации содержимого, а не для стилизации.
Примечание. Назначение элемента.cd-gallery> li.gap - работать в сочетании с текстом: justify; Свойство, примененное к.cd-gallery , чтобы создать сетку галереи. Вам нужно создать столько же элементов.gap , сколько и максимальное количество элементов в строке -1.
Content Filter
All
Добавление стиля
Большая часть CSS касается стилей элементов формы и других базовых украшений. Интересно, как мы определили и использовали некоторые классы - в сочетании с jQuery - для изменения поведения некоторых элементов на основе определенных событий.
Например: на всех устройствах панель фильтров фиксируется, когда она достигает вершины области просмотра. Для достижения этого эффекта мы использовали класс.is-fixed , примененный к элементу main (.cd-main-content), чтобы мы могли ориентировать некоторые его дочерние элементы. В частности: .cd-tab-filter-wrapper находится в статическом положении, в то время как.cd-filter и.cd-filter-trigger находятся в абсолютном положении (относительно.cd-main-content). Когда мы применяем.is-fixed класс к.cd-main-content , мы переключаем положение всех этих элементов на Fixed.
Cd-tab-filter-wrapper { background-color: #ffffff; z-index: 1; } .cd-filter { position: absolute; top: 0; left: 0; width: 280px; height: 100%; background: #ffffff; z-index: 2; transform: translateX(-100%); transition: transform 0.3s, box-shadow 0.3s; } .cd-filter-trigger { position: absolute; top: 0; left: 0; height: 50px; width: 60px; z-index: 3; } .cd-main-content.is-fixed .cd-tab-filter-wrapper { position: fixed; top: 0; left: 0; width: 100%; } .cd-main-content.is-fixed .cd-filter { position: fixed; height: 100vh; overflow: hidden; } .cd-main-content.is-fixed .cd-filter-trigger { position: fixed; }
Еще одна вещь, о которой стоит упомянуть - это.filter-is-visible класс. Он применяется к нескольким элементам, когда пользователь запускает панель фильтра. На всех устройствах оно используется для изменения значения translateX элемента.cd-filter (от -100% до 0). На больших устройствах (> 1170px) мы также нацеливаем на.cd-gallery и.cd-tab-filter и уменьшаем их ширину: таким образом панель не будет перекрывать контент, а пользователь использует дополнительные возможности Пространство для применения фильтров и просмотра изменений одновременно, без необходимости закрывать панель.
Обработка событий
Для реализации функциональности фильтра содержимого мы интегрировали плагин MixItUp jQuery. Чтобы инициализировать плагин в контейнере галереи, мы используем функцию mixItUp () и объявляем переменную buttonFilter , которая содержит все пользовательские функциональные возможности фильтра. Кроме того, мы используем jQuery для открытия / закрытия панели фильтров и исправления (вместе с навигацией с вкладками), чтобы он все еще отображался при прокрутке галереи.