Pull to refresh

JavaScript обёртка скроллбара в виде jQuery плагина

Reading time 6 min
Views 68K
Проблема стилизации скроллбара браузера до сих пор актуальна, несмотря на огромное количество скриптов, предназначенных для решения данной задачи. Рассмотрим реализацию стилизуемого скроллбара, максимально приближенного к родному поведению браузера.

Существует два основных подхода для стилизации скроллбара средствами HTML/CSS, каждый из которых имеет свои плюсы и минусы:

  • Эмуляция скролла средствами JavaScript
  • JavaScript обёртка над родным скроллом


Эмуляция скролла средствами JavaScript


Огромным преимуществом данного подхода является то, что это полностью JavaScript решение. Благодаря этому данное решение легче в разработке и является максимально кроссбраузерным (в том плане, что поведение скролла во всех браузерах будет одинаковым, не зависимо от особенностей реализации скролла браузера), оказывая минимальное влияние на HTML структуру и применяемые CSS стили. Подобным подходом пользуются jScrollPane, FleXcroll, Tiny Scrollbar и многие другие.

Однако сильная сторона данного подхода является одновременно и его слабой стороной:

  • Сложность или невозможность эмулировать/перехватить все события
  • JavaScript эмуляция работает медленнее родного скролла


JavaScript обёртка над родным скроллом


Данный подход заключается в том, что родной скролл скрывается средствами HTML/CSS, продолжая выполнять своё предназначение — прокрутку содержимого. Подобное решение является более предпочтительным по той причине, что родной скролл в любом случае работает лучше эмулированного. Многие пробовали создавать плагины на базе этого подхода, но из-за отсутствия каких-либо стандартов, кроссбраузерная реализация очень сложна, и накладывает определённые ограничения на HTML структуру и используемые стили CSS. По этой причине многие разработчики создают реализацию только вертикального скролла, который ведёт себя во всех браузерах более-менее предсказуемо.

Одна из неплохих реализаций подобного подхода была описана совсем недавно в статье Кроссбраузерная кастомизация системного скроллбара с достаточно подробным объяснением.

Изобретение трёхколёсного велосипеда

Одной из причин создания очередного велосипеда явилось то, что поиск по скриптам для стилизации скролла показал более 20 разнообразных плагинов, ни один из которых не подошёл для заказанного проекта. Большинство из известных плагинов не подошли по причине медленной работы при большом количестве инициализированных скроллов на странице. Другие же не подошли из-за неудобной реализации или недостаточной кроссбраузерности.

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

  • Поведение скролла должно быть максимально приближённым к родному
  • Вертикальный и горизонтальный скроллбар
  • Минимальная нагрузка на процессор
  • Полная стилизация внешнего вида средствами HTML/CSS
  • Возможность применить свою HTML структуру для скроллбара, либо указать плагину использовать уже существующий элемент в качестве скроллбара
  • Автоматическое изменение скролла при изменении содержимого или изменении размеров самого контейнера без необходимости переинициализации скрипта
  • Ширина и высота контейнера может быть установлена как в пикселях, так и в процентах, либо ограничена средствами CSS max-height/max-width
  • Скролл может находиться как в любом месте внутри контейнера, так и за его пределами
  • Автоматическое вычисление размеров скроллбара, либо значение, устанавливаемое через CSS стили
  • Поддержка IE7+, Firefox, Chrome, Opera, Safari
  • Возможность «из коробки» отключения скролла для мобильных браузеров


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

Избежать полной эмуляции скролла всё же не получилось — необходимо эмулировать поведение скролла, когда мышь находится над стилизуемым скроллом: отлавливать скролл мыши, drag&drop, клики по самому скроллу и стрелочкам. Идеального решения «Подключил и забыл», конечно же, не получилось. Но практически всех поставленных целей удалось добиться в полной мере.

Демонстрация

Исходные файлы

Сравнение функциональности популярных скроллбаров


На заметку


Следует помнить, что для работы стилизованного скроллбара необходима определённая HTML структура, для которой исходный контейнер JavaScript-ом оборачивается в другой контейнер при инициализации. По этой причине сущестуют некоторые особенности использования, стуктуры HTML и стилей CSS:

  1. Исходный контейнер не должен содержать инлайновые стили
  2. Следует избегать использования padding/margin для контейнера. Если нужен отступ, лучше добавить внутрь ещё один контейнер с необходимым отступом
  3. При изменении содержимого контейнера не стоит обращаться к контейнеру по классу, т.к. эти классы будут использоваться и контейнером-обёрткой


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

