A Native JS Framework?
I set out to see how far an app can get with native javascript. I named the core function jsapp() because it sounds easy to say like Jay Sap and can be literally read as "js app", but init() might be more suitable. I demonstrated a ton of different ways of loading data, and I included "components" in the form of calling a function instead of referencing the variable.
The way it works is by using javascript's map and join functions on an array-ified data object that holds all of the app's data. It makes use of js template literals instead of a custom template string as would normally be found in moustache or handlebars.
I haven't considered what would happen if this were "live" - meaning that I haven't yet considered the question of how re-renders be would handled efficiently. This is mainly a demonstration of native javascript templating and a showcase of some of the improvements made to the language over the years that have increased the readability of js code significantly.
const jsapp = appData => {
// not nice, but necessary for button onclick
if (!jsapp.appData) {
console.log('app data not set');
jsapp.appData = appData;
}
document.querySelectorAll('.app-test').forEach(element => {
element.innerHTML = ([appData].map(app => `
<div class="app-content">
<header class="header">
<h1>${app.header.title}</h1>
<h2>${app.header.subtitle}</h2>
<button onclick="jsapp.appData.header.login.handleLogin()">${app.header.login.title}</button>
</header>
${app.characters()}
<div>
<p>
${app.content.copy}
</p>
</div>
${app.summary(app.header)}
<footer>Copyright ${app.footer.copyrightYear}</footer>
</div>
`).join(''));
});
}
let characters = () => {
let dinosaurAbilities = ['run', 'jump', 'bite', 'have a tail', 'roar'];
let raccoonAbilities = ['run', 'jump', 'climb', 'steal', 'eat trash', 'find trash', 'stay up late'];
return [{
name: 'Buddy',
type: 'Dinosaur',
abilities: dinosaurAbilities
}, {
name: 'Rapscallion',
type: 'Raccoon',
abilities: raccoonAbilities
}].map(character => `
<div class="information">
<div class="character-intro">
<span><b>${character.name}</b> is a <i>${character.type}</i> who can do the following:</span>
</div>
<ul>
${character.abilities.map(ability => `
<li class="ability">${ability}</li>
`).join('')}
</ul>
</div>
`).join('')
};
// each of these would normally be instantiated in its own file
// instead of being declared here
let header = {
title: 'Can an App Be Made Using Native Javascript?',
subtitle: `Let's Find Out`,
login: {
title: 'Login Button',
handleLogin: (event) => alert('You clicked me!')
}
}
let footer = {
copyrightYear: new Date().getFullYear()
}
let content = {
copy: 'This is a simple test. Who knows what the outcome will be?'
}
let summary = (data) => {
return `
<div class="summary">Summary</div>
<div>The app title is ${data.title}</div>
<div>The app subtitle is ${data.subtitle}</div>
`;
}
// render the app
jsapp({characters, header, footer, content, summary})
That's all of it. There is no css, so viewing the demo should be pretty painful to people who appreciate appearances. However, it's just a demo for my own purposes, so I wanted to keep it as spartan as possible.
One thing I can see changing is the main function's signature. It would make more sense to call it like this jsapp('.app-test', {data}), and have the selector in the function be determined at call-time. But again, it's just a demo.
Comments:
Leave a Comment
Submit