@apicart/js-utils
Version:
A small set of useful utilities for easier development
186 lines (150 loc) • 4.21 kB
text/typescript
import {
Loops,
Objects
} from '.';
class Dom {
public addClass(element: Element, classes: string|string[]): Dom
{
if (typeof classes === 'string') {
classes = classes.split(' ');
}
Loops.forEach(classes, (newClass: string) => {
if (!element.classList.contains(newClass)) {
element.className += ' ' + newClass;
}
});
element.className = element.className.trim();
return this;
}
public findParent(element: Element, selector: string): Element|null
{
let parent = null;
/* eslint-disable-next-line no-cond-assign */
while (element = element.parentElement) {
if (this.matches(element, selector)) {
parent = element;
break;
}
}
return parent;
}
public matches(element: any, selector: string): boolean
{
const elementPrototype = Element.prototype;
if (elementPrototype.matches) {
return element.matches(selector);
} else if (elementPrototype.msMatchesSelector) {
return element.msMatchesSelector(selector);
}
return false;
}
/* eslint-disable @typescript-eslint/no-this-alias, max-nested-callbacks */
public on(eventTypes: string|string[], selectors: string|string[], callback: Function): this
{
const self = this;
if (typeof eventTypes === 'string') {
eventTypes = eventTypes.split(' ');
}
if (typeof selectors === 'string') {
selectors = selectors.split(',');
}
Loops.forEach(selectors, (selector: string): void => {
Loops.forEach(eventTypes, (eventType: string): void => {
((selector, eventType): void => {
document.addEventListener(eventType, function (event): void {
let target = event.target as any;
if (target === this) {
return;
}
if (!self.matches(target, selector)) {
target = self.findParent(target, selector);
}
if (target && target !== this) {
event.preventDefault();
const newEvent = {
currentTarget: target,
originalEvent: event
};
Loops.forEach([
'altKey',
'bubbles', 'button', 'buttons',
'cancelable', 'char', 'charCode', 'clientX', 'clientY', 'ctrlKey',
'data', 'detail',
'eventPhase',
'key', 'keyCode',
'metaKey',
'offsetX', 'offsetY', 'originalTarget',
'pageX', 'pageY', 'preventDefault',
'relatedTarget',
'screenX', 'screenY', 'shiftKey', 'stopImmediatePropagation', 'stopPropagation',
'target', 'toElement', 'type',
'view',
'which'
], (keyToCopy: any) => {
if (!Objects.keyExists(event, keyToCopy)) {
return;
}
const isFunction = event[keyToCopy] instanceof Function;
if (isFunction) {
newEvent[keyToCopy] = function (): any {
return event[keyToCopy]();
};
} else {
Object.defineProperty(newEvent, keyToCopy, {
get: function () {
return event[keyToCopy];
}
});
}
});
callback.call(target, newEvent);
}
});
})(selector, eventType);
});
});
return this;
}
/* eslint-enable @typescript-eslint/no-this-alias, max-nested-callbacks */
public removeClass(element: Element, classes: string): Dom
{
element.className = element.className
.replace(new RegExp(classes.trim().replace(' ', '|'), 'g'), '')
.trim()
.replace(/\s+/, ' ');
return this;
}
public toggleClass(element: Element, classes: string): Dom
{
const classesForCheck = classes.split(' ');
let classesToRemove = '';
let classesToAdd = '';
Loops.forEach(classesForCheck, (value: string) => {
if (element.classList.contains(value)) {
classesToRemove += ' ' + value;
} else {
classesToAdd += ' ' + value;
}
});
this.removeClass(element, classesToRemove);
this.addClass(element, classesToAdd);
return this;
}
public trigger(element: any[], event: string): this
{
if (element instanceof Element) {
element = [element];
}
Loops.forEach(element, (elementItem: Element): void => {
elementItem.dispatchEvent(
new Event(event, {
bubbles: true,
cancelable: true
// view: window
})
);
});
return this;
}
}
export default new Dom();