Phase 3 β€” JavaScript
Lesson 0010 β€’ Beginner β†’ Intermediate β€’ Deep Dive Edition

Functions & Events

In Lesson 9, you stored data in variables and injected HTML into the page. But your page still couldn't react to what the user does. This lesson gives JavaScript its superpower: functions (reusable blocks of code) and events (responding to clicks, key presses, hovers, and more). After this lesson, your pages will truly come alive.

This is the deep dive edition. Every concept is broken down step-by-step with real-world analogies, line-by-line explanations, visual execution walkthroughs, interview questions, and build challenges. (javascript.info: Functions Β· MDN: Introduction to events)

1. Learning Objective

By the end of this lesson you will:

Why This Matters

Every button click, every form submission, every dropdown menu, every search bar on every website you've ever used β€” they all work because of functions and events. In React, every component is literally a function. In Next.js, every page is a function. In Node.js/Express, every API route handler is a function. This lesson is the single most important JavaScript lesson in your entire journey.

2. Prerequisites β€” Quick Revision

Before we start, let's make sure the Lesson 9 foundations are solid:

ConceptWhat You Should Know
let / constconst for values that don't change, let for values that do
Data Typesstring, number, boolean, null, undefined
Template LiteralsBacktick strings with ${variable} interpolation
console.log()Prints values to the DevTools Console for debugging
document.getElementById()Finds an HTML element by its id attribute
.innerHTMLSets or gets the HTML content inside an element

If any of these feel fuzzy, review the JavaScript Foundations reference before continuing.

3. What Is a Function? β€” The Mental Model

3.1 The Real-Life Analogy

Think of a function like a recipe card in a kitchen:

You write the recipe once. But you can use it as many times as you want β€” every morning, for every guest. That's exactly what a function does: you write the code once, and call it whenever you need it.

Anatomy of a Function makeChai name (sugar, milk) parameters (inputs) { ... code } body (instructions) return output const myChai = makeChai(2, "full cream"); calling the function β€” passing arguments, capturing the return value

Figure 1 β€” A function has a name, takes inputs (parameters), runs instructions (body), and gives back an output (return value).

4. Creating Functions β€” Three Ways

4.1 Function Declaration (The Classic Way)

This is the most common and beginner-friendly way to create a function:

script.js β€” function declaration
function greet(name) {
  const message = `Hello, ${name}! Welcome.`;
  return message;
}

