Article parts
To tackle the topic of web application interface (GUI) rendering and to compare available approaches, the article is split into following parts:
- HTML and HTML partials rendering[Part 1],
- Javascript rendering[This part],
- Hotwire [Part 3],
- Conceptual differences of all approaches and how to choose the most applicable one [Part 4].
GitHub Repository
There are two solutions presented in this article and complete implementation of solutions shown in this article can be cloned from GitHub repositories:
https://github.com/emir-gradient/cursor-tracking-simple-javascript-example
https://github.com/emir-gradient/greeting-app-react
Concept of Javascript Rendering
Javascript is a scripting or programming language that can be executed in the web browser (as the part of single HTML page) and can add dynamic behavior to the web page. Looking into basic operations, it allows us to create, modify, remove and animate HTML elements as the result of some event happening ("mouse click" as user interaction is one example).
Being able to manipulate all HTML elements on the web page allows as to performin Javascript rendering. So, instead of server building interface on every user's request, that work is delegated to the browser side. More precisely, to the Javascript side.
Relevant term, when it comes to Javascript rendering, is "Document Object Model" (DOM). DOM is the data representation of the objects that comprise the structure and content of a HTML document on the web. Javascript has access to DOM, to manipulate it, and as the result of that manipulation, presented web interface changes (browsers react to DOM manipulations by rendering applied DOM manipulations).
The following example describes Javascript usage to make interactive web page. It includes section for rendering elements to the HTML body, and to add interactivity to it. Note how HTML body in this example consists of the <script> tag only.
This example shows current coordinates of the cursor on the webpage. Also, it updates those coordinates in the real time whenever cursors moves.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple JS User Interaction Example</title>
</head>
<body>
<script src="js/simple-js-user-interaction-example.js"></script>
</body>
</html>
js/simple-js-user-interaction-example.js
// INITIAL INTERFACE CREATION PART
// Invent new div HTML element
const titleDiv = document.createElement('div');
// Configure its content.
titleDiv.innerHTML = '<h1>Cursor position on the page:</h1>';
// Append it to the bottom of HTML body element
document.body.append(titleDiv);
const cursorPositionDiv = document.createElement('div');
cursorPositionDiv.innerText = 'x: 0.00, y: 0.00';
document.body.append(cursorPositionDiv);
// INTERACTIVE PART
// Function to be executed on every mouse move over the web page window.
window.onmousemove = function (event) {
// Fill in `cursorPositionDiv` with content of the current mouse coordinates.
cursorPositionDiv.innerText = `x: ${event.clientX}, y: ${event.clientY}`;
};
SPA Frameworks and Four Main Concerns
Looking at previously presented relatively simple example, one can imagine how complete web application that solves relatively large set of business problems might become hardly maintainable and unstable. To point couple of reasons out:
- Absence of consistency: In one section innerHTML of element is modified, in another one it is innerText.
- Absence of structure: There are simply two sections (marked by comments), one for Initial Rendering, and another one for Interactivity Updates.
- Absence of cross-browser support: Some of the operations might not be supported in all browsers.
- Absence of performance optimization: There is simple function that replaces innerText of cursorPositionDiv on every 'mousemove' event being emitted. Meaning that browser will refresh given `div` content whenever it is possible for it to do so. This rendering operation is completely independent of any other rendering operations that might be needed in larger application.
Still, since previous example does single initial rendering of the content, and single interactivity operation, one can safely argue that implementation is completely fine and every additional improvement on aforementioned points is an overkill.
Now, if we look at any more complex example, we see that there is a need to first design some structured, consistent, cross-browser supported and highly performant Javascript library. When that structure is enforced at some level higher than language-level, we call that library Framework, and when that framework strives to solve these four main concerns for Javascript rendering (on multiple business complexity levels), we call it "SPA Framework".
Popular SPA Frameworks in the moment of writing this article are Angular, React, Vue and Ember.
Choice of SPA Framework for "Greeting App"
To be able to compare different web rendering approaches (in [Part 4] of this article) intent is to develop the same "Greeting App" example application using them. Specification (and functionality scope) can be read in HTML and HTML partials rendering [Part 1],
To choose between these SPA frameworks, in general, we should consider multiple factors relevant for the business case we are trying to solve. When it comes to the "Greeting App" and its scope, every of these frameworks is sufficient, so without particular reason, in this article React.js is utilized.
Starting React Application Development
To develop React web application in this article, "Create React App" is utilized. It provides us with solid starting point, to be able to work on our React application.
To create initial application, execute the following command:
npx create-react-app greeting-app
Result of this command is that initial React template will be downloaded and configured in "greeting-app" directory. Inside that directory is root of the project. To run your application in development environment, you need to execute configured 'start' script:
npm start
As the result, development server starts, it opens the browser and loads our React application.
Rendering using React
Entry point for every SPA application is initial HTML file with one empty div. That div, after loading SPA framework, is filled by SPA framework rules (in this case React). Body of initial HTML that we get as result of 'create-react-app' looks as follows:
public/index.html
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
As we can see, there is single <noscript> tag and single empty div having "root" id. <noscript> tag is HTML tag whose content is ignored by all browsers that support Javascript. In case when web browser does not support it, it actually renders content inside the <noscript> tag (effectively ignoring <noscript> tag).
Root div is the place in HTML where we expect React to render implemented user interface.
Following source files (with explanation of details relevant for this topic) are an entry point of our React application:
src/index.js
// Imports React modules
import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
// Imports index.css for our SPA.
import './index.css';
// Imports our App component
import App from './App';
// Creating Root React Component / Node.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// Strict Mode component provides us with React warnings in case of potential problems, such as
// possible memory leaks. It performs this only when is run in Development environment.
<React.StrictMode>
<App />
</React.StrictMode>
);
// Measure performance in Development environment.
reportWebVitals();
Looking at this file, we see instruction to fetch "root div" utilizing document.getElementById() method and that div is provided as input to ReactDOM.createRoot() method. From this point, React handles rendering "under the hood", meaning that in the rest of our source code we will not manipulate DOM directly (utilizing document object), but delegate actual DOM manipulations to React modules.
Root.render() method as an input takes two components: React.StrictMode and App. App is provided as the child of React.StrictMode component.
This HTML-like syntax is actually JSX. JSX is extension of Javascript syntax that looks similar to HTML (or HTML template language). It has full power of Javascript and practically every section written in JSX is actually preprocessed and executed as plain Javascript in the background. Instruction <App /> in HTML means nothing (it is invalid HTML tag), but in this case, it means "Render App Component" to this place.
In the src/App.js one can see App Component and JSX utilization to define its content.
src/App.js
// Importing svg React logo and App.css content
import logo from './logo.svg';
import './App.css';
// App component
function App() {
// JSX to render on the interface
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
In the implementation of App component, we can see interface that is actually rendered (React logo with link to "Learn React"). Term "className" utilized in JSX has the meaning of "class" in regular HTML. Since "class" is keyword in Javascript, that ambiguity had to be solved by defining another term for HTML class attribute.
Style of the page is defined in src/App.css.
src/App.css
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.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;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Rendering part of our application is covered through React components. Hierarchy of them is defined utilizing JSX, while actual rendering to the DOM is done by React.
Greeting App Home Page
Now, its time to change template React app provided to us to get "Greeting App" that we created in [Part 1] of this article using Express and HTML rendering concepts. To follow the requirement that we are trying to achieve, please check [Part 1] of this article.
First steps to perform are:
- Remove 'src/index.css' file,
- Remove line "import './index.css';" from src/index.js file,
- Create Home Component with "Example Project Body" heading.
- Render Home Component in App Component (instead of current React example).
After these changes, our source code looks as follows:
src/index.js
// Imports React modules
import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
// Imports our App component
import App from './App';
// Creating Root React Component / Node.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// Strict Mode component provides us with React warnings in case of potential problems, such as
// possible memory leaks.
<React.StrictMode>
<App />
</React.StrictMode>
);
// Measure performance in Development environment.
reportWebVitals();
src/App.js
import './App.css';
import {Home} from "./greet/Home";
function App() {
return (
<Home />
);
}
export default App;
src/App.css
body {
padding: 50px;
background: linear-gradient(to bottom right, #009ea7, #009ed7) fixed;
}
h1 {
color: yellow;
text-shadow: pink 2px 1px;
}
a {
color: white;
font-weight: bold;
}
form {
height: 50px;
}
label {
color: yellow;
}
input {
border-radius: 10px;
height: 25px;
}
button {
height: 30px;
border-radius: 10px;
}
.nav-separator::before {
content: "|";
margin-left: 5px;
margin-right: 5px;
}
src/greet/Home.js
export function Home() {
return (
<>
<h1>Example Project Body</h1>
</>
);
}
And the resulting home page:
Navigation
When we are in SPA scope, navigation between "pages" is done by SPA Framework and is done in browser. In comparison with server HTML rendering, this scenario does not involve basic HTML behavior for links and forms. As the reminder, basic HTML behavior is that on user's click to link, http request with new URL is sent to server and then based on server's response, interface is updated.
In React, it is preferred to use react-router-dom module. This module provides us with necessary implementation to incorporate routing in our React application.
So, first step is to install it as dependency to our application by executing the following command:
npm install --save react-router-dom
Since we expect router to choose which of our components are loaded (based on the URL), it makes sense that first rendered component in our App is React Router component to manage this choice. Naturally, we need to configure mapping between routes and actual components.
For this purpose, Default Greet component is created, and router configured to properly map individual routes to individual components (ones that we decided should act like pages).
src/greet/Greet.js
export function Greet() {
return (
<h1>Hello!</h1>
);
}
src/App.js
import './App.css';
import {Home} from "./greet/Home";
import {createBrowserRouter, RouterProvider} from "react-router-dom";
import {Greet} from "./greet/Greet";
const router = createBrowserRouter(
[
{
path: '/',
element: <Home />,
},
{
path: '/greet',
element: <Greet />,
}
]
);
function App() {
return (
<RouterProvider router={router} />
);
}
export default App;
After these changes, when we access http://localhost:3000/ and http://localhost:3000/greet components Home and Greet are rendered.
To render navigation bar under main area where we present greeting message, NavBar component is created. Inside of that component, for links, we utilize Link component provided by React Router module. When it is ready, we use that component on every of our pages.
src/greet/NavBar.js
import {Link} from "react-router-dom";
export function NavBar() {
return (
<>
<Link to={'/'}>Home</Link>
<span className="nav-separator" />
<Link to={'/greet'}>Default Greet</Link>
<span className="nav-separator" />
</>
);
}
src/greet/Home.js
import {NavBar} from "./NavBar";
export function Home() {
return (
<>
<h1>Example Project Body</h1>
<NavBar />
</>
);
}
src/greet/Greet.js
import {NavBar} from "./NavBar";
export function Greet() {
return (
<>
<h1>Hello!</h1>
<NavBar />
</>
);
}
These changes are resulting in rendered and functional navigation bar in the "Greet Application".
Accepting the Form Input
In Part 1, when form was submitted, request to the server was sent. Intention was that server builds new HTML to be presented in browser (with greeting message). In "Greeting App" case, we are still building the Form, and still submitting the name as the input. Difference is that given input will be processed in our application and not on the server.
To utilize this, we are improving Greet component to be able to accept name input from query parameters in URL. Additionally, we are creating AddName component, to define interface where our user will actually insert and submit its name.
src/greet/Greet.js
import {NavBar} from "./NavBar";
import {useLocation} from "react-router-dom";
export function Greet() {
const location = useLocation();
let name = new URLSearchParams(location.search).get('name');
let message = 'Hello!';
if (name) {
message = `Hello ${name}!`;
}
return (
<>
<h1>{message}</h1>
<NavBar />
</>
);
}
src/greet/AddName.js
import {Form} from "react-router-dom";
export function AddName() {
return (
<>
<h1>Add your name:</h1>
<Form method="get" action="/greet">
<label htmlFor="name">Name:</label>
<input id="name" type="text" name="name" />
<button type="submit">Submit</button>
</Form>
</>
);
}
Having AddName and Greet components ready to process user's input, only needed adaptations are in NavBar component and router configuration (defined in App.js).
src/App.js
import './App.css';
import {Home} from "./greet/Home";
import {createBrowserRouter, RouterProvider} from "react-router-dom";
import {Greet} from "./greet/Greet";
import {AddName} from "./greet/AddName";
const router = createBrowserRouter(
[
{
path: '/',
element: <Home/>,
},
{
path: '/greet',
element: <Greet/>,
},
{
path: '/add-name',
element: <AddName/>
}
]
);
function App() {
return (
<RouterProvider router={router}/>
);
}
export default App;
src/greet/NavBar.js
import {Link} from "react-router-dom";
export function NavBar() {
return (
<>
<Link to={'/'}>Home</Link><span className="nav-separator" />
<Link to={'/greet'}>Default Greet</Link><span className="nav-separator" />
<Link to={'/add-name'}>Add Name</Link><span className="nav-separator" />
</>
);
}
With this step, Greeting App is completely incorporated in React.
Get an Offer
Contact Us or Schedule a Meeting with us to get an offer for our development & consulting services regarding your current or next Web project.
Dominga
https://comedy.gomuviz.com It's going to be end of mine day, but before finish I am reading this great post to increase my knowledge.
Jere
https://watchnow.gomuviz.com/ After exploring a few of the blog articles on your website, I seriously appreciate your technique of writing a blog. I book-marked it to my bookmark site list and will be checking back soon. Take a look at my website too and let me know how you feel.
Edwardo
https://magicboxpro.flowcartz.com/ Thank you a lot for sharing this with all folks you really know what you are talking about! Bookmarked. Please also talk over with my website =). We may have a link alternate contract between us
Shiela
https://unitynews.ainewsglitch.techmarketers.xyz/ I think that what you posted made a ton of sense. However, what about this? suppose you typed a catchier post title? I am not saying your information isn't solid., but what if you added a title to possibly grab folk's attention? I mean Gradient Blog - [Part 2] Hotwire - between the HTML (server side) and the Javascript rendering. What to choose? is a little boring. You should peek at Yahoo's home page and note how they create news headlines to get people interested. You might try adding a video or a related pic or two to grab readers interested about everything've written. Just my opinion, it might make your posts a little livelier.
Nikephype
dark web access dark web site darknet links
Melody
https://alientechnologyunveiled.blogspot.com Hello, I read your blog like every week. Your humoristic style is witty, keep it up!