Azul Coding
-
Make Your Own Lighter jQuery in TypeScript
Sections:
// AZUL CODING ---------------------------------------
// Make Your Own Lighter jQuery in TypeScript
// https://youtu.be/QYRASC-i0Y0
function $<T extends Element = Element>(
selector: string | T | T[] | NodeListOf<T>,
): AzulQuery<T> {
return AzulQuery.create<T>(selector);
}
class AzulQuery<T extends Element = Element> {
private elements: T[];
[K: number]: T;
private constructor(selector: string | T | T[] | NodeListOf<T>) {
if (typeof selector === "string") {
this.elements = Array.from(document.querySelectorAll<T>(selector));
} else if (selector instanceof Element) {
this.elements = [selector as T];
} else if (selector instanceof NodeList) {
this.elements = Array.from(selector);
} else {
this.elements = selector;
}
}
static create<T extends Element = Element>(selector: string | T | T[] | NodeListOf<T>): AzulQuery<T> {
const instance = new AzulQuery<T>(selector);
return new Proxy(instance, {
get(target, prop, receiver) {
if (typeof prop === "string" && /^\d+$/.test(prop)) {
const index = Number.parseInt(prop);
return target.elements[index];
}
if (prop in target) {
return Reflect.get(target, prop, receiver);
}
return undefined;
},
});
}
*[Symbol.iterator](): Iterator<T> {
for (const element of this.elements) yield element;
}
get(): T[] {
return this.elements;
}
get length(): number {
return this.elements.length;
}
addClass(className: string): AzulQuery<T> {
for (const el of this.elements) {
el.classList.add(className);
}
return this;
}
removeClass(className: string): AzulQuery<T> {
for (const el of this.elements) {
el.classList.remove(className);
}
return this;
}
toggleClass(className: string, force?: boolean): AzulQuery<T> {
for (const el of this.elements) {
el.classList.toggle(className, force);
}
return this;
}
hasClass(className: string): boolean {
return this.elements[0]?.classList.contains(className) || false;
}
text(): string;
text(content: string): AzulQuery<T>;
text(content?: string): string | AzulQuery<T> {
if (content === undefined) {
return this.elements[0]?.textContent || "";
}
for (const el of this.elements) {
el.textContent = content;
}
return this;
}
html(): string;
html(content: string): AzulQuery<T>;
html(content?: string): string | AzulQuery<T> {
if (content === undefined) {
return this.elements[0]?.innerHTML || "";
}
for (const el of this.elements) {
el.innerHTML = content;
}
return this;
}
attr(name: string): string | undefined;
attr(name: string, value: string): AzulQuery<T>;
attr(attrs: Record<string, string>): AzulQuery<T>;
attr(name: string | Record<string, string>, value?: string): string | undefined | AzulQuery<T> {
if (typeof name === "string" && value === undefined) {
return this.elements[0]?.getAttribute(name) || undefined;
}
if (typeof name === "string" && value !== undefined) {
for (const el of this.elements) {
el.setAttribute(name, value);
}
} else if (typeof name === "object") {
for (const [key, val] of Object.entries(name)) {
for (const el of this.elements) {
el.setAttribute(key, val);
}
}
}
return this;
}
removeAttr(name: string): AzulQuery<T> {
for (const el of this.elements) {
el.removeAttribute(name);
}
return this;
}
prop(name: string): any;
prop(name: string, value: any): AzulQuery<T>;
prop(props: Record<string, any>): AzulQuery<T>;
prop(name: string | Record<string, any>, value?: any): any | AzulQuery<T> {
if (typeof name === "string" && value === undefined) {
return this.elements[0] ? (this.elements[0] as any)[name] : undefined;
}
if (typeof name === "string" && value !== undefined) {
for (const el of this.elements) {
(el as any)[name] = value;
}
} else if (typeof name === "object") {
for (const [key, val] of Object.entries(name)) {
for (const el of this.elements) {
(el as any)[key] = val;
}
}
}
return this;
}
removeProp(name: string): AzulQuery<T> {
for (const el of this.elements) {
delete (el as any)[name];
}
return this;
}
css(prop: string): string;
css(prop: string, value: string): AzulQuery<T>;
css(props: Record<string, string>): AzulQuery<T>;
css(prop: string | Record<string, string>, value?: string): AzulQuery<T> | string {
if (typeof prop === "string" && value === undefined) {
const firstEl = this.elements[0];
if (!firstEl) return "";
return window.getComputedStyle(firstEl).getPropertyValue(prop);
}
if (typeof prop === "string" && value !== undefined) {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.setProperty(prop, value);
}
}
} else if (typeof prop === "object") {
for (const [key, val] of Object.entries(prop)) {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.setProperty(key, val);
}
}
}
}
return this;
}
on<K extends keyof EventMapFor<T>>(event: K, handler: (this: T, ev: EventMapFor<T>[K]) => any): AzulQuery<T>;
on(event: string, handler: (this: T, ev: Event) => any): AzulQuery<T>;
on(event: string, handler: ((this: T, ev: Event) => any) | EventListener): AzulQuery<T> {
for (const el of this.elements) {
el.addEventListener(event, handler);
}
return this;
}
show(): AzulQuery<T> {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = "";
}
}
return this;
}
hide(): AzulQuery<T> {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = "none";
}
}
return this;
}
toggle(): AzulQuery<T> {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = el.style.display === "none" ? "" : "none";
}
}
return this;
}
find<U extends Element = Element>(selector: string): AzulQuery<U> {
const found = new Set<U>();
for (const el of this.elements) {
for (const match of el.querySelectorAll<U>(selector)) found.add(match);
}
return AzulQuery.create<U>(Array.from(found));
}
parent<U extends HTMLElement = HTMLElement>(): AzulQuery<U> {
const parents = this.elements
.map((el) => el.parentElement)
.filter((p) => p !== null) as U[];
return AzulQuery.create<U>(parents);
}
prev<U extends Element = Element>(selector?: string): AzulQuery<U> {
const prevElements: U[] = [];
for (const el of this.elements) {
let prev = el.previousElementSibling as U;
while (prev) {
if (!selector || prev.matches(selector)) {
if (!prevElements.includes(prev)) {
prevElements.push(prev);
}
break;
}
prev = prev.previousElementSibling as U;
}
}
return AzulQuery.create<U>(prevElements);
}
next<U extends Element = Element>(selector?: string): AzulQuery<U> {
const nextElements: U[] = [];
for (const el of this.elements) {
let next = el.nextElementSibling as U;
while (next) {
if (!selector || next.matches(selector)) {
if (!nextElements.includes(next)) {
nextElements.push(next);
}
break;
}
next = next.nextElementSibling as U;
}
}
return AzulQuery.create<U>(nextElements);
}
append(content: string | Element | AzulQuery): AzulQuery<T> {
for (const el of this.elements) {
if (typeof content === "string") {
el.insertAdjacentHTML("beforeend", content);
} else if (content instanceof Element) {
el.appendChild(content.cloneNode(true));
} else if (content instanceof AzulQuery) {
for (const child of content) el.appendChild(child.cloneNode(true));
}
}
return this;
}
val(): string;
val(value: string): AzulQuery<T>;
val(value?: string): string | AzulQuery<T> {
if (value === undefined) {
return this.elements[0] ? (this.elements[0] as any).value || "" : "";
}
for (const el of this.elements) {
(el as any).value = value;
}
return this;
}
remove(): AzulQuery<T> {
for (const el of this.elements) {
el.remove();
}
return this;
}
empty(): AzulQuery<T> {
for (const el of this.elements) {
el.innerHTML = "";
}
return this;
}
}
type EventMapFor<T> =
T extends HTMLElement ? HTMLElementEventMap :
T extends SVGElement ? SVGElementEventMap :
ElementEventMap;
(window as any).$ = $;
export default $;
Help support the channel
// AZUL CODING ---------------------------------------
// Make Your Own Lighter jQuery in TypeScript
// https://youtu.be/QYRASC-i0Y0
function $(selector) {
return AzulQuery.create(selector);
}
class AzulQuery {
constructor(selector) {
if (typeof selector === "string") {
this.elements = Array.from(document.querySelectorAll(selector));
} else if (selector instanceof Element) {
this.elements = [selector];
} else if (selector instanceof NodeList) {
this.elements = Array.from(selector);
} else {
this.elements = selector;
}
}
static create(selector) {
const instance = new AzulQuery(selector);
return new Proxy(instance, {
get(target, prop, receiver) {
if (typeof prop === "string" && /^\d+$/.test(prop)) {
const index = Number.parseInt(prop);
return target.elements[index];
}
if (prop in target) {
return Reflect.get(target, prop, receiver);
}
return undefined;
}
})
}
*[Symbol.iterator]() {
for (const element of this.elements) yield element;
}
get() {
return this.elements;
}
get length() {
return this.elements.length;
}
addClass(className) {
for (const el of this.elements) {
el.classList.add(className);
}
return this;
}
removeClass(className) {
for (const el of this.elements) {
el.classList.remove(className);
}
return this;
}
toggleClass(className, force) {
for (const el of this.elements) {
el.classList.toggle(className, force);
}
return this;
}
hasClass(className) {
return this.elements[0]?.classList.contains(className) || false;
}
text(content) {
if (content === undefined) {
return this.elements[0]?.textContent || "";
}
for (const el of this.elements) {
el.textContent = content;
}
return this;
}
html(content) {
if (content === undefined) {
return this.elements[0]?.innerHTML || "";
}
for (const el of this.elements) {
el.innerHTML = content;
}
return this;
}
attr(name, value) {
if (typeof name === "string" && value === undefined) {
return this.elements[0]?.getAttribute(name) || undefined;
}
if (typeof name === "string" && value !== undefined) {
for (const el of this.elements) {
el.setAttribute(name, value);
}
} else if (typeof name === "object") {
for (const [key, val] of Object.entries(name)) {
for (const el of this.elements) {
el.setAttribute(key, val);
}
}
}
return this;
}
removeAttr(name) {
for (const el of this.elements) {
el.removeAttribute(name);
}
return this;
}
prop(name, value) {
if (typeof name === "string" && value === undefined) {
return this.elements[0] ? this.elements[0][name] : undefined;
}
if (typeof name === "string" && value !== undefined) {
for (const el of this.elements) {
el[name] = value;
}
} else if (typeof name === "object") {
for (const [key, val] of Object.entries(name)) {
for (const el of this.elements) {
el[key] = val;
}
}
}
return this;
}
removeProp(name) {
for (const el of this.elements) {
delete el[name];
}
return this;
}
css(prop, value) {
if (typeof prop === "string" && value === undefined) {
const firstEl = this.elements[0];
if (!firstEl) return "";
return window.getComputedStyle(firstEl).getPropertyValue(prop);
}
if (typeof prop === "string" && value !== undefined) {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.setProperty(prop, value);
}
}
} else if (typeof prop === "object") {
for (const [key, val] of Object.entries(prop)) {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.setProperty(key, val);
}
}
}
}
return this
}
on(event, handler) {
for (const el of this.elements) {
el.addEventListener(event, handler);
}
return this;
}
show() {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = "";
}
}
return this;
}
hide() {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = "none";
}
}
return this;
}
toggle() {
for (const el of this.elements) {
if (el instanceof HTMLElement || el instanceof SVGElement) {
el.style.display = el.style.display === "none" ? "" : "none";
}
}
return this;
}
find(selector) {
const found = new Set();
for (const el of this.elements) {
for (const match of el.querySelectorAll(selector)) found.add(match);
}
return AzulQuery.create(Array.from(found));
}
parent() {
const parents = this.elements
.map(el => el.parentElement)
.filter(p => p !== null);
return AzulQuery.create(parents);
}
prev(selector) {
const prevElements = [];
for (const el of this.elements) {
let prev = el.previousElementSibling;
while (prev) {
if (!selector || prev.matches(selector)) {
if (!prevElements.includes(prev)) {
prevElements.push(prev);
}
break;
}
prev = prev.previousElementSibling;
}
}
return AzulQuery.create(prevElements);
}
next(selector) {
const nextElements = [];
for (const el of this.elements) {
let next = el.nextElementSibling;
while (next) {
if (!selector || next.matches(selector)) {
if (!nextElements.includes(next)) {
nextElements.push(next);
}
break;
}
next = next.nextElementSibling;
}
}
return AzulQuery.create(nextElements);
}
append(content) {
for (const el of this.elements) {
if (typeof content === "string") {
el.insertAdjacentHTML("beforeend", content);
} else if (content instanceof Element) {
el.appendChild(content.cloneNode(true));
} else if (content instanceof AzulQuery) {
for (const child of content) el.appendChild(child.cloneNode(true));
}
}
return this;
}
val(value) {
if (value === undefined) {
return this.elements[0] ? this.elements[0].value || "" : "";
}
for (const el of this.elements) {
el.value = value;
}
return this;
}
remove() {
for (const el of this.elements) {
el.remove();
}
return this;
}
empty() {
for (const el of this.elements) {
el.innerHTML = "";
}
return this;
}
}
window.$ = $;
<!-- AZUL CODING --------------------------------------- -->
<!-- Make Your Own Lighter jQuery in TypeScript -->
<!-- https://youtu.be/QYRASC-i0Y0 -->
<!DOCTYPE html>
<html>
<head>
<title>Azul Coding</title>
<style>
body {
margin: 30px;
background-color: #03506E;
}
* {
font-family: 'Golos Text', sans-serif;
font-weight: 500;
font-size: 18px;
}
strong {
font-weight: 700;
}
.square {
display: flex;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
}
.white {
background: white;
}
.blue {
background: #49C8FC;
}
</style>
<script src="azulquery.js"></script>
</head>
<body>
<div id="container" style="display:flex; gap:25px;">
<div class="white square"></div>
<div class="blue square"></div>
</div>
<br>
<button id="btn">Click me</button>
<script>
/* // Try these examples:
$("#container").css({
"gap": "50px",
"transform": "rotate(45deg)"
});
$(".blue.square").removeClass("blue").addClass("white");
$(".blue.square").append("<strong>Text</strong>");
$("#btn").on("click", () => {
$(".blue.square").empty();
});
*/
</script>
</body>
</html>


