04 февраля, 2020

Делаем на Тильде сайдбар с фиксацией при скролле

Так как в Тильде не реализован блок старой доброй боковой панели, хочу рассказать, как самому сделать фиксированный сайдбар с помощью Zero-блока.
Сложность фиксированного сайдбара не в нем самом, а в том, чтобы он правильно "прилипал" при прокрутке страницы. В данной статье я рассмотрю три типа сайдбара и как их можно реализовать на конструкторе Tilda.

Но сначала я вкратце расскажу как сделать простую боковую панель без фиксации.

Сетка Тильды состоит из 12 колонок, пусть 3 крайние справа будет занимать наш сайдбар, 8 - контент сайта, а оставшаяся колонка послужит отступом между ними.

Сайдбар создаем в Zero-блоке примерно как показано на скриншоте:
Все объекты позиционируем относительно левого края Window Container и сдвигаем влево в самое начало контейнера окна (такое положение элементов необходимо задать из-за сдвига зеро-блока, которое будет производиться с помощью css-кода).

Обратите внимание, что у Zero-блока должен быть прозрачный фон.

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

Далее идет блок с html-кодом, в котором нужно прописать некоторые правила для корректного отображения сайдбара:

1. Смещаем контент вверх так, чтобы он оказался напротив сайдбара.

div[id="rec158841053"] /*идентификатор блока контента*/
{
    margin-top: -540px; /*540рх высота сайдбара*/
}
2. Теперь контент и боковая панель находятся на одной высоте. Но тут возникает одна проблема - блок сайдбара перекрывает содержимое сайта, из-за чего с ним невозможно взаимодействовать. Один из вариантов, как можно решить сложившуюся ситуацию - сдвинуть вправо элемент <div>, содержащий сайдбар (именно для этого мы выровняли все элементы зеро-блока по левому краю).

div[id="rec158836396"] /*идентификатор блока сайдбара*/
{
    margin-left: calc(50% + 320px); /*сдвиг на 320рх от центра*/
}
3. Не забудьте про адаптивность. Я рекомендую скрыть сайдбар на экранах меньше 980рх, или перенести его вниз, создав дубликат zero-блока и настроив его диапазон видимости.

Для экранов <1200рх сайдбар нужно сдвинуть ближе к центру:

@media (max-width: 1200px) {
    div[id="rec159275709"] {
        margin-left: calc(50% + 180px);
    }
}
Боковая панель готова! Но пока без фиксации. А чтобы ее настроить, прочтите руководство для сайдбара нужного вам типа.
Тип 1: Короткий сайдбар с фиксацией на всю высоту страницы
Самый простой тип сайдбара, реализуется несколькими строчками CSS кода. Ниже приведен весь необходимый код, включая уже упомянутый:

<style>
@media (min-width: 981px) 
{
    div[id="rec158836396"] /*идентификатор блока сайдбара*/
    {
        position: -webkit-sticky; /*липкое позиционирование*/
        position: sticky;
        top: 40px; /*расстояние от верха экрана*/
        margin-bottom: 530px; /*расстояние до низа страницы*/
        margin-left: calc(50% + 320px); /*сдвиг на 320рх от центра*/
    }
    div[id="rec158841053"] /*идентификатор блока контента*/
    {
        margin-top: -1070px; /*сдвиг вверх*/
    }
}    

@media (max-width: 1200px) {
    div[id="rec158836396"] {
        margin-left: calc(50% + 180px);
    }
}
</style>
  • Весь код выполняется при разрешении экранов >980рх, т.к. при меньшем сайдбар скрыт.
  • top: 40px; - это расстояние, на котором сайдбар "прилипнет" к верху экрана. Этот параметр обязательно нужно указать.
  • margin-bottom: 530px; - расстояние до нижнего края страницы, на котором сайдбар перестанет "прилипать" и не наедет на подвал.
  • margin-top: -1070px; - расстояние, на которое поднимается вверх блок контента, складывается из высоты сайдбара и отступа под ним, в данном случае: 540рх + 530рх.
Тип 2: Короткий сайдбар с фиксацией на определенную высоту
Данный тип сайдбара фиксируется на расстояние, равное высоте блока напротив. Если бы в верстке у боковой панели и контента был общий родительский блок, то зафиксировать сайдбар можно было бы как и в примере выше. Но в нашем случае придется применить скрипты (за исходный код спасибо Шпаргалке).

