Плавающий блок (меню) при прокрутке страницы на jquery

Плавающий блок (меню) при прокрутке страницы на jquery

На сайтах, состоящих из нескольких колонок, часто встречается проблема пустого пространства при прокрутке страницы вниз. Когда после определенного момента скриллинга в одной из колонок образуется свободное место, которое смотрится некрасиво. Например, когда текст страницы вдвое, а то и втрое, превышает размеры колонок. Существует, как минимум, три возможных варианта решения этой проблемы. Во-первых, если колонка помещается на экране страницы (даже в тот момент, когда вы достигли футера), то ее можно просто зафиксировать css стилями. Во-вторых, если тексты на сайте примерно одинаковой длины, то колонку можно наполнить блоками до необходимого размера. И в-третьих, можно сделать последний блок или меню плавающим при прокрутке страницы на jquery (JavaScript).

В данной статье речь пойдет о третьем варианте, как о самом распространенном. А вот пример проблемы:

Пример проблемы или зачем нужен плавающий блок

 

Отслеживаем события window scroll (для прокрутки) и resize (для изменения высоты)

Из всего набора событий активного окна window интересуют только два - window scroll и window resize. Необходимо учитывать оба события, так как позиция блока должна корректироваться не только при скроллинге (прокрутке страницы), но и в том случае, если пользователь будет изменять размер окна, например, растягивать или уменьшать по высоте, как показано на рисунке ниже. В противном случае, плавающий блок может залезть на область футера.

События window scroll и window resize

Оба этих события легко отслеживаются JavaScript-ом, так что особых проблем с этим не возникнет. Кроме того, учтите, что если при изменении размера активного окна будет меняться разметка или расположение блоков, например, сжиматься ширина блока с текстом (например, на этом сайте есть два варианта ширины области контента для 1024 и для 1280+), то высота документа будет изменяться, из-за чего необходимо пересчитывать положение блока.

 

Делаем html заготовку с двумя колонками и добавляем блоки в сайдбар

Прежде всего создадим небольшую html-заготовку из двух файлов index.html и template.css

Примечание: В рамках рассмотрения не приводится набор стилей и полный код html, так как они не представляют особого интереса. Тем не менее, весь проект доступен в архиве по ссылке в конце статьи.

Первое, что нам понадобиться - это подключить jQuery c CDN:

<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>

Затем набросать разметку страницы:

<body>

    <div class="header">

        Плавающий блок на jquery

        

    </div>

    <div class="clear"></div>

    <div class="content"></div>

    <div class="sidebar">

        <div class="sidebar-block"></div>

        <div class="sidebar-block"></div>

        <div class="sidebar-block"></div>

        <div class="sidebar-block float"></div>

    </div>

    <div class="clear"></div>

    <div class="footer">

        Футер

    </div>

</body>

 

И наполнить их данными:

 
// Заполним страницу псевдоданными

function pseudoData() {

    // Заполним контент

    for(var i = 0; i < 15; i++) {

        $('.content').html(

            $('.content').html()

            + '<p>Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст '

            + 'Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст '

            + 'Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст Текст '

            + '</p>'

        );

    }

    

    // Заполним блоки сайдбара

    for(var i = 0; i < 10; i++) {

        $('.sidebar-block').each(function (index) { $(this).html($(this).html() + 'Блок ' + (index + 1) + '<br/>') });

    }

}


$(function () {

    // Заполним блоки всевдо данными

    pseudoData();

});



Должна получиться примерно следующая страница:

html-заготовка для плавающего блока

Как видите заготовка достаточно наглядно демонстрирует проблему свободного пространства при прокрутке.

Теперь, можно приступать к написанию кода. 

 

Пишем скрипт плавающего блока (меню) на jquery

Скрипт плавающего блока или меню на jquery состоит из нескольких частей. 

Во-первых, кроссбраузерная функция для добавления обработчиков window scroll и window resize.

// Функция добавления обработчика

function addEventToObject(object, type, callback) {

    if (object == null || typeof(object) == 'undefined') return;

    if (object.addEventListener) {

        object.addEventListener(type, callback, false);

    } else if (object.attachEvent) {

        object.attachEvent("on" + type, callback);

    } else {

        object["on"+type] = callback;

    }

};

