2020-03-29 00:03:15 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Useful DOM utilities.
|
|
|
|
* @module components/dom
|
|
|
|
*/
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns parent of element with specified attribute value.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
|
|
|
* @param {string} name - Attribute name.
|
2022-02-19 12:43:11 +03:00
|
|
|
* @param {mixed} [value] - Attribute value.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @returns {HTMLElement} Parent with specified attribute value.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function parentWithAttribute(elem, name, value) {
|
|
|
|
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
|
|
|
elem = elem.parentNode;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-07-06 13:39:48 -04:00
|
|
|
if (!elem?.getAttribute) {
|
2023-04-19 01:56:05 -04:00
|
|
|
return null;
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns parent of element with one of specified tag names.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
|
|
|
* @param {(string|Array)} tagNames - Tag name or array of tag names.
|
|
|
|
* @returns {HTMLElement} Parent with one of specified tag names.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function parentWithTag(elem, tagNames) {
|
|
|
|
// accept both string and array passed in
|
|
|
|
if (!Array.isArray(tagNames)) {
|
|
|
|
tagNames = [tagNames];
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
while (tagNames.indexOf(elem.tagName || '') === -1) {
|
|
|
|
elem = elem.parentNode;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
if (!elem) {
|
|
|
|
return null;
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns _true_ if class list contains one of specified names.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {DOMTokenList} classList - Class list.
|
|
|
|
* @param {Array} classNames - Array of class names.
|
|
|
|
* @returns {boolean} _true_ if class list contains one of specified names.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
function containsAnyClass(classList, classNames) {
|
|
|
|
for (let i = 0, length = classNames.length; i < length; i++) {
|
|
|
|
if (classList.contains(classNames[i])) {
|
|
|
|
return true;
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2023-04-19 01:56:05 -04:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns parent of element with one of specified class names.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
|
|
|
* @param {(string|Array)} classNames - Class name or array of class names.
|
2023-07-06 15:20:24 -04:00
|
|
|
* @returns {HTMLElement|null} Parent with one of specified class names.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function parentWithClass(elem, classNames) {
|
|
|
|
// accept both string and array passed in
|
|
|
|
if (!Array.isArray(classNames)) {
|
|
|
|
classNames = [classNames];
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
while (!elem.classList || !containsAnyClass(elem.classList, classNames)) {
|
|
|
|
elem = elem.parentNode;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
if (!elem) {
|
|
|
|
return null;
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
return elem;
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
let supportsCaptureOption = false;
|
|
|
|
try {
|
|
|
|
const opts = Object.defineProperty({}, 'capture', {
|
|
|
|
get: function () {
|
|
|
|
supportsCaptureOption = true;
|
2023-05-02 12:53:20 -04:00
|
|
|
return null;
|
2023-04-19 01:56:05 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
window.addEventListener('test', null, opts);
|
2023-05-10 10:31:41 -04:00
|
|
|
} catch {
|
|
|
|
// no capture support
|
2023-04-19 01:56:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Adds event listener to specified target.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {EventTarget} target - Event target.
|
|
|
|
* @param {string} type - Event type.
|
|
|
|
* @param {function} handler - Event handler.
|
|
|
|
* @param {Object} [options] - Listener options.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function addEventListener(target, type, handler, options) {
|
|
|
|
let optionsOrCapture = options || {};
|
|
|
|
if (!supportsCaptureOption) {
|
|
|
|
optionsOrCapture = optionsOrCapture.capture;
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2023-04-19 01:56:05 -04:00
|
|
|
target.addEventListener(type, handler, optionsOrCapture);
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Removes event listener from specified target.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @param {EventTarget} target - Event target.
|
|
|
|
* @param {string} type - Event type.
|
|
|
|
* @param {function} handler - Event handler.
|
|
|
|
* @param {Object} [options] - Listener options.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function removeEventListener(target, type, handler, options) {
|
|
|
|
let optionsOrCapture = options || {};
|
|
|
|
if (!supportsCaptureOption) {
|
|
|
|
optionsOrCapture = optionsOrCapture.capture;
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
2023-04-19 01:56:05 -04:00
|
|
|
target.removeEventListener(type, handler, optionsOrCapture);
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Cached window size.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
let windowSize;
|
2020-03-29 00:03:15 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Flag of event listener bound.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
let windowSizeEventsBound;
|
2020-03-29 00:03:15 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Resets cached window size.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
function clearWindowSize() {
|
|
|
|
windowSize = null;
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2022-08-21 03:09:22 +03:00
|
|
|
* @typedef {Object} windowSize
|
|
|
|
* @property {number} innerHeight - window innerHeight.
|
|
|
|
* @property {number} innerWidth - window innerWidth.
|
|
|
|
*/
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns window size.
|
2022-08-21 03:09:22 +03:00
|
|
|
* @returns {windowSize} Window size.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function getWindowSize() {
|
|
|
|
if (!windowSize) {
|
|
|
|
windowSize = {
|
|
|
|
innerHeight: window.innerHeight,
|
|
|
|
innerWidth: window.innerWidth
|
|
|
|
};
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
if (!windowSizeEventsBound) {
|
|
|
|
windowSizeEventsBound = true;
|
|
|
|
addEventListener(window, 'orientationchange', clearWindowSize, { passive: true });
|
|
|
|
addEventListener(window, 'resize', clearWindowSize, { passive: true });
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
return windowSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Standard screen widths.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
const standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680];
|
2020-03-29 00:03:15 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns screen width.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @returns {number} Screen width.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function getScreenWidth() {
|
|
|
|
let width = window.innerWidth;
|
|
|
|
const height = window.innerHeight;
|
2020-03-08 19:08:07 +01:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
if (height > width) {
|
|
|
|
width = height * (16.0 / 9.0);
|
|
|
|
}
|
2020-03-08 19:08:07 +01:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
standardWidths.sort((a, b) => Math.abs(width - a) - Math.abs(width - b));
|
2022-10-04 16:47:08 -04:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
return standardWidths[0];
|
|
|
|
}
|
2020-03-08 19:08:07 +01:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Name of animation end event.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
let _animationEvent;
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns name of animation end event.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @returns {string} Name of animation end event.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function whichAnimationEvent() {
|
|
|
|
if (_animationEvent) {
|
|
|
|
return _animationEvent;
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
const el = document.createElement('div');
|
|
|
|
const animations = {
|
|
|
|
'animation': 'animationend',
|
|
|
|
'OAnimation': 'oAnimationEnd',
|
|
|
|
'MozAnimation': 'animationend',
|
|
|
|
'WebkitAnimation': 'webkitAnimationEnd'
|
|
|
|
};
|
|
|
|
for (const t in animations) {
|
|
|
|
if (el.style[t] !== undefined) {
|
|
|
|
_animationEvent = animations[t];
|
|
|
|
return animations[t];
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
_animationEvent = 'animationend';
|
|
|
|
return _animationEvent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns name of animation cancel event.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @returns {string} Name of animation cancel event.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function whichAnimationCancelEvent() {
|
|
|
|
return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel');
|
|
|
|
}
|
2018-10-23 01:05:09 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Name of transition end event.
|
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
let _transitionEvent;
|
2020-03-29 00:03:15 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
/**
|
2020-03-29 00:03:15 +03:00
|
|
|
* Returns name of transition end event.
|
2020-03-31 18:59:12 +03:00
|
|
|
* @returns {string} Name of transition end event.
|
2020-03-29 00:03:15 +03:00
|
|
|
*/
|
2023-04-19 01:56:05 -04:00
|
|
|
export function whichTransitionEvent() {
|
|
|
|
if (_transitionEvent) {
|
|
|
|
return _transitionEvent;
|
|
|
|
}
|
2019-01-10 15:39:37 +03:00
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
const el = document.createElement('div');
|
|
|
|
const transitions = {
|
|
|
|
'transition': 'transitionend',
|
|
|
|
'OTransition': 'oTransitionEnd',
|
|
|
|
'MozTransition': 'transitionend',
|
|
|
|
'WebkitTransition': 'webkitTransitionEnd'
|
|
|
|
};
|
|
|
|
for (const t in transitions) {
|
|
|
|
if (el.style[t] !== undefined) {
|
|
|
|
_transitionEvent = transitions[t];
|
|
|
|
return transitions[t];
|
2019-01-10 15:39:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-19 01:56:05 -04:00
|
|
|
_transitionEvent = 'transitionend';
|
|
|
|
return _transitionEvent;
|
|
|
|
}
|
2020-04-02 23:45:45 +02:00
|
|
|
|
2022-01-18 14:52:53 +03:00
|
|
|
/**
|
|
|
|
* Sets title and ARIA-label of element.
|
|
|
|
* @param {HTMLElement} elem - Element to set the title and ARIA-label.
|
|
|
|
* @param {string} title - Title.
|
|
|
|
* @param {string?} [ariaLabel] - ARIA-label.
|
|
|
|
*/
|
|
|
|
export function setElementTitle(elem, title, ariaLabel) {
|
|
|
|
elem.setAttribute('title', title);
|
|
|
|
elem.setAttribute('aria-label', ariaLabel);
|
|
|
|
}
|
|
|
|
|
2020-04-02 23:45:45 +02:00
|
|
|
export default {
|
|
|
|
parentWithAttribute: parentWithAttribute,
|
|
|
|
parentWithClass: parentWithClass,
|
|
|
|
parentWithTag: parentWithTag,
|
|
|
|
addEventListener: addEventListener,
|
|
|
|
removeEventListener: removeEventListener,
|
|
|
|
getWindowSize: getWindowSize,
|
|
|
|
getScreenWidth: getScreenWidth,
|
2022-01-18 14:52:53 +03:00
|
|
|
setElementTitle,
|
2020-04-02 23:45:45 +02:00
|
|
|
whichTransitionEvent: whichTransitionEvent,
|
|
|
|
whichAnimationEvent: whichAnimationEvent,
|
|
|
|
whichAnimationCancelEvent: whichAnimationCancelEvent
|
|
|
|
};
|