RU
FAST          
          and SIMPLE

Objs

  JavaScript library to  
  develop, test, cache and  
  optimize  

Features

You can develop new features with Objs without rewriting anything

Develop

Test

Optimize

With Objs you finally can separate logic and HTML and have full access to cache abilities, async module loading and auto tests for the whole web app. For developers – faster and simplier architecture, for business – cheaper development by using layout designers and module project structure if needed.

*Async – asynchrone, in parallel to normal page functionality.

Get started

Include actual version: v1.0 02/2023

<script src="objs.1.0.min.js" type="text/javascript"></script>

Then use o functions in your project.

import o from 'objs-core';

o.init(states)
	.render()
	.appendInside('#root');

Every function has documentation there but the main principles and examples are below.

Main principles

Logic structure to create any dynamic module and controll it

To control element Objs uses states. State - it's an information how to create or change element. To create element use render state with html (inner HTML) and tag attributes.

// timer create state called render
const timerStates = {
	render: {
		class: 'timer',
		html: 'Seconds: <span>0</span>',
	}
}

Default tag is div. Attributes dataset and style can be object type. Also, render could be a string like: '<div class="timer">Seconds: <span>0</span></div>'

Then add a new state that will start counting. Number will be stored in the object itself - self object. So the state will be a function that gets self, creates a variable, increments it by interval and shows as innerHTML of span.

// new timer states object
const timerStates = {
	render: {
		class: 'timer',
		html: 'Seconds: <span>0</span> ',
	},
	start: ({self}) => {
		// save number or create
		self.n = self.n || 0;
		// start interval
		setInterval(() => {
			self.n++;
			o(self).first('span').html(self.n);
		}, 1000);
	}
}

States are done and the last thing is to create and append element on the page. To do this - init states, render object and start timer... And also - append it:

o.init(timerStates).render().start().appendInside('#simpleTimer');

Somewhere here:

There are more complex examples below but with the same logic: create states, some functions that changes element then init and append elements on page. States after initialization are self methods in addition to standart on, first, remove and etc.

State examples

First part of classical usage examples but with some features

Timer with start / stop

Timer that counts seconds on page but can be stopped. The main structure: state to render, to set events, to change state of the element and to start/stop. When the timer is stopped we append new element to show status. Data saved in the self object.

Important: change by o() in states only children elements to keep pure logic.

o.init({
	render: {
		class: 'exampleTimer',
		html: 'Seconds: <span>0</span>',
	},
	events: ({self}) => {
		self.on('click', () => {
			// check status
			if (self.interval) {
				// use other states as methods
				self.stop();
			} else {
				self.start();
			}
		});
	},
	tick: ({self, o}) => {
		// saving data in self object
		self.n = self.n || 0;
		self.n++;
		// getting child and change
		o(self).first('span').html(self.n);
	},
	// timer control
	start: ({self}) => {
		// setting interval to control
		self.interval = setInterval(() => {
			self.tick();
		}, 1000);
		// removing status
		o(self).first('.status').remove();
	},
	stop: ({self, o}) => {
		// stop interval
		clearInterval(self.interval);
		delete self.interval;
		// add status title
		o.initState(
			'<div class="status">(stopped)</div>'
		).appendInside(self.el);
	}
}).render()
	.events()
	.start()
	.appendInside('#exampleTimer');

Todo list / task manager

Dynamic list of tasks to add, check and delete them. Here you see the same principles of controll and using data for creation start elements.

Special thing is that here .render(listData) creates element for each data set in listData array. Also there is an ID for each task to make label and checkbox synchronized stored as rowID in form object.

New task adds as an element to rows inited object, selects as the last one by .select() and gets inited events. It is recommended to remove selection by .all() to be sure that the common object is not changed e.g. if it's states used somewhere for all elements.

const listData = [
	{
		text: 'Task number one',
		checked: true,
	},
	{
		text: 'Task number two',
		checked: false,
	},
];

const rowState = {
	render: ({checked,text,i}) => `
		<li class="tm__row" >
			<input type="checkbox" 
				${checked ? 'checked="1"':''} 
				id="task${i}">
			<label for="task${i}">
				${text}
			</label>
			<input 
				class="tm__delete" 
				type="button">
		</li>
	`,
	events: ({self, o, i}) => {
		o(self.els[i])
			.first('.tm__delete')
			.on('click', () => {
				self.remove(i);
			});
	},
};

const formState = {
	render: `
		<input 
			class="tm__field" 
			type="text" 
			maxlength="27" 
			placeholer="Input text">
		<input 
			class="tm__add" 
			type="button">
	`,
	events: ({self, o}) => {
		o(self.el)
			.first('.tm__add')
			.on('click', () => {
				const rowsObjs = 
					o.take('.tm__row') 
					|| o.init(rowState);
				const i = self.rowID 
					|| rowsObjs.length;
				self.rowID = i + 1;

				rowsObjs.add(
					o.init(rowState).render({
						i: i,
						text: o(self.el)
							.first('.tm__field')
							.el
							.value
					}).el
				)
					.select()
					.events()
					.appendInside('.tm__list')
					.all();

				o(self.el)
					.first('.tm__field')
					.el
					.value = '';
			});
	}
};

