vh-check
Version:
vh unit buggyfill
199 lines (187 loc) • 7.48 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.vhCheck = factory());
}(this, (function () { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
// don't know a better way to get the size of a CSS 100vh…
function createTestElement() {
var testElement = document.createElement('div');
testElement.style.cssText =
'position: fixed; top: 0; height: 100vh; pointer-events: none;';
document.documentElement.insertBefore(testElement, document.documentElement.firstChild);
return testElement;
}
function removeTestElement(element) {
document.documentElement.removeChild(element);
}
// in some browsers this will be bigger than window.innerHeight
function checkSizes() {
var vhTest = createTestElement();
var windowHeight = window.innerHeight;
var vh = vhTest.offsetHeight;
var offset = vh - windowHeight;
removeTestElement(vhTest);
return {
vh: vh,
windowHeight: windowHeight,
offset: offset,
isNeeded: offset !== 0,
value: 0,
};
}
// export
function noop() { }
function computeDifference() {
var sizes = checkSizes();
sizes.value = sizes.offset;
return sizes;
}
function redefineVhUnit() {
var sizes = checkSizes();
sizes.value = sizes.windowHeight * 0.01;
return sizes;
}
var methods = /*#__PURE__*/Object.freeze({
noop: noop,
computeDifference: computeDifference,
redefineVhUnit: redefineVhUnit
});
function isString(text) {
return typeof text === "string" && text.length > 0;
}
function isFunction(f) {
return typeof f === "function";
}
var defaultOptions = Object.freeze({
cssVarName: 'vh-offset',
redefineVh: false,
method: computeDifference,
force: false,
bind: true,
updateOnTouch: false,
onUpdate: noop,
});
function getOptions(options) {
// old options handling: only redefine the CSS var name
if (isString(options)) {
return __assign({}, defaultOptions, { cssVarName: options });
}
// be sure to have a configuration object
if (typeof options !== 'object')
return defaultOptions;
// make sure we have the right options to start with
var finalOptions = {
force: options.force === true,
bind: options.bind !== false,
updateOnTouch: options.updateOnTouch === true,
onUpdate: isFunction(options.onUpdate) ? options.onUpdate : noop,
};
// method change
var redefineVh = options.redefineVh === true;
finalOptions.method =
methods[redefineVh ? 'redefineVhUnit' : 'computeDifference'];
finalOptions.cssVarName = isString(options.cssVarName)
? options.cssVarName
: redefineVh
? /*
when redefining vh unit we follow this article name convention
https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
*/
'vh'
: defaultOptions.cssVarName;
return finalOptions;
}
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
var passiveSupported = false;
var eventListeners = [];
/* istanbul ignore next */
try {
var options = Object.defineProperty({}, "passive", {
get: function () {
passiveSupported = true;
},
});
window.addEventListener("test", options, options);
window.removeEventListener("test", options, options);
}
catch (err) {
passiveSupported = false;
}
function addListener(eventName, callback) {
eventListeners.push({
eventName: eventName,
callback: callback,
});
window.addEventListener(eventName, callback,
/* istanbul ignore next */
passiveSupported ? { passive: true } : false);
}
function removeAll() {
eventListeners.forEach(function (config) {
window.removeEventListener(config.eventName, config.callback);
});
eventListeners = [];
}
function updateCssVar(cssVarName, result) {
document.documentElement.style.setProperty("--" + cssVarName, result.value + "px");
}
function formatResult(sizes, options) {
return __assign({}, sizes, { unbind: removeAll, recompute: options.method });
}
function vhCheck(options) {
var config = Object.freeze(getOptions(options));
var result = formatResult(config.method(), config);
// usefulness check
if (!result.isNeeded && !config.force) {
return result;
}
updateCssVar(config.cssVarName, result);
config.onUpdate(result);
// enabled by default
if (!config.bind)
return result;
function onWindowChange() {
window.requestAnimationFrame(function () {
var sizes = config.method();
updateCssVar(config.cssVarName, sizes);
config.onUpdate(formatResult(sizes, config));
});
}
// be sure we don't duplicates events listeners
result.unbind();
// listen for orientation change
// - this can't be configured
// - because it's convenient and not a real performance bottleneck
addListener('orientationchange', onWindowChange);
// listen to touch move for scrolling
// – disabled by default
// - listening to scrolling can be expansive…
if (config.updateOnTouch) {
addListener('touchmove', onWindowChange);
}
return result;
}
return vhCheck;
})));