Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Misael Braga de Bitencourt
Misael Braga de Bitencourt

Posted on

Javascript Made Simple!

thumb

Not every frontend projects needs to use a set of complex tools to build or run. Almost all of the new Javascript projects contains a Framework with this tools, transpilers, transformers, minifiers, css libs and dependencies for every feature. But you dont necessarily need to follow this way to build your application, specially if your project is a small one.

This further article shows some approaches to create a application without that bunch of complex tools. A P.O.C. was created in order to demostrate it. It is about one simple crud created with modern Javascript with no dependencies.

It was written using the functional paradigm because Javascript is more Haskell than Java!

About the css, no one lib or framework was used, just a css reset followed by the app style.

Creating a simple CRUD

In order to sample how to use the language in a simple way, using the "Vanilla Javascript", a Create Retrieve Update Delete user interface have been created and the following text explains a several ways to realise it.

sample

An exemple of a simple CRUD APP in raw Javascript could be found in this repository:

https://github.com/misabitencourt/javascript-made-simple

Importing the main.js script

Using a HTML5 document, it is possible to add a "script" tag with "module" as "type" property. This tag will import aES Module on your web application that could also import another modules.

index.html

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Javascript made simple!</title><linkrel="stylesheet"href="css/reset.css"><linkrel="stylesheet"href="css/todolist.css"></head><body><divclass="app"></div><scripttype="module"src="js/main.js"></script></body></html>
Enter fullscreen modeExit fullscreen mode

js/main.js