Во-вторых, сама функция обработчик изменения положения плавающего блока (меню):

// Функция определения позиции плавающего блока

function updateColumnPosition() {

    // Находим последний элемент

    var element = jQuery('.sidebar-block:last'),

        // Вычисляем его высоту

        elementHeight = element.outerHeight(),

        // Получаем высоту футера

        footerHeight = jQuery('.footer').outerHeight(),

        // Получаем высоту шапки сайта

        headerHeight = jQuery('.header').outerHeight(),

        // Получаем текущую высоту открытого окна

        screenHeight = jQuery(window).height(),

        // Получаем высоту всего документа

        documentHeight = jQuery(document).height(),

        // Вычисляемый столбец высоты сверху, которую нельзя занимать

        topHeight = headerHeight,

        // Текущее положение скрола

        scrollTop = jQuery(document).scrollTop();

    

    // Для того, чтобы ширина не сбилась (в зависимости от ваших CSS),

    // то запоминаем ее 

    if(!element.data('isloaded')) {

        element.data('isloaded', true);

        element.css({ width: element.width() });

    }

    

    // Добавляем все высоты остальных блоков 

    element.parent().find('.sidebar-block:not(:last)').each(function () {

        topHeight += jQuery(this).outerHeight();

    });

 

    // Если необходимо фиксировать блок сверху, и он не залезет на футер,

    // то меняем его положение

    if (scrollTop - topHeight > 20 && documentHeight - scrollTop - 40 > elementHeight + footerHeight) {

        element.css({

            position: 'fixed',

            top: (20) + 'px'

        });

    }

    // Если блок нужно фиксировать, но элемент залезет на футер,

    // то выставляем его положение на максимально возможное удаление от начала страницы

    // (т.е. прижимаем к футеру)

    else if (scrollTop - topHeight > 20 && documentHeight - scrollTop - 40 < elementHeight + footerHeight) {

        element.css({

            position: 'fixed',

            top: (documentHeight - elementHeight - footerHeight - 20) - scrollTop + 'px'

        });

    }        

    // Если скрол не превысил границу для фиксации блока,

    // то возвращаем ему его значения    

    else if (scrollTop - topHeight < 20) {

        element.css({

            position: 'relative',

            top: 'inherit'

        });

    }

};

Несколько пояснений к функции. Расчет положения плавающего блока должен идти в зависимости от размера всех соседних блоков в колонке, так как если их высота изменится во время прокрутки (скроллинга), например, загрузятся картинки, то запоминание стартовой позиции приведет к тому, что плавающий блок при возвращении на начальную позицию начнет залезать на предыдущий блок, что не правильно. Так же, если использовать "position: absolute" (абсолютное позиционирования) вместо "position: fixed" (фиксированное позиционирование), то возможно возникновение эффекта "мерцания" блока из-за необходимости перерисовки элементов. Поэтому лучше всего использовать "position: fixed", в таком случае мерцания не будет.

И в-третьих, остается только повесить функцию на события scroll и resize.

$(function () {

    // Заполним блоки всевдо данными

    pseudoData();

    

    // Добавим обработчик для скроллинга

    addEventToObject(window, 'scroll', updateColumnPosition);

    // Добавим обработчик для изменения размера активного окна

    addEventToObject(window, 'resize', updateColumnPosition);

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

    updateColumnPosition();

});

Функцию updateColumnPosition необходимо вызвать не только по возникновению событий, но и сразу после загрузки dom-элементов, так как при обновлении страницы с помощью клавиши "F5", ни одно из событий не вызовется, а страница может быть уже прокручена вниз.

Собрав вместе все части и html-заготовку, получим плавающий блок, который будет останавливаться прямо перед футером, как показано на картинке:

Плавающий блок на jquery

Как видите, получилось достаточно простое решение, которое легко адаптировать под многие сайты, просто заменив селекторы у jQuery

Теперь, у вас под рукой всегда будет код для создания плавающего блока или меню.

