Статьи

В центре внимания: закрепленные стикеры с jQuery

Каждую неделю мы будем ультра-сфокусированно смотреть на интересный и полезный эффект, плагин, хакер, библиотеку или даже изящную технологию. Затем мы попытаемся либо разобрать код, либо создать забавный маленький проект с ним.

Сегодня мы собираемся взглянуть на плагин, который реализует довольно аккуратный эффект — это довольно сложно объяснить в предложении, так что вы можете также нажать на кнопку продолжения, чтобы начать после прыжка.


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

Эта серия статей призвана решить эту проблему, представив читателю действительно хорошо написанный и полезный код — будь то плагин, эффект или технология. Кроме того, если он достаточно мал, мы попытаемся разобрать код и понять, как он работает вуду. Если он намного больше, мы попытаемся создать мини-проект, чтобы изучить веревки и, надеюсь, понять, как использовать его в реальном мире.


Вот небольшая информация:

  • Тип: плагин
  • Технология: JavaScript [Построен на библиотеке jQuery]
  • Функция: плавающий контент только в пределах ограничений его родителя
  • Домашняя страница плагина: Здесь

Во многих случаях вам нужно, чтобы содержимое перемещалось при прокрутке, но только в пределах родительского элемента .

Плавающее содержимое при прокрутке пользователем остальной части страницы — детская игра. JavaScript не требуется — вы можете сделать это с помощью простого старого CSS. Ударьте position: fixed декларация и бум! У вас есть контейнер, который зафиксирован в определенном месте на странице — он плавает на странице, чтобы быть более разговорным.

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

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

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


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

Но прежде чем мы начнем, вот быстрый макет, чтобы показать иерархию:

Учебное изображение

Вся логика плагина может быть сведена к:

  • Вычислить текущую позицию родительского элемента липкого элемента относительно документа. Помечено как 1 на изображении.
  • Получите также рост родителя — так что мы будем знать, когда прекратить плавать, когда мы пройдем мимо родителя. Помечено как 2.
  • Рассчитайте, как далеко страница была прокручена вниз — чтобы узнать, смотрим ли мы на родителя, — чтобы узнать, находимся ли мы в диапазоне. На изображении выше горизонтальная линия отмечает гипотетическую вершину текущего видового экрана. В этом случае это значение будет расстоянием между точками, обозначенными как 3.
  • Используя два значения, которые мы вычислили выше, мы можем очень быстро выяснить, нужно ли должным образом изменить положение стикера.

Если вы запутались, не надо. Например, давайте посмотрим на некоторые номера образцов:

  • Родитель липкого присутствует в 10px от верхней части страницы.
  • Родитель имеет высоту 100px .
  • Страница была прокручена 50px в одном сценарии и 150px в другом.

Таким образом, на основе этой информации, вы можете сделать вывод, что

В первом сценарии липкое должно быть соответствующим образом перемещено. Почему? Страница была прокручена на 10 пикселей сверху — 10 — от самой страницы, а остальное — от родителя стикера. Таким образом, родительский элемент виден в главном окне просмотра.

В сценарии два — липкое можно оставить в покое. Из 150 пикселей 10 поступают со страницы, 100 — с родительского элемента, а остальное — с остальным элементом страницы. Это означает, что пользователь прокрутил мимо родителя, и нам не нужно ничего делать.

Если вы все еще не уверены, не беспокойтесь. Я объясню немного больше, прогуливаясь по источнику.


Источник, лишенный комментариев, представляет собой лишь смидген длиной более 30 строк. Как всегда, мы пройдемся по коду и объясним, что делает каждая строка.