<style>
    .sticky /*блок зафиксирован*/
    {
        position: fixed;
        z-index: 101;
    }
    .stop /*блок не зафиксирован*/
    {
        position: relative;
        z-index: 101;
    }
    
@media (min-width: 981px) 
{
    div[id="rec158836396"] /*идентификатор блока сайдбара*/
    {
        margin-left: calc(50% + 320px); /*сдвиг на 320рх от центра*/
    }

    div[id="rec158841053"] /*идентификатор блока контента*/
    {
        margin-top: -1070px; /*сдвиг вверх*/
    }
}    
@media (max-width: 1200px) {
    div[id="rec159275709"] {
        margin-left: calc(50% + 180px); /*позиционирование сайдбара*/
    }
}
</style>
CSS код включает в себя нашу стартовую заготовку и два новых класса - .sticky и .stop. С помощью них мы будет фиксировать и останавливать сайдбар.

<script>
(function() {

//сюда вставляем ID сайдбара и Р - отступ сверху
var a = document.querySelector('#SideBar'), b = null, P = 40;

window.addEventListener('scroll', Ascroll, false);
document.body.addEventListener('scroll', Ascroll, false);
function Ascroll() {
  if (b == null) {
    var Sa = getComputedStyle(a, ''), s = '';
    for (var i = 0; i < Sa.length; i++) {
      if (Sa[i].indexOf('overflow') == 0 || Sa[i].indexOf('padding') == 0 || Sa[i].indexOf('border') == 0 || Sa[i].indexOf('outline') == 0 || Sa[i].indexOf('box-shadow') == 0 || Sa[i].indexOf('background') == 0) {
        s += Sa[i] + ': ' +Sa.getPropertyValue(Sa[i]) + '; '
      }
    }
    b = document.createElement('div');
    b.style.cssText = s + ' box-sizing: border-box; width: ' + a.offsetWidth + 'px;';
    a.insertBefore(b, a.firstChild);
    var l = a.childNodes.length;
    for (var i = 1; i < l; i++) {
      b.appendChild(a.childNodes[1]);
    }
    a.style.height = b.getBoundingClientRect().height + 'px';
    a.style.padding = '0';
    a.style.border = '0';
  }
  var Ra = a.getBoundingClientRect(),
      R = Math.round(Ra.top + b.getBoundingClientRect().height - document.querySelector('#Content').getBoundingClientRect().bottom);
// сюда вставляем ID блока, при достижении нижнего края которого нужно открепить прилипающий сайдбар

  if ((Ra.top - P) <= 0) {
    if ((Ra.top - P) <= R) {
      b.className = 'stop';
      b.style.top = - R +'px';
    } else {
      b.className = 'sticky';
      b.style.top = P + 'px';
    }
  } else {
    b.className = '';
    b.style.top = '';
  }
  window.addEventListener('resize', function() {
    a.children[0].style.width = getComputedStyle(a, '').width
  }, false);
}
})()
</script>
Скопируйте этот скрипт в блок с html-кодом, поменяв в нем следующие параметры:
  • ('#SideBar') замените на ID сайдбара, например ('#rec158836396')
  • Переменной Р задайте число, равное расстоянию в рх, на котором должен прилипнуть сайдбар к верху экрана
  • ('#Content') замените на ID блока напротив сайдбара, например ('#rec158841053')
Тип 3: Длинный плавающий сайдбар
Длинный сайдбар, не помещающийся целиком в экран, при скроллинге вниз прилипает, когда его нижний край касается нижнего края экрана. И, наоборот, при скроллинге вверх прилипает, когда верхний край касается верхнего края экрана. То есть боковая панель как бы "плавает" между двух границ экрана до тех пор, пока находится напротив определенного блока.

CSS код тот же, что и в предыдущем примере. А скрипт выглядит следующим образом:

