RizVN Login



Плавающий блок (меню) при прокрутке страницы на 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

1 1 1 1 1 1 1 1 1 1 Рейтинг 5.00 (1 Голос)

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

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
Спасибо большое! Все отображается как надо!
Ответить | Ответить с цитатой | Цитировать | Сообщить модератору

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

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


Введите защитный код

Обновить
Защитный код

Каталог программ