importactionsfrom'./actions/index.js';importtodoAppComponentfrom'./components/app.js';window.todoApp=function(renderInEl){/// ... more code
Enter fullscreen modeExit fullscreen mode

Note: Thediv with theapp class is out target element to render the entire application.

The View - Manipulation Elements with just Javascript

The Javascript runtime available on the current major browsers is powerful. One of its power is theDOM element manipulation. Despite that, the "raw javascript" element API could be simplifyed by our home made functions. For example, if you must create a title for one application section using the built in api, it would be like:

consttitle=document.createElement('h1');// Creates elementtitle.textContent='User list';// Sets the text content of itparentElement.appendChild(title);// Appends the title element as a parentElement child (it must be another element, like document.body or another)
Enter fullscreen modeExit fullscreen mode

At the first look, it sounds nice and easy, but if you would have a little bit more complex element tree, it would be a pain:

// Creates the four elementsconsttitle=document.createElement('h1');consttitleIcon=document.createElement('img');consttitlePrimaryText=document.createElement('span');consttitleSecondaryText=document.createElement('small');// Adds CSS classes to themtitle.classList.add('app-main-title');titleIcon.classList.add('app-main-title-icon');titlePrimaryText.classList.add('app-main-title-primary-text');titleSecondaryText.classList.add('app-main-title-secondary-text');// Adds text Content and src to themtitlePrimaryText.textContent='User list';titleSecondaryText.textContent='Listing X users from Y';titleIcon.src='assets/img/users-icon.svg';// Appends the title childrentitle.appendChild(titleIcon);title.appendChild(titlePrimaryText);title.appendChild(titleSecondaryText);// Appends the title with children to one parent elementparentElement.appendChild(title);
Enter fullscreen modeExit fullscreen mode

In order to create a DOM tree in a most efficient way, i reccommend the approach used by some microframeworks like:Mithril,Hyperapp orReact without JSX.

It could solved by create a function with receives, at least, these parameters:

  • String: the tag name
  • String: css class list
  • Element[]: the list of children elements or the text content

The implementation of that is like that:

exportdefaultfunctionel({tag,textContent,classList,children,events,attributes}){constelement=document.createElement(tag||'div');if(textContent){element.textContent=textContent;}if(classList){(classList+'').split('').filter(clazz=>clazz.trim()).forEach(clazz=>element.classList.add(clazz));}if(children){children.forEach(child=>element.appendChild(child));}if(events){events.forEach(event=>{element.addEventListener(event.event,event.listener);});}if(attributes){attributes.forEach(attribute=>{element.setAttribute(attribute.name,attribute.value);});}returnelement;}
Enter fullscreen modeExit fullscreen mode

This function is used in this way:

// Creates the element tree mentioned above using our helperconsttitle=el({tag:'h1',classList:'app-main-title',children:[el({tag:'img',classList:'app-main-title-icon',attributes:{src:'assets/img/users-icon.svg'}}),el({tag:'span',classList:'app-main-title-primary-text',// OR a string constant from another moduletextContent:'User list'// or getTranslatedText() from another module}),el({tag:'span',classList:'app-main-title-secondary-text',// OR a string constant from another moduletextContent:'Listing X users from Y'// or getListCount() from another module})]});// Appending the created element tree to another elementparentElement.appendChild(title);
Enter fullscreen modeExit fullscreen mode

We could got a lot of advantages using this kind of functional module to create views. Imagine if you must code a user list of that screeen, you would have got a shortcut in your hands. Take a look:

// Supposing it is in an async functionconstusers=awaituserService.list();constuserList=el({tag:'ul',classList:'app-user-list',children:users.map(user=>(// Transforms user json to user list item elementel({tag:'li',classList:'app-user-list-item',children:[// User name on listel({tag:'span',textContent:user.name}),// ... icons and stuff]})))});
Enter fullscreen modeExit fullscreen mode

If you choose to use it, you would probably have to create a application state manager. It doesnt needs to be something complicated likeRedux.

I suggest to use an object json witch represents the application state and can't be changed by component functions, just read by them. Once the component needs to trigger some event, it could do it using some callback function in order to sepparate the render function and the state change logic. That state change functions could be tested on unity.

The code above is about the root render of a component tree:

letstate={};letrenderedElement;/** * App state changed! action trigger! * @param {*} actionName Id of the action triggered * @param {*} params action parameters */functionactionTrigger(actionName,params){constaction=actions.find(action=>action.name===actionName);if(!action){console.warn(`Action${actionName} not found`);returnstate;}state=action.exec(state,params);}/** * Single app render, on action trigger or app start */functionrender(){console.log('render called',state);constcurrentRender=todoAppComponent(state,(action,params)=>{actionTrigger(action,params);render();});if(renderedElement){renderedElement.parentElement.removeChild(renderedElement);}renderedElement=currentRender;renderInEl.appendChild(renderedElement);}// First renderrender();
Enter fullscreen modeExit fullscreen mode

A simple component would be like:

exportdefault(state,action)=>el({classList:'todo-list-app-todo-list-crud-list',children:(()=>{if(!(state.todolist&&state.todolist.length)){return[el({tag:'p',classList:'alert-default',textContent:'No items have been created yet.'}),button({text:'Reload',onClick:()=>action(TODO_LIST_RELOAD_ACTION,null)})];}returnstate.todolist.map(todo=>el('div','todo-list-app-todo-list-crud-list-item',{textContent:todo.text}));})()});
Enter fullscreen modeExit fullscreen mode

This suggestion is a reactive functional approach. Some procedural or object oriented one also could be done with raw javascript if you are not confortable with functional programming.

Using modules - decoupling code with javascript modules

ES2016 Javascript modules naturally acts like aSingleton object. The script inside a module have it own scope. Unlike the classic javascript code imported with "script src=", when you declare a variable in some module, that variable does not populate the global scope. The programmer can control what variables can be accessible from other modules using the "export" sintax. For example:

some-module.js

// This variable is only accessible from THIS moduleconstpetSizes={small:1,medium:2,large:3};functioncheckPetSize(size){switch(size){case(petSizes.small):return'small';case(petSizes.medium):return'medium';case(petSizes.large):return'LARGE';default:return'unknown';}}// This function is available for another modulesexportcheckPetSize;
Enter fullscreen modeExit fullscreen mode

By using javascript modules to your advantage, you can naturally create singleton services or someobject factories. Using the javascript "ducktype" to your advantage, the dependency injection also could be done easily.

Fetch HTTP Client

The majority of modern apps must send and retrieve data from backend webservices. It is usually being done with HTTP protocol. Nowadays, there is no need of any lib likeJQuery.ajax oraxios to perform that kind of request.

The modern browsers supportsFetch API by default. This api uses javascript Promises and it is easy to use. See the following sample:

asyncfunctiongetConfigJson(){constresponse=awaitfetch(`https://my-api.com/v1/config/`);if(response.code!==200){thrownewError(`Error no fetch config`);}constconfigJson=awaitresponse.json();returnconfigJson;}
Enter fullscreen modeExit fullscreen mode

I recommend the wrapping of the fetch api in specific module to perform a http fetch. By this way, some request configurations like the API path and the authorization request headers would be easily managed.

Native Local Databases

The HTML, CSS and JS trio is used not only to create web apps but mobile and desktop too (seeElectron andTauri). All of the software on this platforms needs a local database in a higher or lower level. All online websites sometimes needs to store some cache and configuration in a local machine.

In all of this platforms, the JS code could be exactly the same on doing database operations. We have two secure ways to do it:Localstorage andIndexedDb. This first option is the faster way to save and retrieve data. Check this out:

localStorage.setItem('data-key','data-value');
Enter fullscreen modeExit fullscreen mode

This API have been available in all major browsers for years but Localstorage have some issues. It is not available in all javascript backend runtimes and it stores only strings. Thease strings are normally one stringified JSON stored like:

// savelocalStorage.setItem('app-config',JSON.stringify(config));// loadconstconfig=JSON.parse(localStorage.getItem('app-config'));
Enter fullscreen modeExit fullscreen mode

The LocalStorage also have data size limitations and it is different between browsers and runtimes. More than 1 Megabyte datastorage is dangerous to using. For large amount of data, is recommended IndexedDb instead.

Code checking and tests

With all of that APIs you have seen before, you are able to create and entire application with Javascript without no extra transformer, library or framework. You may consider it optional for smaller projects but some code audition tools are recommended. The standart tools for syntax and style checking are:Prettier andESLint.

End to end tests (also known as e2e or integration test) are almost essential for frontend apps. To perform it,Playwright is a good fit for. The original raw javascript code whould not changed or transformed to create it. This lib works just as a bot navigatinig, clicking, typing and checking the app in a browser of your preference.

A good option

As you have seen, it is absolutely possible to create a Javascript application without a bunch of complex polyfills, JSX, transpilers, libs and etc. The first detail you plan about you project do not needs to be a framework choose, why not give a change to the language default resources?

If it is about a huge and complex project with dozens of coders from different ecosystems working on it, the avoiding dependencies could be on your way. But if you have some mid-sized project or an ordinary one, you may claim yourself for simplicity.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Florianópolis, Brazil
  • Joined

More fromMisael Braga de Bitencourt

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp