Библиотека Headroom.js активно использует современные возможности
браузеров: обработку событий прокрутки, classList,
requestAnimationFrame, а также корректную работу с
addEventListener. В старых браузерах (особенно в Internet
Explorer 9 и ниже) часть этих API либо отсутствует, либо реализована с
ограничениями.
Полифиллы позволяют восполнить недостающий функционал, обеспечивая единообразное поведение скриптов.
requestAnimationFrameHeadroom.js использует requestAnimationFrame для
оптимизации обработки прокрутки и предотвращения лишних перерисовок.
Проблема:
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);
};
}
})();
classListHeadroom.js активно добавляет и удаляет CSS-классы
(headroom--pinned, headroom--unpinned).
Проблема:
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|$)'));
}
};
}
});
}
addEventListenerHeadroom.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);
};
}
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>
Порядок критичен: полифиллы должны загружаться до основной библиотеки.
Сервисы вроде 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";
Производительность
Отсутствие аппаратного ускорения
Ограничения DOM API
Поведение скролла
scrollvar options = {
tolerance: 10,
offset: 100
};
tolerance снижает количество обновлений.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();
}
Причины:
requestAnimationFrameРешение:
toleranceПричины:
classListРешение:
Причины:
Решение:
core-jsДля базовой совместимости:
requestAnimationFrameclassListaddEventListenerObject.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>
При невозможности полной поддержки:
Такая стратегия обеспечивает стабильность интерфейса даже в условиях ограниченных возможностей браузера.