@magik_io/mote
v1.6.10
Published
A lightweight, TypeScript-first DOM manipulation library with a jQuery-like API for modern web applications. Small, self-contained, and reusable pieces of code that can be composed to build larger applications.
Maintainers
Readme
Mote
So mote it be
A lightweight, TypeScript-first DOM manipulation library that provides a jQuery-like API for modern web applications. The name "mote" refers to small, self-contained, reusable pieces of code that can be composed to build larger applications.
Features
- TypeScript-First: Fully typed with generic support and strict mode options
- Fluent API: Chainable methods for expressive, readable code
- Lightweight: Zero dependencies, small footprint
- Modern: Built for ES6+ environments
- Type-Safe Events: Custom event typing with
TagEventMap - Multiple Export Formats: ESM, CommonJS, and TypeScript declarations
- jQuery-like: Familiar API for developers who know jQuery
Installation
npm install @magik_io/motepnpm add @magik_io/moteyarn add @magik_io/moteQuick Start
import { Mote, El, All } from '@magik_io/mote';
// Create and append a new element
new Mote('div#container')
.addClass('wrapper')
.text('Hello World')
.on('click', (e) => console.log(e))
.appendTo('#app');
// Manipulate an existing element
new El('#myElement')
.addClass('active')
.html('<p>Updated content</p>')
.on('click', () => console.log('Clicked!'));
// Work with multiple elements
new All('.items')
.addClass('highlighted')
.on('click', (e) => console.log('Item clicked:', e));Table of Contents
Core Classes
Mote - Element Creation
The Mote class extends El and is used for creating new DOM elements and appending them to the document.
import { Mote, mote } from '@magik_io/mote';
// Create element with id using tagName#id syntax
const container = new Mote('div#myContainer')
.addClass('container')
.appendTo('#app');
// Using factory function
mote('button#submitBtn', '#form', (id) => {
// Nested creation with access to parent ID
mote('span', id).text('Submit');
});El - Single Element Manipulation
The El class is for manipulating existing DOM elements.
import { El, el } from '@magik_io/mote';
// Select and manipulate an element
const input = new El('#username')
.addClass('form-control')
.set({ placeholder: 'Enter username' })
.on('input', (e) => console.log(e.target.value));
// Using factory function
el<'input'>('#email').val('[email protected]');All - Multiple Element Manipulation
The All class allows you to manipulate multiple elements at once.
import { All, all } from '@magik_io/mote';
// Select and manipulate multiple elements
new All<'button'>('.btn')
.addClass('btn-primary')
.on('click', (e) => console.log('Button clicked'));
// Iterate over elements
all('.items').each((element, index) => {
console.log(`Item ${index}:`, element);
});API Reference
Mote API
Constructor
new Mote<ElementName>(tagName: ElementName)Creates a new element. Supports tagName#id syntax.
Examples:
new Mote('div') // Creates a div
new Mote('div#container') // Creates a div with id="container"
new Mote('button#submitBtn') // Creates a button with id="submitBtn"Append Methods
appendTo(selector)- Append element to specified targetnew Mote('div').appendTo('#app'); new Mote('span').appendTo(document.body);appendToBody()- Append element to document.bodynew Mote('div#modal').appendToBody();appendToHead()- Append element to document.headnew Mote('script').appendToHead();appendToRoot()- Append element to document.documentElementnew Mote('div').appendToRoot();appendWithin(callback)- Execute callback with element's IDnew Mote('div#parent').appendWithin((id) => { new Mote('span').text('Child').appendTo(id); });
Factory Function
mote<ElementName>(tagName, appendTo, callback?)Example:
mote('div#container', '#app', (id) => {
mote('h1', id).text('Title');
mote('p', id).text('Content');
});El API
Constructor
new El<ElementName, StrictTypes>(selector)Accepts:
- CSS selector string
- HTML element
- Function returning element or selector
Examples:
new El('#myElement')
new El(document.getElementById('myElement'))
new El(() => document.querySelector('.active'))Selection & Navigation
self()- Get the underlying HTML elementconst element = new El('#myDiv').self();parent()- Get parent elementnew El('#child').parent().addClass('parent-class');firstChild<E>()- Get first child elementconst first = new El('#parent').firstChild<HTMLDivElement>();children()- Get all child nodesconst nodes = new El('#parent').children();find(selector)- Find elements within current elementconst buttons = new El('#form').find('button');
Class Management
addClass(className)- Add class(es)el.addClass('active'); el.addClass(['active', 'highlight']); el.addClass(() => 'dynamic-class');removeClass(className)- Remove class(es)el.removeClass('active'); el.removeClass(['active', 'highlight']);toggleClass(className)- Toggle class(es)el.toggleClass('active'); el.toggleClass(['active', 'visible']);hasClass(className)- Check if element has classif (el.hasClass('active')) { /* ... */ }
Content Manipulation
html(content)- Set inner HTMLel.html('<p>Hello World</p>');text(content)- Set text contentel.text('Hello World'); el.text(123); el.text(true);textContent(content?)- Get or set text contentconst text = el.textContent(); el.textContent('New text');textChild(text)- Append text node as childel.textChild('Additional text');empty()- Empty inner HTMLel.empty();clear()- Clear inner HTML (alias for empty)el.clear();
Attributes
set(attributes)- Set attributesel.set({ id: 'myId', 'data-value': '123', disabled: true });unset(attributes)- Remove attributesel.unset(['disabled', 'readonly']); el.unset('disabled');id(value?)- Get or set idconst id = el.id(); el.id('newId');data(suffix)- Get data attributeconst value = el.data('user-id'); // Gets data-user-iddataset()- Get dataset objectconst dataset = el.dataset(); console.log(dataset.userId);
Form Elements
val(value?)- Get or set valueconst value = el.val(); el.val('new value'); el.val(123);check(boolean)- Check or uncheck inputel.check(true); el.check(false);checked()- Get checked stateif (el.checked()) { /* ... */ }type(type)- Set input typeel.type('password');name(name)- Set name attributeel.name('username');input(type)- Set name to id and typeel.input('email'); // Sets name=id and type=emailhtmlFor(elementId)- Set label's for attributenew El('label').htmlFor('myInput');
Images
src(url)- Set image sourcenew El<'img'>('#myImage').src('/images/photo.jpg');alt(text)- Set image alt textnew El<'img'>('#myImage').alt('Photo description');
DOM Operations
child(child, position?)- Append or prepend childel.child('<span>Text</span>'); el.child(document.createElement('div'), 'prepend'); el.child('<p>First</p>', 'prepend');wrap(className)- Wrap element in divel.wrap('wrapper');remove()- Remove element from DOMel.remove();replaceWith(html)- Replace with HTML stringel.replaceWith('<div>New content</div>');replaceWithElement(tagName, id?)- Replace with new elementconst newEl = el.replaceWithElement('div', 'newId');
Event Handling
on(event, listener, options?)- Add event listenerel.on('click', (e) => console.log(e)); el.on<{ userId: string }>('custom', (e) => { console.log(e.detail.userId); });once(event, listener)- Add one-time event listenerel.once('click', (e) => console.log('Clicked once'));off(event, listener, options?)- Remove event listenerel.off('click', myHandler);trigger(event, options?)- Trigger custom eventel.trigger('change'); el.trigger('custom', { detail: { data: 'value' } });now(eventName, detail)- Dispatch custom eventel.now('dataUpdated', { userId: '123' });click()- Dispatch click eventel.click();triggerChange()- Trigger change event (select elements)new El<'select'>('#mySelect').triggerChange();dispatchEvent(eventName)- Dispatch eventel.dispatchEvent('input');
CSS Manipulation
css(property)- Get computed CSS property valueconst color = el.css('color');css(property, value)- Set single CSS propertyel.css('color', 'red'); el.css('font-size', 16);css(properties)- Set multiple CSS propertiesel.css({ color: 'red', 'font-size': '16px', 'background-color': '#f0f0f0' });
Visibility & Display
show()- Show elementel.show();hide()- Hide elementel.hide();toggle()- Toggle visibilityel.toggle();isVisible()- Check if element is visibleif (el.isVisible()) { /* ... */ }
Dimensions & Position
width()- Get widthconst w = el.width(); // Returns numberwidth(value)- Set widthel.width(100); // Sets to 100px el.width('50%'); // Sets to 50%height()/height(value)- Get or set heightconst h = el.height(); el.height(200); el.height('auto');offset()- Get offset position relative to documentconst { top, left } = el.offset();position()- Get position relative to offset parentconst { top, left } = el.position();
Animations
animate(keyframes, options)- Animate element using Web Animations APIel.animate([ { opacity: 0, transform: 'translateY(-20px)' }, { opacity: 1, transform: 'translateY(0)' } ], { duration: 300, easing: 'ease-out' });fadeIn(duration?)- Fade in element (returns Promise)await el.fadeIn(300);fadeOut(duration?)- Fade out element (returns Promise)await el.fadeOut(300);fadeTo(opacity, duration?)- Fade to specific opacity (returns Promise)await el.fadeTo(0.5, 300);slideDown(duration?)- Slide down element (returns Promise)await el.slideDown(300);slideUp(duration?)- Slide up element (returns Promise)await el.slideUp(300);
Utilities
if(expression)- Conditional chainingel.if(condition)?.addClass('active');nestFrom(callback)- Execute callback with element idel.nestFrom((id) => { new Mote('span').text('Child').appendTo(id); });
Factory Function
el<ElementName, StrictTypes>(selector)Example:
const myEl = el<'div'>('#container');All API
Constructor
new All<ElementType>(selector)Example:
new All<'button'>('.btn');Iteration
each(callback)- Iterate over elementsnew All('.items').each((element, index) => { console.log(`Item ${index}:`, element.textContent); });log(treeView?)- Debug log elementsnew All('.items').log(); new All('.items').log(true); // Tree viewself()- Get NodeList of elementsconst elements = new All('.items').self();
Class Management
addClass(className)- Add class to all elementsnew All('.items').addClass('active'); new All('.items').addClass(['active', 'highlight']);removeClass(className)- Remove class from all elementsnew All('.items').removeClass('active');toggleClass(className)- Toggle class on all elementsnew All('.items').toggleClass('visible');
Content & Attributes
html(content)- Set inner HTML for all elementsnew All('.items').html('<p>Same content</p>');text(content)- Set text content for all elementsnew All('.items').text('Same text');textChild(text)- Append text node to all elementsnew All('.items').textChild(' - updated');empty()/clear()- Empty all elementsnew All('.items').empty();set(attributes)- Set attributes on all elementsnew All('.items').set({ 'data-active': 'true' });unset(attributes)- Remove attributes from all elementsnew All('.items').unset(['disabled', 'readonly']);attr(attribute, value)- Set attribute on all elementsnew All('.items').attr('data-value', '123');data(name, value)- Set data attribute on all elementsnew All('.items').data('userId', '123');
Form Elements
val(value?)- Set value on all elementsnew All<'input'>('.inputs').val('same value');type(type)- Set type on all elementsnew All<'input'>('.inputs').type('text');name(name)- Set name on all elementsnew All<'input'>('.inputs').name('fieldName');input(type)- Set name to id and type on all elementsnew All<'input'>('.inputs').input('email');htmlFor(elementId)- Set htmlFor on all label elementsnew All<'label'>('.labels').htmlFor('targetInput');
DOM Operations
child(element, position?)- Append/prepend child to all elementsconst span = document.createElement('span'); new All('.items').child(span); new All('.items').child(span, 'prepend');wrap(className)- Wrap all elements in divnew All('.items').wrap('item-wrapper');remove()- Remove all elements from DOMnew All('.items').remove();replaceWith(html)- Replace all elements with HTMLnew All('.items').replaceWith('<div>Replacement</div>');src(url)- Set src on all elementsnew All<'img'>('.images').src('/images/placeholder.jpg');
Events
on(event, listener, options?)- Add event listener to all elementsnew All('.btns').on('click', (e) => console.log('Clicked'));once(event, listener, options?)- Add one-time listener to all elementsnew All('.btns').once('click', (e) => console.log('First click'));off(event, listener)- Remove event listener from all elementsnew All('.btns').off('click', myHandler);now(eventName, detail)- Dispatch custom event on all elementsnew All('.items').now('refresh', { timestamp: Date.now() });click()- Dispatch click event on all elementsnew All('.btns').click();
CSS Manipulation
css(property, value)- Set single CSS property on all elementsnew All('.items').css('color', 'red');css(properties)- Set multiple CSS properties on all elementsnew All('.items').css({ color: 'red', 'font-size': '16px' });
Visibility & Dimensions
show()- Show all elementsnew All('.items').show();hide()- Hide all elementsnew All('.items').hide();toggle()- Toggle visibility of all elementsnew All('.items').toggle();width(value)- Set width on all elementsnew All('.items').width(100); new All('.items').width('50%');height(value)- Set height on all elementsnew All('.items').height(200);
Animations
fadeIn(duration?)- Fade in all elements (returns Promise)await new All('.items').fadeIn(300);fadeOut(duration?)- Fade out all elements (returns Promise)await new All('.items').fadeOut(300);
Factory Function
all<ElementType>(selector)Example:
const buttons = all<'button'>('.btn');Examples
Creating a Form
import { mote } from '@magik_io/mote';
mote('form#loginForm', '#app', (formId) => {
// Username field
mote('div.form-group', formId, (groupId) => {
mote('label', groupId).text('Username').htmlFor('username');
mote('input#username', groupId)
.addClass('form-control')
.set({ type: 'text', placeholder: 'Enter username' });
});
// Password field
mote('div.form-group', formId, (groupId) => {
mote('label', groupId).text('Password').htmlFor('password');
mote('input#password', groupId)
.addClass('form-control')
.set({ type: 'password', placeholder: 'Enter password' });
});
// Submit button
mote('button#submitBtn', formId)
.addClass('btn btn-primary')
.text('Login')
.on('click', async (e) => {
e.preventDefault();
const username = el<'input'>('#username').val();
const password = el<'input'>('#password').val();
console.log({ username, password });
});
});Dynamic List
import { Mote, All } from '@magik_io/mote';
const items = ['Apple', 'Banana', 'Cherry', 'Date'];
const list = new Mote('ul#fruitList')
.addClass('list-unstyled')
.appendTo('#app');
items.forEach((fruit, index) => {
new Mote('li')
.addClass('list-item')
.text(fruit)
.set({ 'data-index': index.toString() })
.on('click', (e) => {
new All('.list-item').removeClass('active');
e.currentTarget.classList.add('active');
})
.appendTo('#fruitList');
});Modal Dialog
import { Mote } from '@magik_io/mote';
function createModal(title: string, content: string) {
const modal = new Mote('div#modal')
.addClass('modal')
.appendToBody();
mote('div.modal-content', '#modal', (contentId) => {
mote('div.modal-header', contentId, (headerId) => {
mote('h2', headerId).text(title);
mote('button.close', headerId)
.text('×')
.on('click', () => modal.remove());
});
mote('div.modal-body', contentId)
.html(content);
mote('div.modal-footer', contentId, (footerId) => {
mote('button', footerId)
.addClass('btn-primary')
.text('Close')
.on('click', () => modal.remove());
});
});
return modal;
}
// Usage
createModal('Welcome', '<p>Welcome to our application!</p>');Event Delegation
import { El, Mote } from '@magik_io/mote';
const container = new Mote('div#container')
.appendToBody();
// Add event listener to container
new El('#container').on('click', (e) => {
const target = e.target as HTMLElement;
if (target.classList.contains('item')) {
console.log('Item clicked:', target.textContent);
}
});
// Dynamically add items
for (let i = 0; i < 10; i++) {
new Mote('div')
.addClass('item')
.text(`Item ${i}`)
.appendTo('#container');
}Animations and Transitions
import { El, Mote } from '@magik_io/mote';
// Fade in elements on page load
const notification = new Mote('div#notification')
.addClass('alert')
.text('Welcome back!')
.hide()
.appendToBody();
// Fade in after a delay
setTimeout(async () => {
await notification.fadeIn(300);
// Auto-hide after 3 seconds
setTimeout(async () => {
await notification.fadeOut(300);
notification.remove();
}, 3000);
}, 500);
// Slide toggle for accordion
const toggleButton = new El('#accordion-toggle');
const content = new El('#accordion-content');
toggleButton.on('click', async () => {
if (content.isVisible()) {
await content.slideUp(300);
} else {
await content.slideDown(300);
}
});
// Custom animations with Web Animations API
new El('#animatedBox').animate([
{ transform: 'translateX(0px)', opacity: 1 },
{ transform: 'translateX(100px)', opacity: 0.5 },
{ transform: 'translateX(0px)', opacity: 1 }
], {
duration: 2000,
iterations: Infinity,
easing: 'ease-in-out'
});
// CSS manipulation with animations
new El('#styledElement')
.css({
'background-color': '#3498db',
'transition': 'all 0.3s ease',
'transform': 'scale(1)'
})
.on('mouseenter', (e) => {
new El(e.currentTarget as HTMLElement).css({
'background-color': '#2980b9',
'transform': 'scale(1.05)'
});
})
.on('mouseleave', (e) => {
new El(e.currentTarget as HTMLElement).css({
'background-color': '#3498db',
'transform': 'scale(1)'
});
});TypeScript Generic Types
import { El, Mote } from '@magik_io/mote';
// Specify element type for better type safety
const input = new El<'input'>('#username');
const value = input.val(); // TypeScript knows this returns string
// Strict mode for type-safe event handlers
const button = new El<'button', true>('#submitBtn');
button.on('click', (e) => {
// e is typed as MouseEvent specifically for button elements
console.log(e.currentTarget.type);
});
// Create typed elements
const image = new Mote<'img'>('img#logo')
.src('/logo.png')
.alt('Company Logo')
.appendTo('#header');
// Custom event data
interface UserData {
userId: string;
username: string;
}
new El('#userProfile').on<UserData>('userUpdated', (e) => {
console.log(e.detail.userId, e.detail.username);
});TypeScript Support
Mote is built with TypeScript and provides comprehensive type definitions.
Generic Parameters
El<ElementName, StrictTypes>
Mote<ElementName, StrictMode>
All<ElementType>Type Definitions
The library exports several useful types:
import type {
htmlTags, // Union of all HTML tag names
htmlElements, // Union of all HTML element types
selectorString, // CSS selector string
idString, // ID selector (#id)
classString, // Class selector (.class)
GenericEvent, // Generic event type with custom data
TagEventMap, // Event map for specific element types
} from '@magik_io/mote/types';Custom Elements
Extend Mote to support custom HTML elements:
// In your types file
declare module '@magik_io/mote' {
interface CustomHTMLElements {
'my-component': HTMLElement;
'custom-button': HTMLButtonElement;
}
}
// Usage
new Mote<'my-component'>('my-component').appendTo('#app');Strict Type Mode
Enable strict typing for more precise type checking:
// Without strict mode (default)
const el = new El<'button'>('#myBtn');
el.on('click', (e) => {
// e is GenericEvent
});
// With strict mode
const strictEl = new El<'button', true>('#myBtn');
strictEl.on('click', (e) => {
// e is MouseEvent from TagEventMap
console.log(e.button); // Typed correctly
});Contributing
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Development
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Run linter
pnpm lint
# Run tests
pnpm test
# Build library
pnpm buildLicense
MIT © Antonio B.
Links
So mote it be ✨