Скачать проект можно по ссылке:

floating-block.zip

☕ Понравился обзор? Поделитесь с друзьями!

Комментарии / отзывы  

0 # Кристина 08.04.2017 01:25
Подскажите пожалуйста, как плавающему блоку сделать анимацию, чтобы он плавно "плавал", когда страница до него доходит?
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 09.04.2017 11:27
Игорь, здравствуйте!
Подскажите пожалуйста, как плавающему блоку сделать анимацию, чтобы он плавно "плавал", когда страница до него доходит?
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Игорь (Администратор) 10.04.2017 10:33
С небольшими нюансами, нужно подправить следующий код:

После блока
-----------------------------
// Добавляем все высоты остальных блоков
element.parent().find('.sidebar-block:not(:last)') .each(function () {
topHeight += jQuery(this).outerHeight();
});

-----------------------------

Заменить все до конца функции вот этим
-----------------------------
var save_scroll = element.data('save-scroll')
;

if (isNaN(save_scroll)) {
save_scroll = 0;
}

if (Math.abs(save_scroll - scrollTop) < 20) {
return;
}

// Если необходимо фиксировать блок сверху, и он не залезет на футер,
// то меняем его положение
if (scrollTop - topHeight > 20 && documentHeight - scrollTop - 40 > elementHeight + footerHeight) {
element.stop(true, true).css({position: 'fixed', top: (save_scroll - scrollTop) }).animate({
top: (20) + 'px'
},500, 'linear', function () {
element.data('save-scroll', scrollTop);
});
}
// Если блок нужно фиксировать, но элемент залезет на футер,
// то высставляем его положение на максимально возможное удаление от начала страницы
// (т.е. прижимаем к футеру)
else if (scrollTop - topHeight > 20 && documentHeight - scrollTop - 40 < elementHeight + footerHeight) {
element.stop(true, true).css({position: 'fixed', top: element.css('top') }).animate({
top: ((documentHeight - elementHeight - footerHeight - 20) - scrollTop) + 'px'
},500, 'linear', function () {
element.data('save-scroll', scrollTop);
});
}
// Если скрол не превысил границу для фиксации блока,
// то возвращаем ему его значения
else if (scrollTop - topHeight < 20) {
element.stop(true, true).animate({
top: '0px'
},500, 'linear', function () {
element.css({position: 'relative', top: '0px'});
element.data('save-scroll', scrollTop);
});
}
-----------------------------
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 10.04.2017 14:17
Спасибо большое! Сейчас попробую!
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 11.04.2017 15:42
Подскажите пожалуйста, а с css это тоже можно сделать? Что-то у меня не работает код(
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Игорь (Администратор) 12.04.2017 16:26
Вероятно что-то не то делаете. Нужно заменить в примере с 73 строки по 97 (включительно). С css можно сделать, но это привязка к html5 + расчеты позиционирования нужно корректировать.
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 27.05.2017 11:53
Здравствуйте!
Такой вопрос: если я знаю конкретную высоту .sidebar-block:last (т.к. вставила туда изображение), как правильно ее прописать в данном коде?
Спасибо!
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Игорь (Администратор) 28.05.2017 09:24
Здравствуйте
Код вычисляет высоту динамически, вам не нужно указывать высоту картинки.
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 28.05.2017 14:06
Если несложно, подскажите пожалуйста, в чем моя ошибка.
Заранее благодарю!
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Игорь (Администратор) 28.05.2017 14:54
У вас немного другие стили.
Находите строчку
Цитата:
footerHeight = jQuery('.article5').outerHeight(),
и меняете ее на
Цитата:
footerHeight = jQuery('.article5').outerHeight() + 200,
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору
0 # Кристина 31.05.2017 11:25
Спасибо большое! Все отображается как надо!
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору

Добавить комментарий / отзыв

Комментарий - это вежливое и наполненное смыслом сообщение (правила).



* Нажимая на кнопку "Отправить", Вы соглашаетесь с политикой конфиденциальности.
Присоединяйтесь
 

 

Программы (Freeware, OpenSource...)