tsdom
Version:
Fast, lightweight JavaScript DOM manipulation utility
363 lines (273 loc) • 7.46 kB
text/typescript
import * as utility from './utility';
import * as event from './event';
import { IHandler, IEvents, IEvent } from './event/registry';
/* -----------------------------------
*
* IMeta
*
* -------------------------------- */
export interface IMeta {
owner?: Instance;
}
/* -----------------------------------
*
* Instance
*
* -------------------------------- */
export class Instance {
[index: number]: HTMLElement;
public length: number;
public events: IEvents;
private meta: IMeta;
public constructor(qry: string | HTMLElement, ctx?: Element, meta?: IMeta) {
let els: any;
this.meta = meta || {};
this.events = event.registry();
if (typeof qry === 'string') {
els = utility.query(qry, ctx ? ctx : document);
} else {
els = qry;
}
if (!els) return this;
if (els.nodeType === 1 || els === window) {
this[0] = els;
this.length = 1;
} else {
for (
let len = (this.length = els.length);
len--;
this[len] = els[len]
);
}
}
public get(key: number) {
return this[key];
}
public first() {
return new Instance(this[0]);
}
public find(qry: string) {
return new Instance(qry, this[0], { owner: this });
}
public closest(qry: string) {
const match = document.querySelectorAll(qry);
let el = this[0];
let i;
do {
i = match.length;
while (--i >= 0 && match.item(i) !== el) {
// noop
}
} while (i < 0 && (el = el.parentElement));
return new Instance(el);
}
public each(cb: (el: HTMLElement) => void) {
for (let i = 0, len = this.length; i < len; ) {
if (cb.call(this, this[i], i++) === false) {
break;
}
}
return this;
}
public css(obj: { [index: string]: string }) {
this.each(el => {
for (const key of Object.keys(obj)) {
const val = obj[key];
el.style.setProperty(key, val);
}
});
return this;
}
public attr(obj: { [index: string]: string } | string) {
if (typeof obj === 'string') {
const value = this[0].getAttribute(obj);
return value;
}
this.each(el => {
for (const key of Object.keys(obj)) {
const val = obj[key];
el.setAttribute(key, val);
}
});
}
public hasClass(str: string) {
let result = false;
this.each(el => {
result = utility.hasClass(el, str);
});
return result;
}
public addClass(str: string) {
this.each(el => {
const state = utility.hasClass(el, str);
if (!state) {
el.className += ' ' + str;
}
});
return this;
}
public removeClass(str: string) {
this.each(el => {
const state = utility.hasClass(el, str);
if (state) {
const reg = new RegExp('(\\s|^)' + str + '(\\s|$)');
const val = el.className.replace(reg, ' ').trim();
el.className = val.replace(/\s{2,}/g, ' ');
}
});
return this;
}
public toggleClass(str: string) {
if (this.hasClass(str)) {
this.removeClass(str);
} else {
this.addClass(str);
}
return this;
}
public on(ev: string, op1: string | IHandler, op2?: IHandler) {
const { events } = this;
const direct = typeof op1 === 'function' && op2 === undefined;
const delegate = typeof op1 === 'string' && typeof op2 === 'function';
this.each(el => {
let cb = null;
if (direct) {
cb = event.direct(op1 as IHandler);
}
if (delegate) {
cb = event.delegate(el, op1 as string, op2);
}
if (cb) {
el.addEventListener(ev, cb, true);
events.add({
type: ev,
handler: cb,
});
} else {
throw new Error('TSDom.on: Invalid Arguments');
}
});
return this;
}
public off(ev: string) {
const { events } = this;
this.each(el => {
const items: IEvent[] = events.find(ev);
items.forEach(item => {
if (item !== undefined) {
el.removeEventListener(ev, item.handler, true);
}
});
});
events.remove(ev);
return this;
}
public val(val?: string) {
const item = this.get(0) as HTMLInputElement;
if (item.value === undefined) {
return null;
}
if (val === undefined) {
return item.value;
}
item.value = val;
return val;
}
public text(val?: string) {
const item = this.get(0);
if (!item) {
return null;
}
if (val === undefined) {
return item.innerText;
}
this.each(el => {
el.innerHTML = val;
});
return val;
}
public data(key: string, val?: string) {
const item = this.get(0);
if (!item) {
return null;
}
if (val === undefined) {
return item.getAttribute(`data-${key}`);
}
this.each(el => {
el.setAttribute(`data-${key}`, val);
});
return val;
}
public html(val?: string) {
const item = this.get(0);
if (!item) {
return null;
}
if (val === undefined) {
return item.innerHTML;
}
this.each(el => {
el.innerHTML = val;
});
return val;
}
public append(item: string | Node | HTMLElement) {
this.each(el => {
if (typeof item === 'string') {
return el.insertAdjacentHTML('beforeend', item);
}
el.appendChild(item);
});
return this;
}
public prepend(item: string | Node | HTMLElement) {
this.each(el => {
if (typeof item === 'string') {
return el.insertAdjacentHTML('afterbegin', item);
}
el.insertBefore(item, el.firstChild);
});
return this;
}
public empty() {
this.each(el => {
while (el.firstChild) {
el.removeChild(el.firstChild);
}
});
return this;
}
public remove() {
this.each(el => {
el.parentNode.removeChild(el);
});
}
public toArray() {
const array: HTMLElement[] = [];
this.each(el => {
array.push(el);
});
return array;
}
}
/* -----------------------------------
*
* Namespace
*
* -------------------------------- */
export declare namespace TSDom {
export type Init = (
qry: string | HTMLElement,
ctx?: Element,
meta?: IMeta
) => Instance;
export class Object extends Instance {}
}
/* -----------------------------------
*
* Constructor
*
* -------------------------------- */
export default (qry: string | HTMLElement, ctx?: HTMLElement) =>
new Instance(qry, ctx);