<script>
(function() {

//сюда вставляем ID сайдбара, Р - отступ сверху, N - отступ снизу
var a = document.querySelector('#SideBar'), b = null, K = null, Z = 0, P = 40, N = 40;

window.addEventListener('scroll', Ascroll, false);
document.body.addEventListener('scroll', Ascroll, false);
function Ascroll() {
  var Ra = a.getBoundingClientRect(),
      R1bottom = document.querySelector('#Content').getBoundingClientRect().bottom;
      // сюда вставляем ID блока напротив сайдбара

  if (Ra.bottom < R1bottom) {
    if (b == null) {
      var Sa = getComputedStyle(a, ''), s = '';
      for (var i = 0; i < Sa.length; i++) {
        if (Sa[i].indexOf('overflow') == 0 || Sa[i].indexOf('padding') == 0 || Sa[i].indexOf('border') == 0 || Sa[i].indexOf('outline') == 0 || Sa[i].indexOf('box-shadow') == 0 || Sa[i].indexOf('background') == 0) {
          s += Sa[i] + ': ' +Sa.getPropertyValue(Sa[i]) + '; '
        }
      }
      b = document.createElement('div');
      b.className = "stop";
      b.style.cssText = s + ' box-sizing: border-box; width: ' + a.offsetWidth + 'px;';
      a.insertBefore(b, a.firstChild);
      var l = a.childNodes.length;
      for (var i = 1; i < l; i++) {
        b.appendChild(a.childNodes[1]);
      }
      a.style.height = b.getBoundingClientRect().height + 'px';
      a.style.padding = '0';
      a.style.border = '0';
    }
    var Rb = b.getBoundingClientRect(),
        Rh = Ra.top + Rb.height,
        W = document.documentElement.clientHeight,
        R1 = Math.round(Rh - R1bottom),
        R2 = Math.round(Rh - W);
    if (Rb.height > W) {
      if (Ra.top < K) {  // скролл вниз
        if (R2 + N > R1) {  // не дойти до низа
          if (Rb.bottom - W + N <= 0) {  // подцепиться
            b.className = 'sticky';
            b.style.top = W - Rb.height - N + 'px';
            Z = N + Ra.top + Rb.height - W;
          } else {
            b.className = 'stop';
            b.style.top = - Z + 'px';
          }
        } else {
          b.className = 'stop';
          b.style.top = - R1 +'px';
          Z = R1;
        }
      } else {  // скролл вверх
        if (Ra.top - P < 0) {  // не дойти до верха
          if (Rb.top - P >= 0) {  // подцепиться
            b.className = 'sticky';
            b.style.top = P + 'px';
            Z = Ra.top - P;
          } else {
            b.className = 'stop';
            b.style.top = - Z + 'px';
          }
        } else {
          b.className = '';
          b.style.top = '';
          Z = 0;
        }
      }
      K = Ra.top;
    } else {
      if ((Ra.top - P) <= 0) {
        if ((Ra.top - P) <= R1) {
          b.className = 'stop';
          b.style.top = - R1 +'px';
        } else {
          b.className = 'sticky';
          b.style.top = P + 'px';
        }
      } else {
        b.className = '';
        b.style.top = '';
      }
    }
    window.addEventListener('resize', function() {
      a.children[0].style.width = getComputedStyle(a, '').width
    }, false);
  }
}
})()
</script>
Скопируйте этот скрипт в блок с html-кодом, поменяв в нем следующие параметры:
  • ('#SideBar') замените на ID сайдбара, например ('#rec158836396')
  • Переменной Р задайте число, равное расстоянию в рх, на котором должен прилипнуть сайдбар к верху экрана
  • Переменной N задайте число, равное расстоянию в рх, на котором должен прилипнуть сайдбар к низу экрана
  • ('#Content') замените на ID блока напротив сайдбара, например ('#rec158841053')
Заключение
Любые нестандартные для Тильды решения приходится воплощать в жизнь благодаря различным "костылям", вроде этих скриптов. И пусть код из данной статьи не идеальный пример чистого кода, зато он работает :)

И помните, если сайдбар на вашем сайте отличается от приведенных в примере, будьте предельно внимательны при применении кода. Возможно, его даже придется сильно кастомизировать. В любом случае, не стесняйтесь писать комментарии с вопросами! Я обязательно вам отвечу.
Понравилась статья?
Для статьи использованы материалы
Похожие статьи

Выпадающее меню на zero-блоке дает неограниченные возможности в создании меню с уникальным и неповторимым дизайном, и сделать его совсем не сложно

Я покажу вам, как я создаю логотип и бренд за семь дней - от вдохновения до сдачи проекта.

Комментарии
Как сделать выпадающее меню из zero-блока на Тильде
Следующая статья
Напишите мне
Хотите нанять меня? Или узнать больше о моей работе? Свяжитесь со мной, я буду рада ответить на все ваши вопросы или обсудить с вами наш новый проект.