Создание красивого адаптивного алфавитного указателя
На сайте du9.org есть красивый список авторов.
Сделан он следующим образом:
В четырех колонках расположены списки фамилий, каждый со своей буквы.
Первые четыре буквы стоят вначале каждой из колонок. Затем при прокрутке вниз следующая буква “E” попадает в колонку, которая заканчивается первой. В данном случае это первая колонка с буквой “A”.
Затем по тому же принципу идут остальные буквы:
Первый этап
Загорелся сделать такое на одном из своих сайтов. Только у оригинала верстка не адаптивная, я же попробую сделать чтобы при уменьшении окна браузера 4 колонки на десктопе переходили в 2 колонки на планшете и затем в 1 колонку на мобильных. И списки по буквам должны вести себя аналогичным образом, т.е. следующая буква должна попадать именно в ту колонку, которая заканчивается первой.
Для адаптивности я использую свою простую колоночную сетку на флексбоксах на манер Бутстрапа:
<div class="row"> <div class="col-lg-3 col-md-6 col-xs-12 abc_col-1"></div> <div class="col-lg-3 col-md-6 col-xs-12 abc_col-2"></div> <div class="col-lg-3 col-md-6 col-xs-12 abc_col-2"></div> <div class="col-lg-3 col-md-6 col-xs-12 abc_col-4"></div> </div>
Таким образом col-lg-3 на десктопах будет делить 12 колонок на 3 и выстраивать списки в 4 колонки как на сайте исходнике, col-md-6 будет делить 12 колонок пополам и выдавать по 2 колонки на планшете, но а при col-xs-12 все колонки будут занимать всю ширину экрана.
Припишем к концу каждой колонки технические классы с номером (abc_col-1, abc_col-2 и так далее). Они будут нужны в дальнейшем для указания куда класть список следующей буквы.
Всю магию будем делать на jQuery. Но перед этим нужно определиться что нам выдает php-шаблон Вордпресса и какой HTML на выходе мы получаем.
Авторы просто идут друг за другом в одной колонке.
<p class="abc_main-letter" id="A">A</p> <p class="abc_author letter A">Achdé</p> <p class="abc_author letter A">Adam Święcki</p> <p class="abc_author letter A">Alain Bardet</p> <p class="abc_author letter A">Alain Dodier</p> <p class="abc_author letter A">Alberto Madrigal</p> ... <p class="abc_main-letter" id="B">B</p> <p class="abc_author letter B">Bartolomé Segui Nicolau</p> <p class="abc_author letter B">Bartosz Sztybor</p> <p class="abc_author letter B">Bastien Vivès</p> <p class="abc_author letter B">Bec</p> <p class="abc_author letter B">Benec</p> ...
Мне не удалось обернуть в шаблоне вывод авторов в тег списка <ul>, что семантически неверно, так как это именно список. В итоге каждая строчка пока идет в теге абзаца <p> с техническими классами и айдишниками.
Добавим красоту в css-стилях как у исходника и центруем.
.abc_author { text-align: center; } .abc_main-letter { background: url(img/bg-letter.jpg) center 263px no-repeat; font-family: 'LeagueGothicRegular', sans-serif; font-size: 300px; line-height: 285px; margin-top: 2px; margin-bottom: 2px; text-align: center; color: black; }
Получаем уже красивые крупные буквы и список по центру:
Скрипты я подключил в шаблон footer.php перед закрывающим тегом <body>. Не стал навешивать на все страницы, прописал условие:
<?php if (is_category(“здесь ID категории авторов”)) { $path = get_template_directory_uri(); echo "<script src='https://code.jquery.com/jquery-3.2.1.min.js'></script>"; echo "<script src='$path/js/abc.js'></script>"; } ?>
Все визуально выглядит пока вот так:
Также создадим технические дивы обертки для отдельных букв со списками и дадим им соответствующий технических класс.
<div class="letter-A"></div> <div class="letter-B"></div> <div class="letter-C"></div> <div class="letter-D"></div> <div class="letter-E"></div> <div class="letter-F"></div> <div class="letter-G"></div> <div class="letter-H"></div> <div class="letter-I"></div> ...
Первое, что мы делаем в jQuery — это кладем соответствующую букву и идущие за ним строчки в соответствующий <div>.
$('#A').appendTo( $('.letter-A') ); $('.A').appendTo( $('.letter-A') ); $('#B').appendTo( $('.letter-B') ); $('.B').appendTo( $('.letter-B') ); $('#C').appendTo( $('.letter-C') ); $('.C').appendTo( $('.letter-C') ); $('#D').appendTo( $('.letter-D') ); $('.D').appendTo( $('.letter-D') ); $('#E').appendTo( $('.letter-E') ); $('.E').appendTo( $('.letter-E') ); $('#F').appendTo( $('.letter-F') ); $('.F').appendTo( $('.letter-F') ); $('#G').appendTo( $('.letter-G') ); $('.G').appendTo( $('.letter-G') ); $('#H').appendTo( $('.letter-H') ); $('.H').appendTo( $('.letter-H') ); $('#I').appendTo( $('.letter-I') ); $('.I').appendTo( $('.letter-I') ); ...
Затем нам нужно первые четыре буквы расставить по колонкам слева направо. Просто переносим дивы по номерам колонок (мы для этого создавали технические классы abc_col-1, abc_col-2… с номерами):
$('.letter-A').appendTo( $('.abc_col-1') ); $('.letter-B').appendTo( $('.abc_col-2') ); $('.letter-C').appendTo( $('.abc_col-3') ); $('.letter-D').appendTo( $('.abc_col-4') );
Теперь визуально это выглядит так:
Далее нужно определить куда класть следующую букву «E». Она должна попасть в колонку с меньшим количеством строк.
Для этого считаем число элементов по-буквенно, используя один из технических классов, и выведем сразу в консоль для наглядности:
var count_A = $('.A').length console.log("столбец A", count_A); var count_B = $('.B').length console.log("столбец B", count_B); var count_C = $('.C').length console.log("столбец C", count_C); var count_D = $('.D').length console.log("столбец D", count_D);
Затем посчитаем минимальное значение и выведем результат в консоль:
var count_min = Math.min(count_A, count_B, count_C, count_D); console.log("минимальное значение начальных столбцов для E", count_min);
В итоге в консоли видим следующее:
В последнем столбце «D» количество строк минимальное. Значит нашу букву “E” нужно класть именно туда. Теперь нужно как-то сообщить это jQuery.
Делаем проверку на равнозначность минимального значения (11) и числа из каждой колонки. Если значения равные, то скрипт положит <div class="letter-E"> в эту колонку.
if (count_min == count_A) { $('.letter-E').appendTo($('.abc_col-1')); } if (count_min == count_B) { $('.letter-E').appendTo($('.abc_col-2')); } if (count_min == count_C) { $('.letter-E').appendTo($('.abc_col-3')); } if (count_min == count_D) { $('.letter-E').appendTo($('.abc_col-4')); }
Но может получиться ситуация, когда в начальных колонках будет одинаковое количество строк. В таком случае скрипт положит в див в самую последнюю из них. А нам нужно наоборот в самую первую.
Чтобы этого избежать пишем следующее:
if (count_min == count_A && count_min == count_B) { $('.letter-E').appendTo($('.abc_col-1')); } // если число строк буквы A и буквы B одинаковое, то клади в первую колонку if (count_min == count_A && count_min == count_C) { $('.letter-E').appendTo($('.abc_col-1')); } // если число строк буквы A и буквы C одинаковое, то клади в первую колонку if (count_min == count_A && count_min == count_D) { $('.letter-E').appendTo($('.abc_col-1')); } // если число строк буквы A и буквы D одинаковое, то клади в первую колонку
Это была первая часть. Мы раскидали по колонкам первые четыре буквы и положили в минимальную колонку следующую букву «E».
Второй этап
Теперь нам нужно понять по какому принципу класть другие буквы. Дело в том, что количество авторов в колонках все время меняется. Контент-менеджер через админку может как добавлять их, так и удалять. Таким образом число строк постоянно меняется и его нужно как то пересчитывать на ходу, чтобы понимать какая из них минимальная для отнесения туда следующей по алфавиту буквы.
В начале я решал это следующим образом: просто считал число строк уже по всем колонкам, находил минимальное значение и клал туда следующую букву:
var count_abc_col_1 = $(".abc_col-1").find(".letter") var count_abc_col_1 = $(count_abc_col_1).length console.log("первая колонка", count_abc_col_1); var count_abc_col_2 = $(".abc_col-2").find(".letter") var count_abc_col_2 = $(count_abc_col_2).length console.log("вторая колонка", count_abc_col_2); var count_abc_col_3 = $(".abc_col-3").find(".letter") var count_abc_col_3 = $(count_abc_col_3).length console.log("треться колонка", count_abc_col_3); var count_abc_col_4 = $(".abc_col-4").find(".letter") var count_abc_col_4 = $(count_abc_col_4).length console.log("четвертая колонка", count_abc_col_4); var count_min = Math.min(count_abc_col_1, count_abc_col_2, count_abc_col_3, count_abc_col_4); console.log("минимальное значение столбцов для F", count_min); if (count_min == count_abc_col_1) { $('.letter-F').appendTo($('.abc_col-1')); } if (count_min == count_abc_col_2) { $('.letter-F').appendTo($('.abc_col-2')); } if (count_min == count_abc_col_3) { $('.letter-F').appendTo($('.abc_col-3')); } if (count_min == count_abc_col_4) { $('.letter-F').appendTo($('.abc_col-4')); } if (count_min == count_abc_col_1 && count_min == count_abc_col_2) { $('.letter-F').appendTo($('.abc_col-1')); } if (count_min == count_abc_col_1 && count_min == count_abc_col_3) { $('.letter-F').appendTo($('.abc_col-1')); } if (count_min == count_abc_col_1 && count_min == count_abc_col_4) { $('.letter-F').appendTo($('.abc_col-1')); }
Консоль показывает, что буква «F» должна идти во вторую колонку:
Проблемы в процессе работы
Но по ходу столкнулся с одной не очевидной проблемой: в середине процесса заметил, что после буквы «J» должна идти буква «К», но почему то визуально встает буква «M». Стал проверять на предмет ошибки в подсчетах минимального числа строк в колонках, но все было правильно. В колонке 2 было 33 строки, а в колонке 4 было 30. Скрипт все подставил правильно, но визуально выглядело так, как будто буква «М» опережала букву «К» и попала не туда. Оказалось что на внешнее восприятие влияют сами буквы. Стилями они сделаны большими для красоты, и даже не смотря на меньшее число строк в колонке из-за самих букв колонка визуально смотрится больше.
В итоге подсчет числа строк для определения нужной строки не подходит.
В процессе раздумий пришел к следующему решению: так как все буквы и строки лежат в соответствующих технических дивах, то можно с помощью jQuery посчитать их суммарную высоту, и чья будет меньше, туда и класть следующую букву.
var full_height1 = 0; $(".abc_col-1 div").each(function() { full_height1 += $(this).height(); }); console.log("4 колонка", full_height1); var full_height2 = 0; $(".abc_col-2 div").each(function() { full_height2 += $(this).height(); }); console.log("4 колонка", full_height2); var full_height3 = 0; $(".abc_col-3 div").each(function() { full_height3 += $(this).height(); }); console.log("4 колонка", full_height3); var full_height4 = 0; $(".abc_col-4 div").each(function() { full_height4 += $(this).height(); }); console.log("4 колонка", full_height4); var min_div = Math.min(full_height1, full_height2, full_height3, full_height4); console.log("минимальная высота дивов для F", min_div); if (min_div == full_height1) { $('.letter-F').appendTo($('.abc_col-1')); } if (min_div == full_height2) { $('.letter-F').appendTo($('.abc_col-2')); } if (min_div == full_height3) { $('.letter-F').appendTo($('.abc_col-3')); }
Консоль:
Как видим все высчитывается более точнее с учетом визуальной составляющей и буква «K» спокойно перемещается во вторую колонку.
Планшетная и мобильная версии
С этим разобрались. Теперь делаем адаптивную подстройку букв для планшетов и мобильных. Напоминаю, что при сужении видимой области экрана до max-width: 1100px наша верстка из четырех колонок превращается в две (планшетный вариант). А при max-width: 640px в одну (мобильные). И наши буквы должны вести себя соответствующе.
Нам нужно динамически отслеживать изменение ширины экрана и реагировать на это. Для этого получаем ее значение. Создаем нужные переменные, прописываем функцию проверки ширины checkWidth() и кладем туда нужные условия.
Комментарий для планшетной версии: так как мы используем только первые две колонки, нам вначале нужно переложить дивы с буквами в другое место. Кладем в ненужную четвертую колонку. Потом все по старой схеме.
Комментарий для мобильный версии: тут еще проще, мы просто складываем все буквы, начиная с В, в первую колонку и они спокойно выстраиваются в нужной порядке по алфавиту.
var $window = $(window); function checkWidth() { var windowsize = $window.width(); console.log(windowsize); if (windowsize >= 1100) { console.log("Еще десктоп"); } if (windowsize < 1100 && windowsize > 640) { console.log("Уже планшет"); $('.letter-C').appendTo($('.abc_col-4')); $('.letter-D').appendTo($('.abc_col-4')); $('.letter-E').appendTo($('.abc_col-4')); $('.letter-F').appendTo($('.abc_col-4')); $('.letter-H').appendTo($('.abc_col-4')); $('.letter-G').appendTo($('.abc_col-4')); // буква С var full_height1 = 0; $(".abc_col-1 div").each(function() { full_height1 += $(this).height(); }); console.log("1 колонка", full_height1); var full_height2 = 0; $(".abc_col-2 div").each(function() { full_height2 += $(this).height(); }); console.log("2 колонка", full_height2); var min_div = Math.min(full_height1, full_height2); console.log("минимальная высота дивов для C", min_div); if (min_div == full_height1) { $('.letter-C').appendTo($('.abc_col-1')); } if (min_div == full_height2) { $('.letter-C').appendTo($('.abc_col-2')); } if (windowsize <= 640) { console.log("Уже мобильные"); $('.letter-B').appendTo($('.abc_col-1')); $('.letter-C').appendTo($('.abc_col-1')); $('.letter-D').appendTo($('.abc_col-1')); $('.letter-E').appendTo($('.abc_col-1')); $('.letter-F').appendTo($('.abc_col-1')); $('.letter-G').appendTo($('.abc_col-1')); $('.letter-H').appendTo($('.abc_col-1')); $('.letter-I').appendTo($('.abc_col-1')); $('.letter-J').appendTo($('.abc_col-1')); $('.letter-K').appendTo($('.abc_col-1')); $('.letter-L').appendTo($('.abc_col-1')); $('.letter-M').appendTo($('.abc_col-1')); $('.letter-N').appendTo($('.abc_col-1')); $('.letter-O').appendTo($('.abc_col-1')); $('.letter-P').appendTo($('.abc_col-1')); $('.letter-Q').appendTo($('.abc_col-1')); $('.letter-R').appendTo($('.abc_col-1')); $('.letter-S').appendTo($('.abc_col-1')); $('.letter-T').appendTo($('.abc_col-1')); $('.letter-U').appendTo($('.abc_col-1')); $('.letter-V').appendTo($('.abc_col-1')); $('.letter-W').appendTo($('.abc_col-1')); $('.letter-X').appendTo($('.abc_col-1')); $('.letter-Y').appendTo($('.abc_col-1')); $('.letter-Z').appendTo($('.abc_col-1')); } }
Посмотреть пример на CodePen
Комментарии