Рубрики
Без рубрики

Приоритетная навигация Flexbox

Вы цените отзывчивый, удобный для пользователя дизайн приоритетной навигации? Вы также цените выравнивание и интервал Flexbox? Что делать, если у вас может быть твоего доверия и есть его тоже? В этом посте я поделюсь, как я создал приоритетное навигационное меню, которое использует Flexbox.

Автор оригинала: Mary Schieferstein.

Проблема

Какая ваша наименее любимая часть создания сайта? Дизайн? Анимация? Обработка формы?

Для меня это меню. Меню – это байн моего существования. Каждый раз, когда я создаю новый сайт, сайт нуждается в меню. Конечно, там много меню, я мог бы использовать. Проблема в том, что никто из них не работает именно так, как я хочу. Поэтому я беру существующее меню и измените его, чтобы создать свой собственный. Как только у меня это работает, я его повторно повторно. Отказ Отказ Пока я не найду непоправимый недостаток или новый, лучший тип меню приходит. Затем вернулся к чертежной доске.

Новый, лучший тип меню, который я в последний раз пытался решить? Приоритетная навигация.

Приоритетная навигация Это тип горизонтального меню, которое показывает столько ссылок, как позволяет пробел, скрывая остальные под динамически созданным (и обновленным) «более». Он улучшает пользовательский опыт:

  • Сохранение важных предметов видимы как можно дольше
  • Уменьшение потребности в кнопках меню, которые, особенно при отображении в виде значков без меток, могут быть запутаны пользователям

Я был действительно взволнован улучшенной отзывчивостью и удобством использования этого типа меню. Тем не менее, была одна часть, о которой я не был взволнован: ни один из примеров я нашел использованный Flexbox.

По моему опыту Flexbox является самым быстрым, наиболее широко поддерживаемым современным методом расстояния и выравнивания предметов. Вот почему я использую его на меню все время. Всего за несколько строк кода я могу равномерно расположены, вертикально центрированные пункты меню. Это также облегчает повторное использование кода меню между сайтами – поскольку меню построено, чтобы быть гибким, меньше необходимо изменить.

Поэтому, когда я не мог найти никого, создавая приоритетную навигацию с моим любимым Flexbox, я был полон решимости исправить проблему. И теперь я собираюсь поделиться с тобой, как я это сделал.

Решение

HTML

Во-первых, некоторые тесты HTML для работы с. Когда я изначально создал это меню, я сделал его в использовании с темой WordPress. Поскольку WordPress является популярной платформой, для этого примера я буду придерживаться совместимого WordPress Code.

Вот заголовок HTML, который я вытащил с сайта WordPress, затем модифицирован для удаления идентификаторов:

Вот кодепен того же: @ Приоритетная навигация Flexbox – Шаг 1: HTML

Скриншот кодепена 1.png.

CSS.

Логотип и меню

Далее, конечно, заголовок должен быть создан. Логотип и меню каждому нужно свое собственное пространство определено. Большинство логотипов будут иметь идеальную ширину дисплея на сайте, поэтому я установлю мой #branding пространство для 15rem (240px на основе размера шрифта браузера 16px):

#branding {
  width: 15rem;
  float: left;
}

Так как я добавил маржу на мой «логотип» внутри #branding Div, нет никакого места между #branding и NAV контейнеры.

#siteIdentity {
  margin-right: 2.5rem;
}

Поэтому NAV ширина просто 100% минус ширина #branding :

nav {
  width: calc(100% - 15rem);
  float: right;
}

Добавление Flexbox в меню

Время добавить Flexbox в меню! Поскольку элементы меню будут скрываться, когда они переполняют, упаковка не требуется ни не требуется. (Примечание: префиксы браузера должны поддерживать старые браузеры.)

#menu-main-menu {
  -ms-flex-pack: end;
  flex-pack: end;
  -webkit-justify-content: flex-end;
  -moz-justify-content: flex-end;
  -ms-justify-content: flex-end;
  justify-content: flex-end;
}

#menu-main-menu > li {
  position: relative;
  text-align: center;
  -webkit-box-flex: 0 0 auto;
  -moz-box-flex: 0 0 auto;
  width: 0 0 auto;
  -webkit-flex: 0 0 auto;
  -ms-flex: 0 0 auto;
  flex: 0 0 auto;
  padding: 1.75rem 0;
}

#menu-main-menu > li:not(.hidden-links),
.menu-main-menu-container button {
  white-space: nowrap;
}

Скрытие подмесу

Наконец, подменусы должны быть скрыты:

ul#menu-main-menu li > ul {
  display: none;
  position: absolute;
  top: 100%;
  background: #fff;
  padding: 0;
}

Результат

Вот кодепен, показывающий, как это выглядит со всеми моими CSS, включая детали, подчеркиваемые выше: @ Flexbox Priority Навигация – Шаг 2: CSS

