Полифиллы для старых браузеров

Библиотека Headroom.js активно использует современные возможности браузеров: обработку событий прокрутки, classList, requestAnimationFrame, а также корректную работу с addEventListener. В старых браузерах (особенно в Internet Explorer 9 и ниже) часть этих API либо отсутствует, либо реализована с ограничениями.

Полифиллы позволяют восполнить недостающий функционал, обеспечивая единообразное поведение скриптов.


Основные зависимости, требующие полифиллов

1. requestAnimationFrame

Headroom.js использует requestAnimationFrame для оптимизации обработки прокрутки и предотвращения лишних перерисовок.

Проблема:

  • В старых браузерах API отсутствует
  • Используются устаревшие альтернативы (setTimeout)

Полифилл:

(function() {
  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];

  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame =
      window[vendors[x] + 'CancelAnimationFrame'] ||
      window[vendors[x] + 'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
  }
})();

2. classList

Headroom.js активно добавляет и удаляет CSS-классы (headroom--pinned, headroom--unpinned).

Проблема:

  • В старых версиях IE отсутствует element.classList

Полифилл:

if (!("classList" in document.documentElement)) {
  Object.defineProperty(Element.prototype, 'classList', {
    get: function() {
      var element = this;

      return {
        add: function(className) {
          if (!element.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))) {
            element.className += " " + className;
          }
        },
        remove: function(className) {
          element.className = element.className.replace(
            new RegExp('(\\s|^)' + className + '(\\s|$)'), ' '
          );
        },
        toggle: function(className) {
          if (this.contains(className)) {
            this.remove(className);
          } else {
            this.add(className);
          }
        },
        contains: function(className) {
          return element.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
        }
      };
    }
  });
}

3. addEventListener

Headroom.js использует стандартный механизм подписки на события.

Проблема:

  • В старых браузерах используется attachEvent

Полифилл:

if (!window.addEventListener) {
  window.addEventListener = function(event, callback) {
    return window.attachEvent('on' + event, callback);
  };

  window.removeEventListener = function(event, callback) {
    return window.detachEvent('on' + event, callback);
  };
}

4. Object.assign

Внутренние настройки Headroom.js могут объединяться через Object.assign.

Проблема:

  • Отсутствует в старых браузерах

Полифилл:

if (typeof Object.assign != 'function') {
  Object.assign = function(target) {
    if (target == null) {
      throw new TypeError('Cannot convert undefined or null to object');
    }

    target = Object(target);

    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i];

      if (source != null) {
        for (var key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
          }
        }
      }
    }
    return target;
  };
}

Подключение полифиллов

Существует несколько стратегий:

Ручное подключение

<script src="polyfills/requestAnimationFrame.js"></script>
<script src="polyfills/classList.js"></script>
<script src="polyfills/objectAssign.js"></script>
<script src="headroom.min.js"></script>

Порядок критичен: полифиллы должны загружаться до основной библиотеки.


Использование CDN с полифиллами

Сервисы вроде polyfill.io позволяют подключать только необходимые полифиллы:

<script src="https://polyfill.io/v3/polyfill.min.js?features=default,requestAnimationFrame,Element.prototype.classList"></script>

Преимущества:

  • Автоматическое определение браузера
  • Минимальный размер загружаемого кода
  • Упрощение поддержки

Интеграция через сборщики

Современные инструменты, такие как Webpack или Vite, позволяют автоматически подключать полифиллы.

Пример с использованием Babel:

npm install core-js regenerator-runtime
import "core-js/stable";
import "regenerator-runtime/runtime";

Особенности работы Headroom.js в старых браузерах

  1. Производительность

    • Даже с полифиллами возможны задержки
    • Особенно заметно при прокрутке
  2. Отсутствие аппаратного ускорения

    • CSS-анимации могут работать менее плавно
  3. Ограничения DOM API

    • Некоторые методы работают медленнее
  4. Поведение скролла

    • Старые браузеры иначе обрабатывают события scroll

Оптимизация под старые браузеры

Упрощение логики

var options = {
  tolerance: 10,
  offset: 100
};
  • Увеличение tolerance снижает количество обновлений
  • Уменьшение чувствительности улучшает производительность

Минимизация DOM-операций

  • Избегать частых изменений классов
  • Использовать CSS для анимаций вместо JavaScript

Использование CSS fallback

.no-js .header {
  position: fixed;
  top: 0;
}

При отсутствии JavaScript интерфейс остаётся функциональным.


Проверка поддержки перед инициализацией

function supportsHeadroom() {
  return (
    'classList' in document.documentElement &&
    'addEventListener' in window &&
    'requestAnimationFrame' in window
  );
}

if (supportsHeadroom()) {
  var headroom = new Headroom(document.querySelector("header"));
  headroom.init();
}

Частые проблемы и способы их устранения

1. “Дёргание” шапки

Причины:

  • Нет requestAnimationFrame
  • Слабая оптимизация скролла

Решение:

  • Проверка наличия полифилла
  • Увеличение tolerance

2. Классы не применяются

Причины:

  • Отсутствует classList

Решение:

  • Подключение соответствующего полифилла

3. Скрипт не работает в IE

Причины:

  • Несовместимый синтаксис ES6

Решение:

  • Транспиляция через Babel
  • Подключение core-js

Минимальный набор полифиллов для Headroom.js

Для базовой совместимости:

  • requestAnimationFrame
  • classList
  • addEventListener
  • Object.assign

Этого набора достаточно для корректной работы в большинстве устаревших браузеров.


Практическая конфигурация

<!DOCTYPE html>
<html>
<head>
  <script src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.classList,requestAnimationFrame,Object.assign"></script>
  <script src="headroom.min.js"></script>
</head>
<body>

<header id="header">...</header>

<script>
  var header = document.getElementById("header");
  var headroom = new Headroom(header);
  headroom.init();
</script>

</body>
</html>

Контроль деградации функциональности

При невозможности полной поддержки:

  • Фиксированная шапка вместо скрывающейся
  • Упрощённые анимации
  • Отказ от динамического поведения

Такая стратегия обеспечивает стабильность интерфейса даже в условиях ограниченных возможностей браузера.