Вот источник, для вашей справки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$.fn.stickyfloat = function(options, lockBottom) {
                var $obj = this;
                var parentPaddingTop = parseInt($obj.parent().css(‘padding-top’));
                var startOffset = $obj.parent().offset().top;
                var opts = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);
                 
                $obj.css({ position: ‘absolute’ });
                 
                if(opts.lockBottom){
                    var bottomPos = $obj.parent().height() — $obj.height() + parentPaddingTop;
                    if( bottomPos < 0 )
                        bottomPos = 0;
                }
                 
                $(window).scroll(function () {
                    $obj.stop();
 
                    var pastStartOffset = $(document).scrollTop() > opts.startOffset;
                    var objFartherThanTopPos = $obj.offset().top > startOffset;
                    var objBiggerThanWindow = $obj.outerHeight() < $(window).height();
                     
                    if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){
                        var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );
                        if ( newpos > bottomPos )
                            newpos = bottomPos;
                        if ( $(document).scrollTop() < opts.startOffset )
                            newpos = parentPaddingTop;
             
                        $obj.animate({ top: newpos }, opts.duration );
                    }
                });
            };

Время посмотреть, что на самом деле. Я собираюсь предположить, у вас есть довольно базовое понимание JavaScript.

1
$.fn.stickyfloat = function(options, lockBottom) {};

Шаг 1 — Общая оболочка для плагина jQuery. Как вы, вероятно, знаете, options — это объект, содержащий разные параметры для настройки поведения плагина. lockBottom , что lockBottom , lockBottom ли lockBottom нам функциональность или нет. Мы оставим это включенным.

1
var $obj = this;

Шаг 2 — Сохраните ссылку на переданный элемент. В этом контексте this указывает на элемент DOM, который соответствует селектору, который вы передали. Например, если вы передали в #menu , this указывает на элемент с этим идентификатором.

1
var parentPaddingTop = parseInt($obj.parent().css(‘padding-top’));

Шаг 3 — Это просто для сглаживания эффекта, если родительский элемент имеет большой отступ. Если это так, это будет включать отступы в расчете.

1
var startOffset = $obj.parent().offset().top;

Шаг 4 — Мы вычисляем позицию родителя относительно документа, используя метод offset jQuery. Мы работаем через DOM, используя parent метод. Мы $obj так как мы уже кэшировали липкое. Обратитесь к документации по jQuery API, если вы не знакомы с этими методами.

В этом случае расстояние от вершины достаточно, поэтому мы получим только это значение.

1
var opts = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);

Шаг 5 — Довольно общая часть процесса разработки плагина jQuery. По сути, мы объединяем переданные параметры с некоторыми предустановками, чтобы получить окончательный набор параметров, которые используются во всем коде. Имейте в виду, что переданные параметры всегда имеют приоритет над значениями по умолчанию.

1
$obj.css({ position: ‘absolute’ });

Шаг 6 — Эффект, о котором идет речь, будет создан путем манипуляции с top значением CSS элемента, поэтому мы просто продолжим и установим его позицию в абсолютное значение, если он еще не был установлен таким образом.

1
2
3
4
5
if(opts.lockBottom){
                   var bottomPos = $obj.parent().height() — $obj.height() + parentPaddingTop;
                   if( bottomPos < 0 )
                       bottomPos = 0;
               }

Шаг 7 — Как отмечено выше, опция lockBottom указывает, работает ли рассматриваемый эффект или нет. Если включено, мы можем начать расчет. То, что мы рассчитываем, это точка отсечения, после которой нам не нужно было бы менять положение залипания.

Естественно, вы можете просто рассчитать рост родителя, но эффект не будет определен. Вам нужно будет учитывать высоту самой липкой ленты вдоль любых отступов на самом родителе.