Codepen_screenshot_2.png.

jquery

Очевидно, что есть еще работа. CSS заставляет его выглядеть лучше, но элементы меню работают прямо над логотипом, когда уделяется шанс. Что нам нужно добавить, чтобы сделать пункты меню исчезнуть под «более» меню? JavaScript Отказ Приоритетная навигация не работает без JavaScript. В этом случае, поскольку я работал с WordPress, и jQuery уже включен, я решил использовать библиотеку jQuery для облегчения доступа к DOM. (ПРИМЕЧАНИЕ. Использование «jQuery», а не долларовой знак «$» из-за WordPress, не позволяя знаку доллара по умолчанию.)

На странице загрузки

Наиболее распространенный способ скрыть элементы меню – и метод, который я выполнил – заключается в том, чтобы рассчитать ширину. Я подумал, что лучше всего хранить ширину каждого пункта меню на странице нагрузки. Я также добавляю мою «больше» подменю динамически на странице загрузки – еще раз, потому что в то время я работал со сгенерированной WordPress меню. (Если бы у кого-то была возможность добавить элемент в HTML, это было бы более эффективно.) Остальная часть jQuery (документ). Соедините Функция просто для обработки раскрывающихся щелчков.

/ global variables
var navItems = [];
var navItemWidth = [];
var navItemVisible = [];
var moreWidth = 0;
var winWidth = 0;

jQuery(document).ready(function () {
  winWidth = jQuery(window).width();
  
  navItems = jQuery('#menu-main-menu > li');
  
  // get width of each item, and list each as visible
  navItems.each(function () {
    var itemWidth = jQuery(this).outerWidth();
    navItemWidth.push(itemWidth);
    navItemVisible.push(true);
  });
  
  // add more link
  jQuery('#menu-main-menu').append('');
  moreWidth = jQuery('#menu-more').outerWidth();
  
  // toggle sub-menu
  jQuery('#menuMoreLink').click(function(event) {
    event.preventDefault();
    jQuery('.menu-item-has-children:not(#menu-more)').removeClass('visible');
    jQuery(this).parent('.menu-item-has-children').toggleClass('visible');
  });
  
  // collapse all sub-menus when user clicks off
  jQuery('body').click(function(event) {
    if (!jQuery(event.target).closest('.menu-item').length) {
      jQuery('.menu-item-has-children').removeClass('visible');
    }
  });
  
  jQuery('.menu-item-has-children a').click(function(e) { e.stopPropagation(); });
  jQuery('.menu-item-has-children ul').click(function(e) { e.stopPropagation(); });
  jQuery('.menu-item-has-children li').click(function(e) { e.stopPropagation(); });
  
  // toggle all sub-menus
  jQuery('.menu-item-has-children').click(function(event) {
    if (!jQuery(this).hasClass('visible')) {
      jQuery(this).siblings('.menu-item-has-children').removeClass('visible');
      jQuery(this).addClass('visible');
    }
    else {
      jQuery(this).removeClass('visible');
    }
  });
  
  // format navigation on page load
  formatNav();
  
  // watch for difference between touchscreen and mouse
  watchForHover();
});

Форматирование навигации

Настоящая волшебство происходит в Форматнав функция. Он петли через каждое пункт меню верхнего уровня, добавляя ширину элемента и сравнивая их с доступным пространством. Если не хватает достаточно места, он перемещает элемент в подменю «Дополнительное». Если доступно больше места, чем было ранее, он выходит из пунктов меню из подменю «Дополнительное» подменю и повторно вставляет их в навигацию верхнего уровня.

function formatNav () {
  // initial variables
  var room = true;
  var count = 0;
  var tempWidth = 0;
  var totalWidth = 0; 
  var containerWidth = jQuery('.menu-main-menu-container').innerWidth();
  var navPadding = 5; // for spacing around items
  var numItems = navItems.length - 1;
  
  // for each menu item
  navItems.each(function () {
    // get width of menu with that item
    tempWidth = totalWidth + navItemWidth[count] + navPadding;
    
    // if the menu item will fit
    if (((tempWidth < (containerWidth - moreWidth - navPadding)) || ((tempWidth < (containerWidth)) && (count == numItems))) && (room == true)) {
      // update current menu width
      totalWidth = tempWidth;
      
      // show menu item
      if (navItemVisible[count] != true) {
        // move back to main menu
        jQuery('#menu-more').before(jQuery('#moreSubMenu').children().first());
        
        navItemVisible[count] = true;
        
        // if all are visible, hide More
        if (count == numItems) {
          jQuery('#menu-more').hide();
        }
      }
    }
    // if the menu item will not fit
    else {
      // if there is now no room, show more dropdown
      if (room == true) {
        room = false;
        
        // change text to "Menu" if no links are showing
        if (count == 0) {
          jQuery('nav').addClass('all-hidden');
          jQuery('#menuMoreLink').text("Menu");
        }
        else {
          jQuery('nav').removeClass('all-hidden');
          jQuery('#menuMoreLink').text("More");
        }
      
        jQuery('#menu-more').show();
      }
      
      // move menu item to More dropdown
      jQuery(this).appendTo(jQuery('#moreSubMenu'));
      
      navItemVisible[count] = false;
    }
    
    // update count
    count += 1;
  });
}