Также пока не удаётся избавиться от неприятного эффекта прокрутки контейнера при выделении текста в WebKit браузерах Chrome и Safari. Частично удалось это сделать при использовании outer-скролла (есть в демонстрации).

Примечания


Из-за разности поведения скролла не только в разных браузерах, но и в разных версиях одного и того же браузера (да-да, того самого браузера), пришлось перепробовать множество комбинаций margin/padding/border/position для вычисления схожего поведения. Во время поиска решения, нашлось несколько интересных особенностей:

  • В ИЕ8 — баги с использованием max-height: в некоторых случаях рендерер 8-й версии ИЕ крашится, либо пытается сделать высоту контейнера равной max-height (официальный баг релиз версии)
  • WebKit-браузеры (Chrome, Safari) при выделении текста делают прокрутку внутреннего контейнера, «обнажая» скрытый скроллбар
  • Если содержимое превышает размеры контейнера, то отображение правого и нижнего паддинга остаётся на усмотрение самого браузера
  • При поиске плагинов, чтобы быстро узнать используется ли стандартный механизм скроллинга, достаточно нажать на скролл мыши, находясь над прокручиваемым контентом


Обновление до версии 0.1.3 (20130425):


  • улучшена эмуляция скролла: теперь скролл колёсиком мыши над эмулируемым скроллом работает почти идентично натуральному скроллу; также изменено поведение при нажатии и удержании мыши на подложке скроллбара
  • исправлено CSS позиционирование, при котором происходил «лишний» сдвиг контейнера при выделении текста в WebKit браузерах Google Chrome / Safari


Обновление до версии 0.1.4 (20130430):


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


Обновление до версии 0.1.6 (20131018):


  • добавлена опция игнорирования инициализации скролла на мобильных устройствах
  • добавлена поддержка drag для скролла на тач-устройствах


Обновление до версии 0.1.7 (20140307):


  • добавлен хак для исправления появления скроллбара при 1-пиксельном зазоре в ИЕ


Обновление до версии 0.1.8 (20140308):


  • исправлен баг с отображением горизонтального скроллбара если высота содержимого меньше высоты контейнера (случаи с height:100%)


Обновление до версии 0.1.9 (20140310):


  • исправлен баг с отображением вертикального скроллбара если контент содержит div с height:100%; overflow: hidden;, внутри которого div с высотой, превышающей высоту внешнего контейнера (спасибо за отзывы и замечания Evangeline Rei)
  • добавлена опция autoUpdate отвечающая за автоматическую реинициализацию скроллбара при изменении размеров контента/контейнера


Обновление до версии 0.2.0 (20140312):


  • добавлена опция onUpdate для возможности пересчёта размеров скроллбара вручную. благодаря данной опции был добавлен скроллбар в виде карты страницы
  • добавлена опция stepScrolling отвечающая за пошаговую прокрутку к месту, где удерживается нажатой кнопка мыши на скроллбаре. если опция отключена, то содержимое будет прокручено сразу до местоположения мыши, в противном случае будет происходить пошаговая прокрутка с ускорением до места удерживания мыши (эмуляция поведения скролла)
  • скрытие/отображение скроллбаров полностью перенесено в CSS
  • изменено поведение onInit — теперь запуск функции происходит только один раз при инициализации


Обновление до версии 0.2.1 (20140323):


  • исправлена ошибка с прокруткой к #anchor-элементу, когда становится видимым горизонтальный скролл


Обновление до версии 0.2.2 (20140411):


  • исправлена ошибка с инициализацией нескольких скроллбаров с настройками по умолчанию
  • добавлен элемент scroll-element_corner для удобной CSS настройки уголка если видны оба скроллбара


Обновление до версии 0.2.3 (20140703):


  • исправлена ошибка с видимым скроллбаром при изменении zoom-а в браузере
  • исправлена ошибка с неверной инициализацией размеров скроллбара для ИЕ при использовании CSS * { box-sizing: border-box; }
  • добавлена callback функция onScroll, которая позволяет легче определять степень прокручивания контейнера
  • скроллбар может быть использован как директива для отличного фреймворка Angular.JS
  • добавлена возможность переопределять значения по-умолчанию через глобальную переменную jQueryScrollbarOptions до загрузки плагина
  • оптимизирована проверка изменения размеров контейнера


Обновление до версии 0.2.4 (20140715):


  • добавлена возможность стилизации скроллбара для TEXTAREA наконец-то
Tags:
Hubs:
+31
Comments 62
Comments Comments 62

Articles