@medyll/idae-be
Version:
A modern, lightweight, and extensible DOM manipulation library built with TypeScript. Designed for precise element targeting and manipulation using a callback-based approach. Features include advanced DOM traversal, event handling, style management, attri
461 lines (460 loc) • 17.4 kB
JavaScript
import { Be, be } from '../be.js';
var domMethods;
(function (domMethods) {
domMethods["update"] = "update";
domMethods["append"] = "append";
domMethods["prepend"] = "prepend";
domMethods["insert"] = "insert";
domMethods["afterBegin"] = "afterBegin";
domMethods["afterEnd"] = "afterEnd";
domMethods["beforeBegin"] = "beforeBegin";
domMethods["beforeEnd"] = "beforeEnd";
domMethods["remove"] = "remove";
domMethods["wrap"] = "wrap";
domMethods["normalize"] = "normalize";
domMethods["replace"] = "replace";
domMethods["clear"] = "clear";
domMethods["unwrap"] = "unwrap";
})(domMethods || (domMethods = {}));
/**
* Handles DOM manipulation operations for Be elements.
*/
export class DomHandler {
beElement;
static methods = Object.values(domMethods);
constructor(element) {
this.beElement = element;
}
methods = DomHandler.methods;
/**
* Handles various DOM operations on the element(s).
* @param actions An object specifying the DOM actions to perform.
* @param actions.update - HTML content to update the element(s) with.
* @param actions.append - Content to append to the element(s).
* @param actions.prepend - Content to prepend to the element(s).
* @param actions.remove - If true, removes the element(s) from the DOM.
* @param actions.replace - Content to replace the element(s) with.
* @param actions.clear - If true, clears the content of the element(s).
* @returns The Be instance for method chaining.
*/
handle(actions) {
Object.entries(actions).forEach(([method, props]) => {
switch (method) {
case 'update': // be('.test').dom({update: {content: '<p>Updated</p>', callback: () => {}}})
this.update(props.content, props.callback);
break;
case 'append':
this.append(props.content, props.callback);
break;
case 'prepend':
this.prepend(props.content, props.callback);
break;
case 'replace':
this.replace(props.content, props.callback);
break;
case 'remove':
this.remove(props.callback);
break;
case 'clear':
this.clear(props.callback);
break;
case 'normalize':
this.normalize(props.callback);
break;
case 'wrap':
this.wrap(props.tag, props.callback);
break;
case 'unwrap':
this.unwrap(props.callback);
break;
}
});
return this.beElement;
}
/**
* Updates the content of the element(s).
* @param content - The new content to set.
* @param callback - Optional callback function.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"></div>
* const beInstance = be('#test');
* beInstance.update('<p>Updated content</p>'); // Updates the content of the element
*/
update(content, callback) {
this.beElement.eachNode((el) => {
if (el) {
el.innerHTML = content;
callback?.({
fragment: content,
be: be(el),
root: this.beElement
});
}
});
return this.beElement;
}
/**
* Appends content to the element(s).
* @param content - The content to append (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after appending.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"></div>
* const beInstance = be('#test');
* beInstance.append('<span>Appended</span>'); // Appends content to the element
*/
append(content, callback) {
const ret = [];
this.beElement.eachNode((el) => {
const normalizedContent = this.normalizeContent(content);
if (normalizedContent instanceof DocumentFragment) {
el.appendChild(normalizedContent);
}
else {
ret.push(normalizedContent);
el.appendChild(normalizedContent);
}
});
callback?.({
fragment: content,
be: be(ret),
root: this.beElement
});
return this.beElement;
}
/**
* Prepends content to the element(s).
* @param content - The content to prepend (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after prepending.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"></div>
* const beInstance = be('#test');
* beInstance.prepend('<span>Prepended</span>'); // Prepends content to the element
*/
prepend(content, callback) {
const ret = [];
this.beElement.eachNode((el) => {
const normalizedContent = this.normalizeContent(content);
if (normalizedContent instanceof DocumentFragment) {
el.insertBefore(normalizedContent, el.firstChild);
}
else {
ret.push(normalizedContent);
el.insertBefore(normalizedContent, el.firstChild);
}
});
callback?.({
fragment: content,
be: be(ret),
root: this.beElement
});
return this.beElement;
}
/**
* Inserts content into the element(s) at a specified position.
* @param mode - The position to insert the content ('afterbegin', 'afterend', 'beforebegin', 'beforeend').
* @param element - The content to insert (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after insertion.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"></div>
* const beInstance = be('#test');
* beInstance.insert('afterbegin', '<span>Inserted</span>'); // Inserts content at the beginning
*/
insert(mode, element, callback) {
switch (mode) {
case 'afterbegin':
return this.afterBegin(element, callback);
case 'afterend':
return this.afterEnd(element, callback);
case 'beforebegin':
return this.beforeBegin(element, callback);
case 'beforeend':
return this.beforeEnd(element, callback);
default:
throw new Error(`Invalid mode: ${mode}`);
}
}
/**
* Inserts content at the beginning of the element(s).
* @param content - The content to insert (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after insertion.
* @returns The Be instance for method chaining.
*/
afterBegin(content, callback) {
this.beElement.eachNode((el) => {
this.adjacentElement(el, content, 'afterbegin');
callback?.({
fragment: content,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Inserts content after the element(s).
* @param content - The content to insert (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after insertion.
* @returns The Be instance for method chaining.
*/
afterEnd(content, callback) {
this.beElement.eachNode((el) => {
// Insérer après l'élément cible
el.parentNode?.insertBefore(this.normalizeContent(content), el.nextSibling);
callback?.({
fragment: content,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Inserts content before the element(s).
* @param content - The content to insert (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after insertion.
* @returns The Be instance for method chaining.
*/
beforeBegin(content, callback) {
this.beElement.eachNode((el) => {
// Insérer avant l'élément cible
el.parentNode?.insertBefore(this.normalizeContent(content), el);
callback?.({
fragment: content,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Inserts content at the end of the element(s).
* @param content - The content to insert (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after insertion.
* @returns The Be instance for method chaining.
*/
beforeEnd(content, callback) {
this.beElement.eachNode((el) => {
// Insérer à la fin de l'élément cible
el.appendChild(this.normalizeContent(content));
callback?.({
fragment: content,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Replaces the element(s) with new content.
* @param content - The content to replace the element(s) with (string, HTMLElement, or Be instance).
* @param callback - Optional callback function to execute after replacement.
* @returns The Be instance for method chaining.
*/
replace(content, callback) {
const ret = [];
this.beElement.eachNode((el) => {
const normalizedContent = this.normalizeContent(content);
if (normalizedContent instanceof DocumentFragment) {
el.replaceWith(...normalizedContent.childNodes);
}
else {
ret.push(normalizedContent);
el.replaceWith(normalizedContent);
}
});
callback?.({
fragment: content,
be: be(ret),
root: this.beElement
});
return this.beElement;
}
/**
* Removes the element(s) from the DOM.
* @param callback - Optional callback function to execute after removal.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"><span>To be removed</span></div>
* const beInstance = be('#test span');
* beInstance.remove(); // Removes the span element
*/
remove(callback) {
this.beElement.eachNode((el) => {
el.remove();
callback?.({
fragment: undefined,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Clears the content of the element(s).
* @param callback - Optional callback function to execute after clearing.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"><span>Content</span></div>
* const beInstance = be('#test');
* beInstance.clear(); // Clears the content of the div
*/
clear(callback) {
this.beElement.eachNode((el) => {
const fragment = el.innerHTML;
el.innerHTML = '';
callback?.({
fragment,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Normalizes the content of the element(s).
* @param callback - Optional callback function to execute after normalization.
* @returns The Be instance for method chaining.
*/
normalize(callback) {
this.beElement.eachNode((el) => {
el.normalize();
callback?.({
fragment: undefined,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Wraps the element(s) with a new element.
* @param tag - The tag name of the wrapper element (default is 'div').
* @param callback - Optional callback function to execute after wrapping.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="test"></div>
* const beInstance = be('#test');
* beInstance.wrap('section'); // Wraps the div with a <section> element
*/
wrap(tag = 'div', callback) {
// wrap in tag
this.beElement.eachNode((el) => {
const wrapper = document.createElement(tag);
el.insertAdjacentElement('beforebegin', wrapper); // "afterbegin" | "afterend" | "beforebegin" | "beforeend"
wrapper.appendChild(el);
callback?.({
fragment: tag,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
/**
* Removes the parent element of the selected element(s), keeping the selected element(s) in the DOM.
* @param callback - Optional callback function to execute after unwrapping.
* @returns The Be instance for method chaining.
* @example
* // HTML: <div id="wrapper"><span id="child">Content</span></div>
* const beInstance = be('#child');
* beInstance.unwrap(); // Removes the <div id="wrapper">, keeping <span id="child">
*/
unwrap(callback) {
this.beElement.eachNode((el) => {
const parent = el.parentElement;
if (parent) {
// Move the element itself before the parent
while (el.firstChild) {
parent.insertBefore(el.firstChild, el);
}
// Remove the parent after moving its children
parent.replaceWith(...Array.from(parent.childNodes));
}
callback?.({
fragment: undefined,
be: be(el),
root: this.beElement
});
});
return this.beElement;
}
adjacentElement(element, content, mode) {
const normalizedContent = this.normalizeContent(content);
if (typeof content === 'string') {
// Si le contenu est une chaîne HTML, utilisez insertAdjacentHTML
element.insertAdjacentHTML(mode, content);
}
else if (normalizedContent instanceof HTMLElement) {
// Si le contenu est un élément DOM, utilisez insertAdjacentElement
if (mode === 'afterend') {
element.parentNode?.insertBefore(normalizedContent, element.nextSibling);
}
else if (mode === 'beforebegin') {
element.parentNode?.insertBefore(normalizedContent, element);
}
else {
element.insertAdjacentElement(mode, normalizedContent);
}
}
else if (normalizedContent instanceof DocumentFragment) {
// Si le contenu est un fragment, insérez chaque nœud
Array.from(normalizedContent.childNodes).forEach((node) => {
if (mode === 'afterbegin' || mode === 'beforeend') {
element.appendChild(node);
}
else if (mode === 'afterend') {
element.parentNode?.insertBefore(node, element.nextSibling);
}
else if (mode === 'beforebegin') {
element.parentNode?.insertBefore(node, element);
}
});
}
}
normalizeContent(content) {
if (typeof content === 'string') {
// Si le contenu est une chaîne HTML, créez un fragment DOM
const template = document.createElement('template');
template.innerHTML = content.trim();
return template.content;
}
else if (content instanceof Be) {
// Si le contenu est une instance de Be, utilisez son premier nœud
if (Array.isArray(content.node)) {
const fragment = document.createDocumentFragment();
content.node.forEach((node) => {
if (node instanceof HTMLElement) {
fragment.appendChild(node);
}
});
return fragment;
}
else if (content.node instanceof HTMLElement) {
return content.node;
}
throw new Error('Invalid Be instance: no valid node found.');
}
else {
// Sinon, le contenu est déjà un HTMLElement
return content;
}
}
insertContent(el, content, mode) {
const normalizedContent = this.normalizeContent(content);
if (mode === 'afterEnd') {
el.parentNode?.insertBefore(normalizedContent, el.nextSibling);
}
else if (mode === 'beforeEnd') {
el.appendChild(normalizedContent);
}
}
valueOf() {
if (this.beElement.isWhat !== 'element')
return null;
return this.beElement.inputNode.innerHTML;
}
}