Константин Капельников

личная страница для коллег, работодателей, клиентов и делового нетворкинга

на главную

Создание красивого адаптивного алфавитного указателя

На сайте 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

Комментарии