skull

Robert Tamayo

R
B
blog
skull

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