Documentation
Objs is an open-source library that can get and change DOM elements, control their states and events, send AJAX requests, load and cache scripts, styles, images and test functions (unit and async tests). Also it is just 10KB with no dependencies.
Usage
Just include the script or import with NPM and call any function you need. Use the left or bottom navigation (on mobile) to find topic you need or study examples on the main page.
Actual: v1.1.0 11/2024
Selector / Getting DOM elements
Use o('selector') to select elements like querySelectorAll. If you need only the first element use o.first(selector). Also it can get a DOM element or an array of elements o(elements) or self object in state. Function returns object with Objs methods and DOM elements - more info below.
// gets all elements and returns Objs object
const objElements = o('.class');
// gets the first element only
const objElement = o.first('.class');
// gets new elements to operate
objElements.reset('.class2');
// query to find all children of each element by selector
objElement.find('.childClass');
// returns only the first child of each element
objElement.first('.childClass');
// give an element or an array[] of elements to set them for operating
const objElement = o(document.getElementById('name'));
There are some ways to get DOM elements from Objs if you need:
// returns all selected DOM elements as array [el0, ..]
o('.class').els;
// number of elements
o('.class').length;
// the first DOM element itself
o('.class').el;
// the last DOM element
o('.class').last;
// Select elements from set
// sets the i DOM element for operations (starts from 0 index)
objElements.select(i).el;
// sets the last item to operate
objElements.select().el;
// sets all DOM elements to control again
objElements.all().els;
To remove selected elements from DOM or control Objs list use this:
// removes all elements from DOM
objElements.remove();
// removes i-index element from DOM and returns Objs
objElements.remove(i);
// adds element to operation list and sets oInit attribute to parent's
objElements.add(element);
// it is possible to use selector or an array of elements
objElements.add('.class2');
// removes i-index element from list of objects to operate (not from DOM)
objElements.skip(i);
If you have inited some events by .on(), you should init them for added elements by .onAll(). If you need to disable all events, e.g when a modal window is showed, use .offAll(). It just disable events, so .onAll() will enable them again.
objElements.add(element).onAll();
objElements.offAll();
Testing
Direct changing DOM elements
There are several methods to change all elements or some of them (by using .select(), .skip(), etc.)
Changing attributes
// any attributes
// setting
o(selector).attr('attr', 'value');
// getting
o(selector).attr('attr');
// removing
o(selector).attr('attr', '');
// getting attributes as an array from all elements [{}, ...]
const attributes = o(selector).attrs();
// getting attributes of selected element {}
const attributes = o(selector).select(3).attrs();
// dataset
// setting
o(selector).dataset({
action: "registration"
});
// getting as an array from all elements [{}, ...]
const datasets = o(selector).dataset();
// getting of selected element {}
const dataset = o(selector).select(3).dataset();
// style
// setting attribute as a string
o(selector).style('value');
// setting as array of values
o(selector).css({
height: "100px",
"max-width": "100px",// use "" for hyphen styles
});
// getting inner HTML of all elements in one string
o(selector).innerHTML();
There are special functions to controll class attribute.
// direct setting value of attribute
o(selector).setClass('class1 class2');
// add class for all elements
o(selector).addClass('class');
// remove
o(selector).removeClass('class');
// switching having and not having "class"
o(selector).toggleClass('class');
// switch by condition
o(selector).toggleClass('class', isActive);
// returns false if any or selected element doesn't have the class
const changed = o(selector).haveClass('class');
To append HTML or elements to DOM you can use this functions. Append functions get selector or a DOM element.
// setting inner HTML
o(selector).innerHTML('html');
o(selector).html('html');
// inner text
o(selector).innerText('text');
// inner text content
o(selector).textContent('text');
// creating element for example below, more info in section States control
const objs = o.init(states).render();
// append as the last child/children of the element
// or in the first found by selector
objs.appendInside('#root');
// append before, after
objs.appendBefore(element);
objs.appendAfter('#child');
If you need to operate elements, use .forEach(function) - it gets the same parameters as a state: o, self, i-index and el for each element.
o(selector).forEach(({self, i, el, o}) => {
// self.els[i] - each controlled element
});
Testing
React and JSX
To create a Component for JSX or React element from Objs use o.prepareFor(). The first parameter can be React or createElement function. If the second React.Component is given, the Component will be returned.
It returns clone HTML elements without states, so init them separately on elements after render.
import React from 'react';
import ReactDOM from 'react-dom/client';
// Element example
const root = ReactDOM.createRoot(o('#root').el);
root.render(o.prepareFor(React));
// Component example for JSX
const MyComponent = o.prepareFor(React, React.Component);
function App() {
return (
<div>
<MyComponent />
</div>
);
}
Events
There are synonyms for standart event methods but they can get multi events separated by ', ' (comma and space). Check event.target (e.g. its classList) in the listener to check if common document event started by the right element.
// synonym for addEventListener but with multi events
o(selector).on('event1, event2', listener, options);
// multi events removing (removeEventListener)
o(selector).off('event1, event2', listener, options);
If you use .select() before on/off, it will be effected only for the selected element.
Sometimes it is needed to off all events for some time. To do that use methods below.
// off click handlers
o(selector).offAll('click');
// off all event listeners
o(selector).offAll();
// on events
o(selector).onAll();
All inited events are saved in special parameter .ie - it saves each handler of each event to on/off them. You can get functions from there and init them on other elements.
// the first (0) click event function in array
objs.ie.click[0]
// contains:
// 0: function, 1: options or useCapture or undefined, 2: wantsUntrusted if was set or undefined
// copy click events to other elements with parameters
objs.ie.click.forEach(handler => {
o(selector).on('click', ...handler);
});
Testing
State control for DOM elements
One of the features of Objs - states. It gets a special object of states collection and switches elements between them. It separates logic and veiw by using samples. Samples are bigger than componenst and allow to operate modules without micro separation.
Common states object
It is an object with states as object / string / function value.
If element has to be created - states object should contain render state for creation. Set tag with tag name there or it will be set as 'div'.
// common structure
const states = {
// DEFAULT NAMED state, required for creating element
render: {
tag: "div",// tag name
html: "string",// use `` to make big samples or append attribute
class: "string",
// it is possible to return nothing
events: ({self}) => {
self.on('click', handler);
},
// other attributes
},
// states to change element
stateName: {
attribute: "string",
// function that returns attribute value
class: ({disabled}) => {
return `link ${disabled ? 'hidden' : ''}`;
},
},
// state for on/off events
startEventsName: ({self}) => {
self.onAll('click');
},
pauseEventsName: ({self}) => {
self.offAll('click');
}
}
To append children elements, use append attribute in state with a DOM element or Objs objects or an array of them - they will be added in order.
Use HTML string from markup developer instead of object as 'render' state to init elements even faster. To use dynamic content - set 'render' state as function that returns HTML string. If there are several elements - they will be in a div container.
const states = {
render: (props) => {
return `
<h3>HTML ${props.title} here</h3>
<p>Both tags will be in common DIV and
it is controlled by Objs, not these</p>
`;
},
shown: {
removeClass: 'hidden',
},
hidden: {
addClass: 'hidden',
}
}
If you need fast init and render, use .initState(state, data) - it creates or changes element for the state by rendering it immediately. It is equal to .init(state).render(data)
// creates element
const link = o.initState({
tag: 'a',
href: '/',
innerText: 'Main page'
});
const activeState = {addClass: 'active'};
// fast change element
link.initState(activeState);
Creating elements
Depends on your architecture it is possible to use several types of states value. The simpliest is just string as state without state name. Recommended to use states object with states to be sure about elements initialisation and possibilities of control, e.g. .render() method can create an element for each data in array.
To create element use o.init(state).render() and then append it as a HTML or by special method. Without appending element will not appear in DOM.
// append as HTML
o('#root').innerHTML(o.init(states).render().html());
// append by method
o.init(states).render().appendInside('#root');
// append methods from Direct changing DOM elements section
objs.appendInside('#root');
objs.appendBefore(element);
objs.appendAfter('#child');
State string
// HTML string
o.init('<p>String</p>').render();
State as object.
o.init({// state
tag: 'img',
src: ({src, utm}) => {
return src + '?utm=' + utm;
},
alt: ({alt}) => {return alt}
}).render(props);
State function returning string and .render(data) for 2 elements to create 2 of them and append in DOM.
// function that returns HTML string
o.init(({title, text, data, id}) => {
return `
<div id="article${id}" class="article">
<h2>${title}</h2>
<p>${text}<br>
<hr>
${data}</p>
</div>
`;
}).render([// automatically creates 2 elements for each data set
{
id: 1,
title: 'First title',
text: 'Text of an article',
data: '12/13/2022'
},
{
id: 2,
title: 'Second title',
text: 'Text of an article',
data: '12/13/2022'
},
]).appendInside('.o-articles');// inserts in a first ".o-articles" element
Recommended states object for creating and control elements.
Every state and attribute function gets self, o, i in parameters for inited object (self), o function and index of current element, e.g. for events to control special element, not all od them.
const banner = o.init({// states
render: {
tag: 'img',
class: 'banner-img banner-hidden',
src: ({src, utm}) => {
return src + '?utm=' + utm;
},
alt: ({alt}) => {return alt}
},
shown: {
removeClass: 'banner-hidden'
},
events: ({self}) => {
document.addEventListener('scroll', () => {
self.shown();
})
}
}).render(props).events();
Switching states
To switch states of existing element you just select it by o(), load states with .init() and use methods named as states to switch elements between them easily. After every operation it returns Objs with all methods.
To change specific element use .select() before switching state. If you need to change child, use o(self).find(selector) to create separate object and control other elements.
// example
// creating and appending element
const menuID = o.init(menuStates)
.render(menuData)
.appendInside('#root')
.initID;
// changing state somewhere
o(menuID).stiky();
// or use selector and special method
o.take('.class').stiky();
If element states were inited, it is cached and all states are available by o(query) or o(initID) without new initialization - example above.
Example
Full example of real states object with render state and some other.
// example of states object
const states = {
// state for creating elements if needed
render: {
tag: 'button',// default is 'div' (if doesn't set)
class: 'button',// use addClass to add and removeClass to remove,
html: 'Click me',
onclick: 'clickFunction()',
disabled: true
},
// attaching events
events: ({self, o}) => {
// self is already initialized with all elements
self.on('mouseenter', (e) => {
o(e.target).active();// element is inited, so it has all states
});
self.on('mouseleave', (e) => {
o(e.target).disabled();
});
},
// other states and attributes to change
special: {
// use function to create any dynamic value or build inner HTML
html: (props) => {return props.name},
disabled: (props) => {return Boolean(props.disabled)}
},
error: {
// there is o(), self, i in the props to control element
showError: (props) => {
// setting error text
props.el.closest('.error-text').innerText = props.errorText;
},
disabled: true
},
active: {
disabled: false,
clearError: (props) => {
props.el.closest('.error-text').innerText = '';
}
},
disabled: {
disabled: true,
}
};
If state is a function, it gets props argument with o function, self that contains all functions and inited elements and i index of current element.
If attribute is a function, it gets props argument (data is common with state props) with same o, self, i.
There are special attributes: addClass, removeClass, toggleClass to do so. Style attribute can be style: {} (object) to set more comfortable.
// inits existing element states and runs function for binding events
const btn = o('.button').init(states).events();
// making all ".button" elements "active" state
btn.active();
// switching state to "special" with some text
btn.special({name: 'Special state', disabled: false});
// creates an element from "init" state that can be appended in DOM
const newBtn = o.init(states).render().el;
// creates an element for each prop in props array
const newBtns = o.init(states).render([prop0, prop1]).el;
// it is possible to use state itself and default state will be render()
const newSameBtn = o.init({
tag: 'button',
class: 'button',
html: 'Click me',
onclick: 'clickFunction()',
disabled: true
}).render().el;
// you can use just html to render blocks
const btnState = (props) => {
return `<button class="button">${props.text}</button>`;
};
const anotherBtn = o.init(btnState).render({text: 'Button 1'});
// if props is an array, elements will be created for each
anotherBtn.reset().init(btnState).render([{text: 'Button 1'}, {text: 'Button 2'}]);
Creating states and HTML from DOM
// returns HTML of all elements, e.g. to make innerHTML
const classHTML = o('.class').html();
// returns HTML of all elements (in this case - just one created button)
const newBtnHTML = o.init(states).render().html();
// creating an object with "render" state from the first element
const newStates = o('.button').sample();
// creating an object from the second element
const newStates = o('.button').select(1).sample();
// example of using "cloning" state from DOM
o.initState({
...newStates,
html: ({title}) => {return title},
onclick: ({key}) => {globalSelect(key)},
},
[
{title: 'Btn 1', key: 'one'},
{title: 'Btn 2', key: 'two'},
]);
It is possible to get element in DOM by o(initID) where initID is a parameter objs.initID.
Testing
Import and cache scripts, styles
There is a function o.inc() to import JS scripts / CSS styles / images for modules and HTML states. Also it makes cache in localStorage by sending GET request (enabled by default).
// parameters and defaults
// set false to disable
o.incCache = true;
// 24 hours to store cache
o.incCacheExp = 1000 * 60 * 60 * 24;
// timeOut to load all functions. After - cancels callback and runs failure function
o.incTimeOut = 6000;
// addition for src in the beginning
o.incSource = '';
// prevents repeated including with the same ID (if included before)
o.incForce = false;
// set to false if you need an order in loading scripts
o.incAsync = true;
// service data
// array of all "name: 1/0" statuses of included scripts to check directly
o.incFns = {};
// array of including sets statuses
o.incSet = [0, ...];
Here is the main function. It returns ID of the loading set from o.incSet[] where is 1 for successful load of all scripts and 0 for failure. If scripts are still loading now - there is a callback function. If sources is missing - it returns id of the last loading.
const incId = o.inc({// returns ID to check the status
'name': 'src'// name and src to JS, CSS or an image file
}, callback, callbad);
// simple version with array of links
o.inc([
'modal.js',
'modal.css',
'modalBackground.jpg',
], (setId) => {
// success function gets set ID, e.g. to check statuses
});
o.incSource = 'scripts/';
o.inc({
pow: 'pow.js'// name for identification and src for loading script
powStyle: 'pow.css'// styles for the module
banner: 'pow.jpg'// image to preload
}, () => {// function to run after loading
console.log('Successful using script with pow()');
}, () => {// function to run if failed with timeout
console.log('Loading failed');
});
// checks last loading set of functions
if (o.incCheck(o.inc())) { ... }
// checks special set by ID
if (o.incCheck(incId)) { ... }
// check if the current script is loaded, 'pow' - script name
if (o.incFns['pow']) { ... }
// clears all localStorage cache
// if "all" is true - clears all system parameters
o.incCacheClear(all);
If o.inc() gets an array, there will not be any cache control or check for repeat. Also there is no cache for links started with 'http'.
Testing
GET and POST requests
There are some basic promise functions for sending requests. It's possible to use just one parameter - url. They return Promise so you can use .then() or await.
// simple GET
o.get(url)
.then();
// GET with object data and JSON response
o.get(url, {
data: {
param1: 'value1',
// object value will be JSON stringified
param2: {
...
}
}
})
.then(response => response.json())
.then((response) => {
...
});
For POST you can use body parameter for special data or data parameter for auto-creation data-string.
// POST with object data and JSON response
o.post(url, {
data: {
param1: 'value1',
// object value will be JSON stringified
param2: {
...
}
}
})
.then(response => response.json())
.then((response) => {
...
});
To unify request use .ajax() - it can be GET or POST request.
o.ajax(url, {
method: 'post',// should be post or get
// other parameters, e.g. data
})
.then(response => response.json())
.then((response) => {
...
});
Use o.getParams() to have an array of GET parameters of the page
const params = o.getParams();
Testing
Unit tests and its parameters
Test function is unified and gets test title and tests and as the last parameter can be finish function. It runs after tests or by timeout. Results log can be in two kinds: console style (default) and HTML (o.tStyles = true) – both you find below in test section.
Test expression or function should return true for successful check. If it is false or some string - it marks as error and string is shown as an error text.
To test async functions e.g. requests - use o.testUpdate() with params in check function.
// parameters and defaults
// set to true to see success test results too
o.tShowOk = false;
// set to true to make results HTML styled
o.tStyled = false;
// timeout of async tests
o.tTime = 2000;
// an array of all test sessions
// get session you want and check results or .join() to see all of them
o.tLog = [];
// an array of true/false results of all test sessions
o.tRes = [];
// an array of arrays with true/false results of each test in sessions
o.tStatus = [][];
// usage
o.test("session / function title",// the main title of this test session
// array with a title of check and expression/function to be true
["test/check title", func() === val],
["test/check title", () => {
// return true for success and false or error text for failure
return true;
}],
// get info for async tests
["test/check title", (info) => {
// use o.testUpdate to update test status
setTimeout(() => {
o.testUpdate(info, true, ' - some additional text');
}, 100);
}],
// callback function after all tests (or timeout), gets session number
(n) => {
console.log('Result: \n' + o.tLog[n]);
}
);
There is a function that will be called on errors during Objs functions. Set o.onError to a function and display or log errors as you need. As default, errors will not be shown.
o.onError = (error) => {
console.log(error);
}
Testing
Examples for each test:
tStyles: false; (console view but \n changed to br tags)tStyles: true; (HTML)
Debug settings
Error messages are off by default, but you can turn them on by changing o.showErrors to true. Or use o.logErrors() to see all hidden errors in the console.
To have your own error tool, set o.onError to the function you need. To see hidden errors – get them from o.errors array.
o.errors.forEach(o.onError)
Restrictions
States should NOT be like Objs methods or parameters below
length
el
els
last
ie
initID
initedEvents
Params for states should NOT have keys like o, i, self.
Events should be added by self parameter in states to be in ie array of controlled listeners.
States are overwritable - they can be over write or over write parameters. Be careful.