На окне размера

Каждый раз, когда ширина страницы меняется, однако, меню меняется. Я создал функцию под названием onresize Это обрабатывает такое событие. Из-за использования Flexbox, который вызывает изменение ширины, ширина каждого элемента должна быть пересчитана, после чего Форматнав Функция может быть вызвана снова. Чтобы сохранить эту функцию неоднократно вызываться в случае, если пользователь играет с размером окна на некоторое время, я добавил тайм-аут для jquery (окно) .resize так что onresize будет называться только после того, как изменение размера остановилась на полторы.

/ format navigation on page resize
var id;
jQuery(window).resize(function() {
    clearTimeout(id);
    id = setTimeout(onResize, 500);
});

function onResize () {
  if(winWidth != jQuery(window).width()){
    // get width of each item, and list each as visible
    var count = 0
    navItems.each(function () {
      var itemWidth = jQuery(this).outerWidth();
      if (itemWidth > 0) {
        navItemWidth[count] = itemWidth;
      }
    });

    moreWidth = jQuery('#menu-more').outerWidth();

    // hide all submenus
    jQuery('.menu-item-has-children').removeClass('visible');

    formatNav();
    
    winWidth = jQuery(window).width();
  }
}

Поддержка зависания

Я изначально разработал меню для работы на Click/Touch, так как Menus Hover требует точности мышью для использования. Тем не менее, я вскоре обнаружил, что пользователи предпочитают иметь возможность зависать, поэтому я добавил функцию в JavaScript, которая отличающаяся между сенсорным экраном и мышью, добавляя класс «Havover» в элемент тела, когда пользователь обладает наплечными возможностями:

function watchForHover() {
  var hasHoverClass = false;
  var lastTouchTime = 0;

  function enableHover() {    
    // filter emulated events coming from touch events
    if (new Date() - lastTouchTime < 500) return;
    if (hasHoverClass) return;

    jQuery('body').addClass('has-hover');
    hasHoverClass = true;
  }

  function disableHover() {
    if (!hasHoverClass) return;

    jQuery('body').removeClass('has-hover');
    hasHoverClass = false;
  }

  function updateLastTouchTime() {
    lastTouchTime = new Date();
  }

  document.addEventListener('touchstart', updateLastTouchTime, true);
  document.addEventListener('touchstart', disableHover, true);
  document.addEventListener('mousemove', enableHover, true);

  enableHover();
}

Я также добавил поддержку зависания CSS, чтобы при использовании зависания ( body.has-hover ) и li.menu-item-есть дети Будет зависеть, подкранже будет иметь Дисплей: блок; и Должность: родственник; применяемый.

Заключение

Сделать все вместе, вот кодепен окончательного результата: @ Приоритетная навигация Flexbox – Шаг 3: jQuery

Кодепен скриншот 3.png.

Вы заметите, что это не идеально. Наличие подметра, вложенных в подменю, которое расширяет вниз, в порядке, когда переключается через щелчок, но при пакете, пункты меню прыгают и некоторые могут быть недоступны. Действительно, суб-меню, как правило, проблематичны для приоритетных навигаций, которые лучше всего работают, когда нет суб-меню. Я попробовал решение для этого, которое открыло подменю только тогда, когда стрелка вниз зависала, которая функционировала, но не обязательно неактуально. Другие предложили перемещать поддесящие меню справа или налево, а не расширяя их в раскрывающемся списке подменю, что переключает сложность для обеспечения того, чтобы элементы меню не теряют со страницы.

Существует также много вариантов для приоритетной навигации, которая более легкими, чем тот, который я только что изложил. Часть этого связано с моей решимостью использовать Flexbox. Другая часть заключается в том, что я вытащил код с полностью построенного сайта, построенного в реальных условиях с бюджетами и сроками, и, в то время как я убрал некоторые ненужные биты, я уверен, что больше я пропустил. Однако даже принимает во внимание все эти факторы, я уверен, что другие будут способны достичь одинакового в меньшем количестве строк кода. Итак, вообще, возьмите это и улучшите его!

Конечно, это решение не для всех. Не все люди делятся моей фиксацией с Flexbox. Есть случаи, в которых инструмент, как Flexbox, не нужен для чистой, хорошо расположенной навигации.

Но в те времена, когда расстояние и вертикальное выравнивание должно быть больше. Отказ Отказ гибкий? Кто-то может найти, что это пригодится.