Recognize this thing?
const aFunction = () => {};
It's a function.
Functions can do all sorts of things depending on the parameters you pass, the code you write in the body, and what they return. But no matter how you use them, a function is still just a function. This folder won't help you learn anything new about how functions work, instead you'll learn about how to use them in a program.
There is no magical "best way" to use functions. Here is just 3 common roles functions can play in simple JS programs. What you learn in this folder is enough to (mostly) make sense of what you find in the world of JS tutorials. In later modules you will learn about different function roles, but these 3 are enough for now. Let's get to it.
Other developers should be able to know WHAT your code does without needing to now HOW it works. Functions are great for this. Writing short single-purpose functions with good names has a few benefits:
- Your main program is easier to read: a single function call is simpler to read and understand than many lines of code.
- You can re-use your function many times in many places, each time with different arguments. This helps to avoid repeated code.
- With functions you can test your code using many different inputs to be sure it does what you expect. Some functions are harder to test than others, for now you only need to test your utils.
And finally, the function roles you will learn in this module:
- Listeners
- Handlers
- Utils
- Components
- Custom Events
As you study your way around the internet it's important to keep in mind that these 4 function roles are not standard everywhere. Different resources may use different terms, or may not even talk about function roles at all. These roles are just a helpful way for you to organize your thoughts as you read and write your first programs.
Not really a function, but this one is important. The /init
files in your applications will be the only file directly by your .html
files. Init files are responsible for only one thing:
- Calling Event Listener functions with the correct DOM id's.
JavaScript programs are event-driven. This is a fancy way to say that websites sit waiting for an event to occur, then they do something, then they go back to waiting. For example:
- ... a user clicks a button then the background changes color ...
- .. a user hovers over a word then an image is shown ...
- ... a user types their search then suggestions are displayed under the search bar ...
If these examples makes you think of a user story, then you're thinking in the right! Event listeners are functions that define what happens when a user makes a specific action. They do this by attaching an event listener to the DOM that calls a Handler function each time that event occurs.
There are a few advantages to defining your event listeners in a separate function instead of attaching them directly in the /init
files:
- You can pass DOM id's as an argument, abstracting away the details of your UI. Now if you change your HTML id's you only need to change the
/init
files. - You can reuse the listener function multiple times if you need to.
- You can split your projects into smaller, more specific tasks making it easier to collaborate.
You can tell if it matches these criteria:
- it can take in JS data as arguments
- it can read from the DOM to get the elements that need listeners
- it does not return anything
- it does not interact with the user directly or modify the DOM
- it can
import
from:- utils
- handlers
- it can
export
to:/init
files
Event handlers are the entry point for user interactions. "Entry Point" is just a fancy way to say "the first code that is executed". So when a user interacts with your web page a few things happen:
- A new event is created. Events are a special kind of object in JavaScript that have lots of useful information like where the event occurred on the screen, what type of interaction occurred, or what input a user provided. The browser will do this behind the scenes.
- Event Listeners respond to this new event by calling your handler functions, passing the event object as an argument.
- Your handler function is executed and the users story is completed.
Event handlers are the functions responsible for reading user input from events and using other functions to make a user story happen. Here is a little checklist to help you know if a function is being used as an event handler:
- it is passed as a callback to
_.addEventListener('interaction', handler)
- it does interact with the user by reading their input and updating the UI
- it takes either no parameters, or only one
event
parameter - it can return
true
orfalse
, but that's a technical thing for the browser - it can
import
from:- utils
- business-logic
- components
- handlers
- it can
export
to:- listeners
These are pure functions, the kind of function you studied in Behavior, Strategy, Implementation. They take in JS data, transform it, and return a new value. Unlike handlers or procedures which can interact with the user interface, util functions are easy to test and document because their behavior is predictable - the same arguments will always give the same return value.
Writing programs that use util functions will make your code more readable, and unit testing your util gives confidence that you program works as it should.
Util functions are responsible for the many complicated things you may need to do with a user's data, for example finding their favorite movies from an array or making sure an email is valid . Here's a checklist you can use to see if a function is playing the role of util in your program:
- it must take in JS data as arguments
- it must return new JS data
- it has unit tests -
expect(actual).toEqual(expected)
- it does not interact with the user in any way (DOM,
prompt
,alert
,confirm
) - it can
import
from:- utils
- it can
export
to:- anywhere
Components are functions that return a component of the user interface. The term "component" is very common in web development and depending on which language or framework you are using, it's exact definition can change. For now a component is just a function that returns a DOM element.
Remember component-based design from UX/UI? This is it! In this module you will begin learning how to write functions that return the UI components planned by the designer. When you create a wireframe, you will need to think about how it can be broken into components. Once you have a plan for the components in your page you can begin developing and testing each component separately before integrating them into a full page.
Here is a little checklist to help you find out if a function is being used as a component:
- it returns a DOM element
- if it has parameters, they are plain JS data
- it may have unit tests or a
test.html
file - it does not modify the DOM, or read from any element other than itself
- it can
import
from:- utils
- components
- custom-events
- it can
export
to:- handlers
- components
Even components can have different roles! You'll learn more about this in the following module (Single Page Apps) when your entire UI is rendered using components. No more HTML :) Here's a little preview:
One huge benefit of developing your application with components is that you can test each part of the UI in isolation before integrating them into your website. For now you will learn to test your components in two different ways:
- Structure: Does your rendered component have the correct DOM structure? The right number of children, the right
.className
, the right.tagName
, ... all with unit tests exactly like you learned for util functions. You will use a file calledcomponent-name.spec.js
to write your component unit tests. - Behavior: Does your component do the right thing when rendered into a web page? Does it react to the user in the correct way? Does it look right when it's rendered with it's styles into a web page? You will test your component's behavior by rendering it into an HTML document with different arguments and using/inspecting it to make sure it works correctly. You will do this in a file called
component-name.test.html
.
Not really a separate role, custom events are used in your components . Custom events are a very useful way to share data across different parts of your user interface. Custom Events behave just like native events (click
, mouseover
, ...) but can be customized to have a different .type
and to carry any data you want in the .detail
property.
You need to document a custom event in the component that fires it:
/**
* Returns a new input element where a user can type their greeting.
*
* @param {string} [initialGreeting=''] - The greeting to set as initial value for the input.
* @returns {HTMLInputElement} - The input where the user will write their greeting.
* @fires CustomElement#greet - The detail contains a 'greeting' property with a string value.
*/
export const greetingInputComponent = (initialGreeting = '') => {
const inputEl = document.createElement('input');
inputEl.value = initialGreeting;
// intercept the event each time the user changes the input's value
inputEl.addEventListener('change', (event) => {
// prevent the native 'change' event from bubbling up
event.stopPropagation();
// create a new custom event containing the new greeting
const greetingEvent = new CustomEvent('greet', {
bubbles: true,
detail: { greeting: inputEl.value },
});
// dispatch the event so it bubbles up through the DOM
inputEl.dispatchEvent(greetingEvent);
});
return inputEl;
};
Then you can listen for this event like you would a native event:
document.getElementById('input-root').addEventListener('greet', (event) => {
// read the user data from the custom event's .detail property
const greeting = event.detail.greeting;
// do something with the user's data
console.log(greeting);
});
None of the examples or exercises in this repo use procedures (except
/hack-these
) and you shouldn't either. It is better to use functions that have a more clearly-defined role.
Any other other function is a procedure (for now).
"Procedure" is actually a general term in programming that refers to any function that does anything. So technically handlers, utils, initialization are procedures too! It's sort of like cloth and clothes. Using the same fabric you can make a shirt, or pants, or a backpack, or many other things. Functions are the fabric, roles are the different ways you can use it.
For now you are only learning four specific roles (listeners, handlers, utils and components), we'll call anything else a procedure and give it a helpful name.
Procedures can be responsible for many things in a program. Many of the procedures you find in tutorials will read from or write to the user interface. Here's a short checklist to see if a function is being used as a procedure in your program:
- it is a function
- it is not a listener
- it is not a handler
- it is not util
- it is not a component
The more experience you have with designing programs the more roles you will learn. For example in the Architecture module you will learn a few more function roles: Custom Events, Business Logic and Data Access.