React has become one of the most popular frameworks for building user interfaces. A typical React application looks like this:
React Application (App.js)
import React, { useState } from 'react';
import './App.css';
function App() {
// State to hold the message
const [message, setMessage] = useState('Welcome to My Simple React App');
// Function to update the message
const handleClick = () => {
setMessage('Hello! You clicked the button.');
};
return (
<div className="App">
<header className="App-header">
<h1>{message}</h1>
<button onClick={handleClick}>Click Me!</button>
</header>
</div>
);
}
export default App;
Styling with CSS (App.css)
.App {
text-align: center;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
button {
font-size: 1.2em;
padding: 10px 20px;
cursor: pointer;
background-color: #61dafb;
border: none;
border-radius: 5px;
margin-top: 20px;
}
button:hover {
background-color: #21a1f1;
}
The issue with this approach is that React doesn't provide a unified way to manage state, handle presentation, manage events, make API calls, or handle animations. Additionally, controlling program flow with time is challenging. Moreover, the HTML tree and CSS introduce an extra intermediary layer that is inherently static, making it difficult to control affecting design flexibility.
TargetJ is a new JavaScript framework that introduces a new paradigm for programming single-page sites, addressing all the issues mentioned above. It is open-source, and you can find it at https://github.com/livetrails/targetj. The core concept of the framework is that it gives life cycles to methods and variable assignments, allowing them to operate autonomously, much like living cells.
This is achieved by providing a unified interface wrapper for both methods and variable assignments, forming new constructs called targets. The interface wrapper doesn't necessarily add extra code, as targets can also be written as methods or variable assignments. Targets can also be written as objects, which is the most expressive format, allowing them to have callbacks to adjust to external changes.
In other words, targets are the fundamental building blocks of components. Every component in TargetJ is composed of a set of targets.
A brief explanation of what targets consist of will help in understanding how TargetJ works. Each target has the following:
1. Target Value and Actual Value
The target value is the value assigned to a variable or the result produced by a method, while the actual value is the one used by the rest of the application. When the target value differs from the actual value, TargetJ updates the actual value iteratively until it matches the target value. This process is controlled by two additional variables: Step
, which defines the number of iterations, and Interval
, which specifies the duration (in milliseconds) the system waits before executing each iteration.
2. State
Targets have four states that control their lifecycle: Active, Inactive, Updating, and Complete.
-
Active: This is the default state for all targets. It indicates that the target is ready to be executed, meaning the target value needs to be initialized from the variable it represents, or its
value()
method needs to be executed to calculate its output. - Inactive: Indicates that the target is not ready to be executed.
- Updating: Indicates that the actual value is being adjusted to match the target value.
- Complete: Indicates that the target execution is finished, and the actual value has successfully matched the target value.
3. Target Methods
All methods are optional. They are used to control the lifecycle of targets or serve as callbacks to reflect changes.
-
Controlling methods:
enabledOn
,loop
,steps
,cycles
. -
Callbacks:
onValueChange
,onStepsEnd
,onImperativeStep
,onImperativeEnd
,onSuccess
,onError
.
TargetJ Solution
Let’s see how TargetJ tackles the issues mentioned earlier.
1. Handling presentation
Let’s start with the button as we work progressively to implement the React example above, allowing us to illustrate each issue more clearly.
In the example below, you can see that the HTML elements, styles, and attributes are all written as targets, each capable of operating independently. For instance, the background target has three values and will continuously morph between them over 50 steps. HTML nesting can also be dynamically changed by targets. This is achieved by declaring the domParent
target from the element seeking a container or the domHolder
target, declared by containers for elements without a domParent
. In our example, the button has no domParent
, so it will be nested in the application root, supporting a flat HTML structure.
You can view a live example here: https://targetj.io/examples/articlePresentation.html.
TargetJ Application (articlePresentation.js)
App(new TModel({
width: 100,
height: 100,
fontSize: '1.2em',
lineHeight: 100,
cursor: 'pointer',
border: 'none',
borderRadius: 5,
html: 'Click me',
topMargin: 20,
x() { return this.getCenterX(); },
background: {
loop: true,
value() {
return { list: [ '#21a1f1', '#61dafb' ] };
},
steps: 30
}
}));

2. Animation
In the next example, we add four sets of animation moves to our button by creating a new target called animate
. We iterate through the array of moves using cycle
, which only updates once all the moves in a set are completed. The setTarget
defines an imperative target within the declarative target of animate
. The onImperativeEnd
callback is triggered when the actual value reaches the target value of each style attribute.
We also added a baseElement
target, which alternates between <img>
and <button>
every second. The focus here is not on presentation, but rather to illustrate how HTML can be seen in a new light, reducing the design constraints typically imposed by it.
You can view a live example here: https://targetj.io/examples/articleAnimation.html.
TargetJ Application (articleAnimation.js)
App(new TModel({
width: 100,
height: 100,
src: 'https://targetj.io/img/structure.jpg',
fontSize: '1.2em',
lineHeight: 100,
cursor: 'pointer',
border: 'none',
borderRadius: 5,
html: 'Click me',
topMargin: 20,
x() { return this.getCenterX(); },
baseElement: {
loop: true,
cycles: 1,
interval: 1000,
value(cycle) { return ['img', 'button' ][cycle]; }
},
background: {
loop: true,
value() { return { list: [ '#21a1f1', '#61dafb' ] }; },
steps: 30
},
animate: {
cycles: 3,
value(cycle) {
return [
{ rotate: 0, scale: 1, background: 'blue', borderRadius: 0 },
{ rotate: 180, scale: 1.5, background: 'red', borderRadius: 75 },
{ rotate: 360, scale: 2, background: 'yellow', borderRadius: 50 },
{ rotate: 0, scale: 1, background: '#61dafb', borderRadius: 5 }
][cycle];
},
onValueChange(value) {
this.setTarget("coolAnimation", value, 50);
},
onImperativeEnd(key) {
if (key === 'background' && this.getTargetCycle(this.key) === 3) {
this.resetImperative(key);
}
}
}
}));

3. Handling events
In this example, we added three targets to handle three events: click
, mouseenter
, and mouseleave
. The target events onClickEvent
, onEnterEvent
, and onLeaveEvent
are special targets that the TargetJ task will call when the corresponding events are triggered. These targets can either activate other targets or handle the events directly as methods.
We also added a special target called canHandleEvents
, which specifies that the button can handle touch events. Since canHandleEvents
is a target, it can also be activated by other events, enabling or disabling touch event handling based on certain conditions.
Notice that the shake
target has the active
flag set to false
. This indicates that the target will not be executed until it is activated by other targets.
You can view a live example here: https://targetj.io/examples/articleEvents.html.
TargetJ Application (articleEvents.js)
import { App, TModel, Easing } from "targetj";
App(new TModel({
canHandleEvents: 'touch',
width: 100,
height: 100,
fontSize: '1.2em',
lineHeight: 100,
cursor: 'pointer',
border: 'none',
textAlign: 'center',
borderRadius: 5,
html: 'Click me',
background: '#61dafb',
topMargin: 20,
x() { return this.getCenterX(); },
shake: {
active: false,
cycles: 3,
value(cycle) {
const x = this.getX();
this.setTarget('x', { list: [ x - 20, x, x + 20, x ] }, 10 - 2 * cycle, 0, Easing.inOut);
}
},
onClickEvent: 'shake',
onEnterEvent() { this.setTarget('background', '#21a1f1'); },
onLeaveEvent() { this.setTarget('background', '#61dafb'); },
}));

4. Calling API
In the example below, we define a target named load
. Inside the value
function, we make an API call using fetch()
. The second argument specifies the API URL, and the third argument contains the query parameters passed to the API. A fourth optional parameter, omitted in this example, can specify a cache ID if we want to cache the result. This cache ID can also be used to retrieve the cached data. If it’s not specified, the result will always come from the API.
Once the API response is received, it triggers either onSuccess
or onError
, depending on the outcome.
You can view a live example here: https://targetj.io/examples/articleAPI.html.
TargetJ Application (articleAPI.js)
import { App, TModel, getLoader } from "targetj";
App(new TModel({
canHandleEvents: 'touch',
width: 100,
height: 100,
fontSize: '1.2em',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center',
cursor: 'pointer',
border: 'none',
borderRadius: 5,
html: 'Click me',
background: '#61dafb',
topMargin: 20,
x() { return this.getCenterX(); },
load: {
active: false,
value() {
return getLoader().fetch(this, 'https://targetj.io/api/randomUser', { id: 'user0' });
},
onSuccess(res) {
this.setTarget('html', `My name is ${res.result.name}`);
},
onError() {
this.setTarget('html', 'Click again!');
}
},
onClickEvent: 'load',
onEnterEvent() { this.setTarget('background', '#21a1f1'); },
onLeaveEvent() { this.setTarget('background', '#61dafb'); }
}));

5. State Management
Each target manages its own state. Targets can be activated by certain events, such as user actions or when the page is closed, allowing them to refresh their states. TargetJ tasks execute all targets in the order they appear, from top to bottom. For more complex cases, enabledOn()
can keep a target active until the condition defined within enabledOn()
is met.
The getPager()
function in TargetJ, used for switching between pages, maintains a cache of each page. Similarly, the getLoader()
, as we saw above, can cache results to be used across different parts of the application.
6. Controlling the execution flow with time
TargetJ was designed from the ground up to handle complicated and intricate sequences. Here is a brief explanation of what it provides: Each target has an interval
property, which can also be implemented as a method. The interval
controls the execution rate of the value()
of the target. The execution rate for each step, which updates the actual value to match the target value, can also be configured. Imperative targets can define their own interval
as well. The onImperativeStep
and onImperativeEnd
callbacks allow you to listen for each step update or when all steps of the imperative targets are completed.
Here is the full implementation of the React example mentioned earlier, with the following added to demonstrate TargetJ’s ability to control different parts of the application over time:
- Display the message first, then add the button after 1 second.
- Scale the button and revert back after 1 second from showing it.
You can view a live example here: https://targetj.io/examples/articleTime.html.
TargetJ Application (articleTime.js)
import { App, TModel, getEvents } from "targetj";
App(new TModel({
title() {
return new TModel({
topMargin: 20,
html: 'Welcome to My Simple TargetJ App',
x() { return this.getCenterX(); }
});
},
button() {
return new TModel({
canHandleEvents: 'touch',
fontSize: '1.2em',
padding: '10px 20px',
cursor: 'pointer',
border: 'none',
borderRadius: '5px',
html: 'Click me',
topMargin: 20,
scale: {
cycles: 1,
interval: 1000,
value(cycle) {
return [ 1, [ { list: [ 1, 2, 1 ] }, 30, 0 ] ][cycle];
}
},
background() { return getEvents().isTouchHandler(this) ? '#21a1f1' : '#61dafb'; },
x() { return this.getCenterX(); },
onClickEvent() { this.getParentValue('title').setTarget('html', 'Hello! You clicked the button.'); },
onEnterEvent: 'background',
onLeaveEvent: 'background'
});
},
children: {
cycles: 1,
interval: 1000,
value(cycle) {
return [ this.val('title'), this.val('button') ][cycle];
}
}
}));

Conclusion
We believe TargetJ offers a fluid and flexible medium that enables a new kind of user experience that was previously difficult to achieve. It also simplifies development significantly, making it more enjoyable without sacrificing performance. You can find the framework at https://github.com/livetrails/targetj and explore its interactive documentation at www.targetj.io.
Top comments (0)