Create your first web app: notes + counter easy and fast 📝⚡
Getting started with programming can seem overwhelming, with so many languages, frameworks, and methodologies to consider. Sometimes it might seem easier to delegate everything to an AI. Don't worry! Programming for the web can be simple, fun, and best of all, completely free! 🌐✨
What You Are Going To Build
By following this tutorial, you'll create a simple web application to store text notes and count characters and words.
The app will retain data, so it'll remember the text when you reload or reopen the page. This means you'll have a convenient way to save any text you want to reference later, such as addresses, URLs, etc. 📄🔗

It also counts characters and words, which is a good introduction to string (text) manipulation. Of course, the app is very simple—that's the point! But with this tutorial, you'll learn the basics of developing a web app, and then the world will be your oyster. 🦪💻
How Are You Going to Build It?
Although web applications are very diverse, they typically involve three main technologies: HTML, CSS, and JavaScript. It's essential that you understand their roles before you begin. 📚
- HTML: Hypertext Markup Language allows you to define the structure of your content. With it, you can mark up text based on its meaning, indicating whether it's a paragraph, a heading, or an item in a list, for example.
- CSS: Cascading Style Sheets allow you to style your content. Using this language, you can color a paragraph green, make all your links bold, or position a sidebar next to your main article. 🎨
- JavaScript: With this programming language, you can add functionality to your website. You can make buttons show or hide parts of your document when clicked, or implement a full-featured word processor or video game. 🎮
He website.dev (written by members of the Chrome team) and MDN, the Mozilla Developer Network, are two excellent sources of information on fundamental web technologies.

The Structure of the App
The app you'll build will consist of three simple files: index.html, styles.css, and script.js. 🗂️
There are no special requirements to run the app; you just need a web browser. There's no backend functionality, so you don't need a scripting language like PHP or a local web server. Once you're done, you can open the index.html file directly in your browser and everything will work out. 😊
It's important to mention that this app saves your text without writing it to a specific file on disk, since JavaScript can't access the file system. It uses the Web Storage API, a powerful alternative to cookies. 🍪💾
How to Build the Note Taker
Create the following three files in the same directory and then open index.html in a web browser. Alternatively, you can download the files from this GitHub repository. 📥
Building the Structure with HTML
The index.html file defines the structure of your app's content in HTML. It includes references to the other two files so a browser can combine them into the final product.
Like all HTML documents, the top-level structure includes a head section containing metadata and a body section containing the actual content that will appear on the page:
html>
<html>
<head>
...
head>
<body>
…
body>
html>
The metadata in the head is very brief, with just a page title (which appears in your browser's title bar) and a reference to the styles.css file:
<title>Texttitle>
<link rel="stylesheet" href="styles.css" />
The body is more complicated, but there are only a few important parts:
<div id="counter">
words: <span id="words">0span>
/ characters: <span id="chars">0span>
div>
<textarea id=«text» autofocus=«1»>textarea>
<script src=«script.js»>script>
The div element contains the app's word and character count. It will be located at the top of the web app and will update whenever the text changes.
Note the id attributes, which are vital to the operation. The value of an id must be unique so the app can use it to identify a specific element. Doing this can help us style these elements and also target them when adding functionality.
The textarea is a built-in form element that allows you to type simple text in a browser. With the autofocus attribute, it's ready to type as soon as the page loads. Finally, the page references the JavaScript file, script.js.
At this point, you can open the index.html file in a browser. It won't do anything right now, and it won't even look good, but it's helpful to test your app at each stage to make sure the basics work as you expect.

Making Them Look Good With CSS
CSS allows you to style parts of your web page by targeting those parts and specifying values for properties like font-size and background-color. This sample app doesn't need a very refined design because its interface is so basic. However, we can smooth out the rough edges with a touch of style. 🎨✨
First, style the body to define the overall design:
body {
margin: 0;
padding: 0;
display: grid;
min-height: 100vh;
grid-template-rows: min-content 1fr;
}
This CSS uses the grid layout to position the word and character counts in a narrow row above the textarea. The grid-template-rows property specifies that the count row should be as short as possible (min-content) and the textarea should fill the remaining space (1fr).
The counter row has a basic style to differentiate it from the rest of the text, mainly through its background:
#counter {
font-family: sans-serif;
background-color: #f7f7f7;
text-align: center;
width: 100%;
padding: 0.5em 0;
}
Finally, the text area is styled to display legible text with plenty of room to breathe. The line separating the counts from the text is actually the top border of this text area.
textarea {
width: 100%;
height: 100%;
font-size: 16pt;
padding: 1in;
box-sizing: border-box;
outline: 0;
border: none;
border-top: 1px solid #999;
}

Adding Functionality with JavaScript
With the structure and style in place, you're ready to add functionality: the code that makes this simple app actually do something. 🛠️
Before you start writing even the simplest code, it's a good idea to understand what you want it to do. In this case, there are just a couple of general requirements:
- Save the entered text and reload it when necessary.
- Update character and word counts to provide real-time statistics. 📊
Both requirements involve updating elements in the document. So the script begins by obtaining references to these nodes using the getElementById method:
const words = document.getElementById("words");
const chars = document.getElementById("chars");
const textarea = document.getElementById("text");
Note that words, chars, and text are the IDs of their respective elements in the document, assigned in the HTML file with the id attribute.
Next, we need the app to react when the text is changed:
textarea.addEventListener("input", function(ev) { localStorage.setItem("text", ev.target.value); update_counts(ev.target.value); });
An event listener is a function that JavaScript will automatically execute when a certain event occurs. In this case, we attach a listener to the textarea node, which executes when the "input" event occurs. That is, when someone interacts with the textarea.
Next, we use the Web Storage API to save the current contents of the textarea, which comes from ev.target.value. In this structure, ev is an object representing the event that triggered the listener, ev.target is the node that received the event, and ev.target.value is the text of that textarea.
The setItem method takes two parameters: a key name to associate with the data and the actual data to store.
Next, we call update_counts(), passing the same text. We'll explain that function in a moment.
Now that we're saving the text, you'll want to load it again when the app opens or reloads. We can take care of that using another listener:
window.addEventListener("load", function(ev) {
var text = localStorage.getItem("text"); textarea.value = text; update_counts(text); });
This listener handles the load event that occurs on the window object, a top-level representation of the browser window. When the window loads, a call to getItem() returns the stored text value from local storage. This code then sets the value of the textarea to the loaded text and, like the previous listener, calls update_counts() with this text.
The remaining work is done in the update_counts() function which looks like this:
function update_counts(text) { chars.innerHTML = text.length; words.innerHTML = text.split(' ').length; }
Unlike a textarea, regular text doesn't have a value property, so we set the innerHTML property instead. The length property of a string is a simple read-only property that gives us the number of characters.
text.split(' ') splits the string at the space characters, returning the result as an array of strings. Like strings, arrays have a length property that gives the number of elements in an array.
If you test this code, however, you might notice a couple of errors:
- It will not count a word by itself on a line correctly, as there are no space characters around it.
- With no text at all, it will report a single word because of how split works.
To solve the first problem, you can split on a regular expression (\s means "a blank character") instead of a space. To solve the second, filter out empty strings from the result:
words.innerHTML = text.split(/\s/).filter(function(n) { return n != ''; }).length;
If you don't fully understand that line, check out the MDN pages for split and filter.

You can build many useful web applications with just a few files. You can also extend this simple notes app to add more functionality. For example, you can use the window.location property to retrieve the current URL and support multiple notes by varying the name you pass to localStorage.setItem. 🗒️🌍