// Calling the function
const result = greet("Altamash");
console.log(result); // "Hello, Altamash! Welcome."
function greet(name) { function is the keyword that tells JavaScript "I'm creating a recipe." greet is the name of the recipe. name is a parameter β€” a placeholder for the actual value you'll pass in later. The { opens the body.
const message = `Hello, ${name}!`; Inside the function, we create a local variable using a template literal. The ${name} gets replaced with whatever value was passed in.
return message; return sends the value back to whoever called the function. Without return, the function gives back undefined.
const result = greet("Altamash"); This is the function call. "Altamash" is the argument β€” the actual value that replaces the name parameter. The returned value gets stored in result.
πŸ“ Parameter vs. Argument β€” What's the Difference?
β€’ Parameter = the variable name in the function definition: function greet(name)
β€’ Argument = the actual value you pass when calling: greet("Altamash")
Think of it this way: the parameter is the parking spot, the argument is the car you park in it.

4.2 Function Expression (Storing a Function in a Variable)

A function can also be stored in a variable, just like a number or a string:

script.js β€” function expression
const greet = function(name) {
  return `Hello, ${name}!`;
};

console.log(greet("Altamash")); // "Hello, Altamash!"

Notice: the function has no name after the function keyword. It's "anonymous" β€” it gets its name from the variable. The semicolon at the end is required because this is a variable assignment, not a declaration.

4.3 Arrow Function (The Modern Way) ⭐

Arrow functions were introduced in ES6 (2015) and are the preferred way to write short functions in modern JavaScript, React, and Next.js:

script.js β€” arrow function
// Full syntax
const greet = (name) => {
  return `Hello, ${name}!`;
};

// Shorthand: if the body is a single expression, skip { } and return
const greetShort = (name) => `Hello, ${name}!`;

// If only one parameter, skip the parentheses too
const greetShorter = name => `Hello, ${name}!`;

console.log(greetShorter("Altamash")); // "Hello, Altamash!"
(name) => { ... } The => is called the fat arrow. It replaces the function keyword. Read it as: "takes name, then does this."
(name) => `Hello, ${name}!` When the function body is a single expression, you can remove the { } and the return keyword. The expression's value is automatically returned. This is called an implicit return.
name => ... When there's exactly one parameter, the parentheses around it are optional.

4.4 Comparison: Three Ways to Write Functions

FeatureDeclarationExpressionArrow ⭐
Syntax function name() {} const name = function() {}; const name = () => {};
Hoisted? βœ… Yes β€” can call before it's defined ❌ No ❌ No
Has own this? βœ… Yes βœ… Yes ❌ No β€” inherits from parent
Implicit return? ❌ No ❌ No βœ… Yes (single-expression body)
Best for Top-level named functions Callbacks (older code) Callbacks, React components, event handlers
πŸ’‘ Professional Recommendation In modern codebases (React, Next.js), arrow functions are used for almost everything: event handlers, callbacks, array methods, and component definitions. Use function declarations only for top-level utility functions that benefit from hoisting. Avoid function expressions β€” arrow functions do the same thing with cleaner syntax.

5. Parameters, Default Values, and Return

5.1 Multiple Parameters

script.js β€” multiple parameters
const calculateTotal = (price, quantity, taxRate) => {
  const subtotal = price * quantity;
  const tax = subtotal * taxRate;
  return subtotal + tax;
};

const total = calculateTotal(499, 2, 0.18);
console.log(total); // 1177.64

5.2 Default Parameters

What if someone calls your function but forgets to pass a value? You can provide a fallback:

script.js β€” default parameter values
const greet = (name = "stranger") => `Hello, ${name}!`;

console.log(greet("Altamash")); // "Hello, Altamash!"
console.log(greet());             // "Hello, stranger!"

The = "stranger" after the parameter means: "If no argument is passed, use "stranger" as the default." The default is only used when the argument is undefined (which is what happens when you don't pass anything).

5.3 Return β€” The Function's Output

Three critical rules about return:

  1. return immediately stops the function β€” any code after it is unreachable
  2. A function without return gives back undefined
  3. Never put a newline between return and the value (JavaScript auto-inserts a semicolon!)

The Newline Trap

❌ BROKEN β€” return on its own line
function getPrice() {
  return          // ← JavaScript inserts a semicolon HERE!
    499;           // ← This line is never reached
}
console.log(getPrice()); // undefined (not 499!)
βœ… CORRECT β€” value starts on the same line
function getPrice() {
  return 499;
}

Why? JavaScript has a feature called Automatic Semicolon Insertion (ASI). If return is alone on a line, JS thinks the statement is return; (return undefined). This is a real-world production bug that has bitten professional developers.

6. Scope β€” Where Variables Live and Die

Scope means: "Where in the code can I access this variable?" Think of it like rooms in a house:

script.js β€” scope demonstration
const appName = "My Portfolio"; // GLOBAL β€” visible everywhere

function showWelcome() {
  const greeting = "Welcome!"; // LOCAL β€” only visible inside this function
  console.log(appName);  // βœ… Can access global from inside function
  console.log(greeting); // βœ… Can access own local variable
}

showWelcome();

console.log(appName);  // βœ… Global β€” accessible here
console.log(greeting); // ❌ ReferenceError! greeting is LOCAL to showWelcome

Scope Rule β€” Simple Version

  • A function can see out (access variables from the scope where it was created)
  • But the outside cannot see in (cannot access variables declared inside a function)
  • This is like a one-way mirror: the function sees the world, but the world can't see inside the function

7. Visual Execution β€” The Call Stack

When JavaScript runs your code, it uses a call stack β€” a stack of "frames" that track which function is currently running. Think of it like a stack of plates:

script.js β€” call stack example
function multiply(a, b) {
  return a * b;
}

function calculateArea(width, height) {
  const area = multiply(width, height);
  return area;
}

const result = calculateArea(5, 10);
console.log(result); // 50

Here's how the call stack evolves, step by step:

Step 1 global Script starts Step 2 global calculateArea Call calculateArea Step 3 global calculateArea multiply Call multiply Step 4 global All returned

Figure 2 β€” The call stack: functions are pushed on when called, popped off when they return. Last in, first out (LIFO).

What Happens in Memory

  1. JavaScript starts running your script β€” it creates a Global Execution Context and pushes it onto the call stack
  2. When it hits calculateArea(5, 10), it creates a new execution context for that function, with its own local variables (width = 5, height = 10), and pushes it onto the stack
  3. Inside calculateArea, it hits multiply(width, height) β€” another execution context is created and pushed
  4. multiply returns 50 β€” its context is popped off the stack
  5. calculateArea returns 50 β€” its context is popped off
  6. We're back in the global context. result is now 50.

7.1 Try It Yourself

Ctrl + Enter to run

        

8. Events β€” Making Pages React to Users

8.1 What Is an Event?

An event is anything that happens in the browser that you can respond to:

The real-life analogy: events are like a doorbell. The doorbell itself does nothing until someone rings it. You set up a function (the "handler") that should run when the doorbell rings. The browser is the house β€” it watches for rings and calls your handler when one happens.

8.2 addEventListener β€” The Right Way

index.html
<button id="greet-btn">Click me!</button>
<p id="output"></p>
script.js
// Step 1: Find the elements
const button = document.getElementById("greet-btn");
const output = document.getElementById("output");

// Step 2: Define what should happen when clicked
const handleClick = () => {
  output.textContent = "Hello! You clicked the button! πŸŽ‰";
};

// Step 3: Wire the function to the event
button.addEventListener("click", handleClick);
button.addEventListener("click", handleClick); This says: "Hey browser, when the button fires a "click" event, run the handleClick function."
β€’ First argument: the event type (a string β€” "click", "input", "keydown", etc.)
β€’ Second argument: the function to call (the event handler / callback)
Notice: We pass handleClick β€” NOT handleClick(). The parentheses would immediately call the function instead of waiting for the click!

Calling Instead of Passing

❌ WRONG
button.addEventListener("click", handleClick()); // ← () calls it NOW, not on click!
βœ… CORRECT
button.addEventListener("click", handleClick); // ← No () β€” we're PASSING the function

This is the #1 beginner mistake with events. Remember: you're giving the browser a reference to the function (like handing someone a recipe card), not executing it yourself.

8.3 Inline Arrow Functions in addEventListener

You don't always need a separate named function. For short handlers, write the arrow function directly:

script.js β€” inline arrow handler
button.addEventListener("click", () => {
  output.textContent = "Clicked! πŸŽ‰";
});

8.4 The Event Object β€” Know What Happened

Every time an event fires, the browser automatically passes an event object to your handler function. This object contains details about the event:

script.js β€” using the event object
button.addEventListener("click", (event) => {
  console.log(event.type);    // "click"
  console.log(event.target);  // the button element that was clicked
  console.log(event.clientX); // X coordinate of the mouse pointer
});
PropertyWhat It Tells YouExample Value
event.typeWhich event fired"click", "keydown"
event.targetThe element that triggered the eventThe <button> element
event.keyWhich keyboard key was pressed (keydown events)"Enter", "a"
event.preventDefault()Stops the browser's default behaviorPrevents form from reloading the page
event.clientX / clientYMouse pointer position142, 305

The parameter name event is a convention β€” you can call it e or evt too. Most professionals use e for brevity.

8.5 Common Event Types β€” A Practical Reference

EventFires When…Use Case
"click"User clicks an elementButtons, links, toggles
"input"User types/changes an input field valueLive search, character counters
"change"Input loses focus after being changedDropdown selects, checkboxes
"keydown"User presses a key downKeyboard shortcuts, game controls
"submit"Form is submittedContact forms, login forms
"mouseenter"Mouse moves over an elementHover effects, tooltips
"mouseleave"Mouse moves away from an elementClosing tooltips

9. Real-World Example β€” Interactive Form

Let's combine functions and events to build something real: a form that reacts as the user types and prevents the default page reload on submit.

index.html
<form id="contact-form">
  <label for="name-input">Name:</label>
  <input type="text" id="name-input" placeholder="Your name">
  <p id="live-preview"></p>
  <button type="submit">Submit</button>
</form>
<p id="form-result"></p>
script.js
// 1. Find the elements
const form = document.getElementById("contact-form");
const nameInput = document.getElementById("name-input");
const preview = document.getElementById("live-preview");
const result = document.getElementById("form-result");

// 2. Live preview as user types
nameInput.addEventListener("input", (e) => {
  const value = e.target.value;
  preview.textContent = value
    ? `Hello, ${value}! πŸ‘‹`
    : "";
});

// 3. Handle form submission
form.addEventListener("submit", (e) => {
  e.preventDefault(); // ← CRUCIAL: stops page from reloading!

  const name = nameInput.value.trim();

  if (name === "") {
    result.textContent = "Please enter your name!";
    return; // exit early
  }

  result.textContent = `βœ… Form submitted! Welcome, ${name}!`;
  nameInput.value = ""; // clear the input
  preview.textContent = "";
});

Why e.preventDefault()?

By default, when a form is submitted, the browser reloads the entire page (or navigates to a new URL). This destroys all your JavaScript state. e.preventDefault() says: "Don't do the default thing. I'll handle it with JavaScript instead." This is used in every single React and Next.js form.

10. Common Mistakes β€” The Pitfall Gallery

Mistake 1: Forgetting return

const double = (n) => { n * 2; };      // ❌ Returns undefined!
const double = (n) => { return n * 2; }; // βœ… Explicit return
const double = (n) => n * 2;             // βœ… Implicit return (no braces)

If you use { } in an arrow function, you must write return explicitly.

Mistake 2: Shadowing Variables

let count = 0;
function increment() {
  let count = 0;  // ❌ Creates a NEW local variable β€” doesn't change the outer one!
  count++;
}
increment();
console.log(count); // Still 0!

If you declare a variable with the same name inside a function, it "shadows" (hides) the outer variable. Remove the let inside the function if you want to modify the outer variable.

Mistake 3: Using an Element Before It Exists

<head>
  <script src="script.js"></script>  <!-- ❌ Runs before the HTML body loads -->
</head>

Fix: Put your <script> at the end of the body, or use the defer attribute: <script src="script.js" defer>.

11. Interview Perspective

What interviewers ask about functions & events

  1. "What is the difference between a parameter and an argument?"
    Parameter = placeholder in the definition. Argument = actual value at call time.
  2. "What is hoisting?"
    Function declarations are "hoisted" β€” moved to the top of their scope before execution. You can call them before they appear in the code. Function expressions and arrow functions are NOT hoisted.
  3. "What happens if you call a function that doesn't return anything?"
    It returns undefined.
  4. "What is the difference between addEventListener and onclick?"
    addEventListener lets you attach multiple handlers to the same event. onclick only allows one β€” the latest one overwrites previous ones. Always use addEventListener.
  5. "What does e.preventDefault() do?"
    Stops the browser's default behavior for that event (e.g., form submission reloading the page, link navigation).
  6. Tricky: "What does this code output?"
    const x = 10;
    function test() {
      console.log(x);
      const x = 20;
    }
    test();
    Answer: ReferenceError! Due to the Temporal Dead Zone (TDZ). The const x = 20 inside the function shadows the outer x, but it can't be used before its declaration line. This is a scope + hoisting trap.

12. Best Practices β€” How Professionals Write Functions

  1. Use arrow functions for callbacks and event handlers
  2. Use function declarations for top-level utility functions
  3. Name functions descriptively β€” handleClick, calculateTotal, formatDate, not fn, doStuff, x
  4. Keep functions small β€” a function should do ONE thing. If it's doing 5 things, split it into 5 functions
  5. Always use addEventListener β€” never use onclick attributes in HTML
  6. Always use e.preventDefault() on form submit handlers
  7. Prefer const for function expressions β€” prevents accidental reassignment
  8. Use default parameters instead of checking for undefined manually

13. Real-World Use Cases

Where You'll See This in the Wild

  • React: Every React component is a function. Every onClick handler is a function passed to an event listener under the hood.
  • Next.js: Every page and API route is a function. Server Actions are async functions.
  • Node.js / Express: Every route handler is a function: app.get("/api/users", (req, res) => { ... })
  • Array methods: .map(), .filter(), .forEach() all take a function as an argument (this is called a callback)
  • Form validation: Every "Please enter a valid email" message is triggered by an event listener running a validation function

14. Knowledge Check β€” Quiz

Quiz: Functions & Events (6 Questions)

What keyword is used to send a value back from a function?
Correct. return immediately terminates function execution and specifies the value returned to the caller.
return is the keyword. Without an explicit return, a JavaScript function gives back undefined.
What does addEventListener take as its SECOND argument?
Correct! First argument is the event name string ("click"), second is the callback function to execute.
The second argument is the function (callback) that the browser calls when the event occurs. Pass its name without ()!
What value does a function return if it has NO return statement?
Correct. Functions without an explicit return statement return undefined by default.
In JavaScript, if no value is explicitly returned using the return keyword, the result is always undefined.
Which function syntax supports IMPLICIT return (no return keyword required)?
Correct. const add = (a, b) => a + b; automatically returns a + b without braces or return.
Only arrow functions without curly braces { } perform an implicit return of the single expression inside.
What does e.preventDefault() do inside a form submit handler?
Correct. It prevents the default HTTP form submission reload so JavaScript can process input values directly.
Browsers automatically reload the page on form submission. e.preventDefault() stops this default behavior so your JS can run.
Why is button.addEventListener("click", handleClick()) WRONG?
Correct! handleClick() runs the function right away during registration. Omit () to pass the function reference!
Adding () invokes the function right away instead of passing it as a handler for future clicks. Omit ()!
–

Quiz Complete!

Review any concepts you got wrong using the Functions & Events cheatsheet.

15. Retrieval Practice β€” Fill in the Blanks

Write from memory. Effortful recall is how knowledge moves into long-term memory.

/* 1. The modern, concise function syntax is called an _____ function. */
const add = (a, b) => a + b; //
/* 2. A function passed into addEventListener is called a _____ function. */
button.addEventListener("click", );
/* 3. The browser automatically passes an _____ object to the handler. */
button.addEventListener("click", ( ) => {});
/* 4. To prevent page reload on submit, call e._____() */
e. ();
/* 5. Variables declared inside a function have _____ scope. */
// Scope restricted to inside function β†’
/* 6. A function without a return statement gives back _____ */
// Default function return value β†’

16. Practice Questions (Easy β†’ Hard)

Do NOT look at answers. Write the code yourself, run it, and verify.

  1. Write a function square that takes a number and returns its square. Easy
  2. Write an arrow function isEven that returns true if a number is even, false otherwise. Easy
  3. Write a function greetUser with a default parameter so that greetUser() returns "Hello, Guest!". Easy
  4. Create a button in HTML. When clicked, it changes its own text to "Clicked!". Easy
  5. Create an input field and a paragraph. As the user types, the paragraph shows a live character count. Medium
  6. Write a function max that takes two numbers and returns the larger one. Do NOT use Math.max. Medium
  7. Create a "Toggle Dark Mode" button. Clicking it adds/removes a class "dark" on the <body> tag. Medium
  8. Write a function factorial that calculates the factorial of a number using a loop. factorial(5) should return 120. Medium
  9. Build a mini calculator: two input fields, four buttons (+, βˆ’, Γ—, Γ·), and a result display. Each button calls a calculation function and updates the result. Hard
  10. Create a password input with a "Show/Hide" toggle button. When clicked, it switches the input type between "password" and "text". Hard

17. Mini Project β€” Interactive Profile Card

πŸ› οΈ Build Challenge: Make Your Portfolio Card Interactive

Take the profile card you built in Lesson 9 (using the data β†’ template β†’ inject pattern) and upgrade it with functions and events:

  1. Create a function called renderProfile that takes name, focus, and skills as parameters, builds the template literal HTML, and returns it.
  2. Create a form with three input fields: Name, Focus, and Skills (comma-separated).
  3. Add a "Generate Card" button. When the form is submitted:
    • Call e.preventDefault()
    • Read the values from the input fields
    • Call renderProfile() with those values
    • Inject the returned HTML into the page using .innerHTML
  4. Add live preview: As the user types in the "Name" field, show a live greeting above the form.
  5. Add validation: If the name is empty, show an error message instead of generating the card.

What This Project Teaches

  1. Functions with parameters and return values β€” renderProfile takes data in, produces HTML out
  2. Event listeners β€” "submit" on the form, "input" on the name field
  3. e.preventDefault() β€” stop the form from reloading
  4. Reading user input β€” inputElement.value
  5. The data β†’ template β†’ inject pattern β€” reinforcing the React mental model

This is literally how React forms work, minus the JSX syntax. If you can build this, you understand the core pattern of 90% of web applications.

18. Lesson Summary

  1. A function is a reusable block of code with a name, parameters, a body, and a return value
  2. Three ways to create functions: declaration, expression, arrow function (use arrow for most things)
  3. Parameters are placeholders; arguments are the actual values you pass
  4. Default parameters provide fallback values when arguments are missing
  5. return sends a value back. Without it, you get undefined
  6. Scope: functions can see out (access globals), but the outside cannot see in (local variables are private)
  7. Events are things that happen in the browser (click, input, submit, keydown)
  8. addEventListener is the right way to wire a function to an event
  9. Pass the function by name (no parentheses): addEventListener("click", handler)
  10. The event object (e) gives you details about what happened
  11. e.preventDefault() stops the browser's default action (essential for forms)

19. Quick Revision Notes (5-Minute Review)

Functions Cheat Sheet

// Declaration (hoisted)
function add(a, b) { return a + b; }

// Arrow (modern, preferred)
const add = (a, b) => a + b;

// Default parameter
const greet = (name = "Guest") => `Hi, ${name}`;

// Functions return undefined if no return keyword

Events Cheat Sheet

// Attach event
element.addEventListener("click", handler);

// Inline arrow
button.addEventListener("click", (e) => {
  e.preventDefault();
  console.log(e.target);
});

// Common events: click, input, submit, keydown, change
// NEVER use handleClick() with () in addEventListener!

20. Challenge β€” Combine Functions + Events + Lesson 9

⚠️ Coding Challenge

Build a "Grade Calculator" page:

  1. Create a form with 5 input fields for subject marks (out of 100 each).
  2. Write a function calculateAverage that takes 5 numbers and returns the average.
  3. Write a function getGrade that takes an average and returns a letter grade (A+ for 90+, A for 80+, B for 70+, C for 60+, F otherwise).
  4. When the form is submitted, calculate the average and grade, then display both on the page using template literals.
  5. Add live preview: as the user types marks, show the running total of entered subjects.
  6. Validate: all marks must be between 0 and 100. Show an error for invalid input.

This challenge tests: functions with parameters & return, arrow functions, addEventListener, e.preventDefault(), template literals, conditionals, and validation.

21. Comprehension Check β€” Answer These Before Moving On

  1. In your own words, what is the difference between defining a function and calling a function?
  2. Why do arrow functions NOT have their own this? (Hint: think about what would happen inside an event handler inside an object method.)
  3. If you write addEventListener("click", myFunc()) instead of addEventListener("click", myFunc), what happens and why?
  4. What is the call stack, and why does JavaScript need it?
  5. Why do we always call e.preventDefault() in form submit handlers?

Primary Sources: javascript.info: Functions is the best free deep-dive on function mechanics. MDN: Introduction to events is the authoritative reference for event handling.

Finished the build? Type /teach and tell me what you built. I'll review your functions, event handlers, and the form validation pattern. After this, we move on to control flow: conditionals, comparison operators, and logical operators β€” where your code starts making decisions.

πŸ’¬ Questions? Ask your teacher. Functions, arrow functions, scope, addEventListener, the event object, e.preventDefault() β€” if anything feels fuzzy, ask me now. Type your question or paste your code and I'll explain what's happening. Better yet: break something in the playground above and show me the error. Debugging is the fastest way to learn.