o.initState({
	tag: 'ul',
	class: 'tm__list',
	append: o.init(rowState)
		.render(listData)
		.events()
		.els,
}).add(
	o.initState(formState)
		.events()
		.el
).appendInside('#taskManager');
	

Important moments

To insert elements and save their events use append in state, not html.

You can create and render elements from objects, strings, functions and for each data set. To control children elements use o() to create separate object. To load inited elements use o.take(query).

.add() element changes dataset oInit attribute to the parent's one but after removing from DOM it is saved in o.inits[] and self object. Save initID for big modules to take them from o.inits[] and just append instead of new full initialization.

State cache

To optimize some big components loaded from server render Objs can make state object and control it e.g. save HTML string from .html() in localStorage and set cookie for server to return sample and then change it to real element and change if something changed.

It is possible to control history object and cache all HTML samples to switch pages just by AJAX for data.

const noticeState = ({title}) => {
	return `<div 
		class="exampleMenu__message">
			${title}
		</div>
	`;
};

// creating states object from DOM element
const menuStates = {
	render: () => {
		// check if server knows about cache
		if (!getCookie('exampleMenu')) {
			localStorage
				.setItem(
					'exampleMenu', 
					o('.exampleMenu').html()
				);
			setCookie('exampleMenu', true);
		}
		return localStorage.getItem('exampleMenu');
	},
	// add dynamic element
	newMessage: ({self, title}) => {
		o.init(noticeState)
			.render({title})
			.on('click', (e) => {
				o(e.target).remove();
			})
			.appendInside(self.el);
	}
};

o.init(menuStates)
	.render()
	.appendInside('#exampleMenu')
	.newMessage({
		title: 'New Message!'
	});
Site menu
New element:

Add events state like in examples above to handle menu events. Changes also can be cached and inited by AJAX for special data, instead of full element rendering.

Include and cache example

Second part to show the power of async preloading and cache

Preloading

Here is an example of async module loading - see o.inc() function. Success function runs after everything is loaded. As default JS and CSS are cached for 24 hours in localStorage. It is possible to change time or off.

Reload the page or press button to see loading from cache. Clear button removes cards to prevent scaling image without styles. Limitation: 3 cards at one time.

// card.js
let initCard = (query) => {
	return o.initState(`
		<div class="card">
			<img 
				class="card__img" 
				src="/objs/card.jpg">
			<div 
				class="card__text">
				It is a sea
			</div>
		</div>
	`).appendInside(query);
};

// example
o.initState(
	'<button 
		class="example__btn">
		Press to load
	</button>'
).on('click', () => {
	// PRELOADING
	o.inc({
		'cardJS': 'card.js',
		'cardCSS': 'card.css',
		'cardJPG': 'card.jpg',
	}, () => {
		// RUN
		initCard('#examplePreloading');
	});
}).appendInside('#examplePreloading');

o.initState(
	'<button 
		class="example__btn">
		Clear cache
	</button>'
).on('click', () => {
	o('#examplePreloading div').remove();
	o('.card').remove();
	o.incCacheClear(true);
}).appendInside('#examplePreloading');

Test example

The last example - test features

Unit tests

Test function gets test title, then test arrays [title, test] and the last one can be a function to run after tests are complete. Standart timeout for async tests is 2s.

Switch checkboxes to see results. The last two are async tests and if the last one is false - test will finish after 2s timeout. Off HTML styled to see console view with /n changed to <br>.

// turn on showing success result
o.tShowOk = true;

const results = o('.testResult').on('change', () => {
	// HTML style
	o.tStyled = o('#testStyled').el.checked;
	// test function
	o.test(
		'Test title',
		// just boolean
		[
			'First checkbox', 
			results.els[0].checked
		],
		// returns error
		[
			'Second checkbox', 
			results.els[1].checked || 'Not checked'
		],
		// test function
		[
			'Third checkbox', 
			() => {
				return results.els[2].checked;
			}
		],
		// async function
		[
			'Fourth checkbox', 
			(info) => {
				setTimeout(() => o
					.testUpdate(
						info, 
						results.els[3].checked), 
				100);
		}],
		// async timeout expires or not
		['Fifth checkbox', (info) => {
			if (results.els[4].checked) {
				setTimeout(() => o.testUpdate(
					info, 
					true), 
				100);
			}
		}],
		// after all tests or timeout
		(n) => {
			o('#exampleTests')
				.html(
					o.tLog[n].replaceAll('\n', '<br>') + 
					'<br>' + 
					JSON.stringify(o.tStatus[n])
				);
		}
	);
});

Results:

For auto tests - get n in the last function parameter and check o.tRes[n]. If true - all tests in the set are correct. If not - show o.tLog[n] or check o.tStatus[testN] - an array of each test status. Function o.test() also returns n.

To top