1
$(window).scroll(function () { // Lots of code })

Шаг 8 — Мы подключаем наш код внутри анонимной функции к событию scroll в Windows. Конечно, это не самый эффективный способ, но пока мы его проигнорируем.

1
$obj.stop();

Шаг 9 — Первый порядок выражения — остановить все запущенные анимации на липком элементе. Метод stop позаботится об этом.

1
2
3
var pastStartOffset = $(document).scrollTop() > opts.startOffset;
var objFartherThanTopPos = $obj.offset().top > startOffset;
var objBiggerThanWindow = $obj.outerHeight() < $(window).height();

Шаг 10 — Эти три переменные содержат значения, которые мы будем использовать чуть позже.

  • pastStartOffset проверяет, прокрутили ли мы за верхнюю границу родительского элемента. Помните, что мы использовали метод offset чтобы узнать расстояние между родительским элементом и документом. Мы получаем, как далеко вы прокрутили, используя метод scrollTop . Это расстояние между верхней частью документа и верхней частью текущего видового экрана.
  • objFartherThanTopPos проверяет, находится ли objFartherThanTopPos в своем положении по умолчанию — в верхней части его родителя. Если мы прокрутили за пределы родительского элемента, мы не хотим, чтобы он плавал снаружи.
  • objBiggerThanWindow проверяет, больше ли общая высота objBiggerThanWindow , чем размер окна. Если это так, то нет смысла манипулировать липким элементом.
1
if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){ // More code }

Шаг 11 — Здесь плагин вычисляет, нужно ли нам манипулировать липким элементом. Что вышеупомянутая строка делает это:

  • Проверьте, выполняет ли пользователь точную прокрутку в диапазоне родительского элемента. Мы проверяем, находится ли пользователь ниже верхней границы родителя или, в качестве альтернативы, липкая находится сверху.
  • Как отмечено выше, мы продолжаем, только если sticky меньше, чем размер окна.

Мы действуем, только если оба эти условия выполнены.

1
var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );

Шаг 12 — Эта строка определяет переменную newpos , которая указывает позицию, в которую должен быть анимирован липкий элемент. Как вы могли заметить, расчет довольно прост, если иметь в виду изображение выше. Определите расстояние прокрутки, добавьте к нему верхний отступ родителя и, наконец, вычтите расстояние между документом и родителем — начальную точку. Это дает вам расстояние в пикселях между вершиной родительского элемента и точкой внутри, где должна располагаться наклейка.

1
2
if ( newpos > bottomPos )
                            newpos = bottomPos;

Шаг 13 — Если мы прокрутили за нижнюю границу родительского элемента, не нужно больше манипулировать вещами. Зафиксируйте свою позицию там.

1
2
if ( $(document).scrollTop() < opts.startOffset )
                            newpos = parentPaddingTop;

Шаг 14 — Если мы прокрутили выше верхней границы родителя, держите его там заблокированным, чтобы он не двигался дальше вверх.

1
$obj.animate({ top: newpos }, opts.duration );

Шаг 15 — Все готово! Мы просто анимируем липкий элемент, передавая требуемое top значение вместе с продолжительностью эффекта, используя метод animate jQuery.


Как вы, вероятно, могли сделать вывод, использование выглядит примерно так:

1
$(‘#menu’).stickyfloat({ duration: 500 });>

Вместо объяснения примера мини-проекта, как в прошлый раз, я решил просто собрать его и дать вам код.

Вот соответствующие части демо, остальное является образцом:

1
2
3
4
5
6
7
8
9
<div class=»section»>
<div id=»menu» class=»menu»>Sticky menu</div>
<div class=»content»>I wanted to write something incredibly, unabashedly witty here.
</div>
 
<div class=»section»>
    <div id=»menu2″ class=»menu»>Yep, I’ll follow you everywhere as long as you’re within my parent</div>
    <div class=»content»>You were expecting something clever here, didn’t you?
    </div>
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.section {
    padding:10px;
    width:900px;
    margin:0 auto;
    background-color:#f1f1f1;
    position:relative;
}
 
.section .content {
    height:800px;
    background-color:#ddd;
    margin-left:250px;
    text-align:center;
    color:#333;
    font-size:16px;
}
 
.section .menu {
    position:absolute;
    left:10px;
    width:240px;
    height:100px;
    background: #06C;
    text-align:center;
    color:#fff;
    font-size:14px;
}
1
2
$(‘#menu’).stickyfloat({ duration: 400 });
$(‘#menu2’).stickyfloat({ duration: 400 });

Если вы просматриваете файлы во время прочтения этой статьи, это должно быть достаточно понятным, но вы можете задать мне вопросы, если какая-либо часть неясна.


И мы сделали. Мы взглянули на невероятно полезный плагин, просмотрели исходный код и, наконец, закончили с созданием мини-проекта с ним.

Вопросов? Хорошие вещи, чтобы сказать? Критицизмы? Нажмите на раздел комментариев и оставьте мне комментарий. Большое спасибо за чтение!