Session 2.4 – Functions and Events
Module 2: JavaScript & AJAX | Duration: 1 hr
Learning Objectives
By the end of this session, students will be able to:
- Create and use functions with different syntaxes
- Understand function parameters, return values, and scope
- Use arrow functions and understand their benefits
- Handle user events in web pages
- Implement event listeners and understand event propagation
Introduction to Functions and Events
Functions are reusable blocks of code that perform specific tasks. Events allow JavaScript to respond to user interactions, making web pages interactive and dynamic.
Why Functions and Events Matter
Functions help organize code into modular, reusable pieces. Events bridge the gap between user actions and JavaScript responses, enabling interactive web applications. Together, they form the foundation of dynamic web development.
JavaScript Functions
Function Declaration
// Basic function declaration
function greet() {
console.log("Hello, World!");
}
greet(); // Call the function
// Function with parameters
function greetPerson(name) {
console.log(`Hello, ${name}!`);
}
greetPerson("Alice"); // "Hello, Alice!"
// Function with multiple parameters
function add(a, b) {
return a + b;
}
let sum = add(5, 3);
console.log(sum); // 8
// Function with default parameters (ES6)
function greetWithTitle(name, title = "Mr.") {
return `Hello, ${title} ${name}`;
}
console.log(greetWithTitle("Smith")); // "Hello, Mr. Smith"
console.log(greetWithTitle("Johnson", "Dr.")); // "Hello, Dr. Johnson"
Function Expression
// Function expression (anonymous function)
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
// Named function expression
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1);
};
console.log(factorial(5)); // 120
Function Scope and Closures
// Global scope
let globalVar = "I'm global";
function outerFunction() {
// Local scope
let outerVar = "I'm outer";
function innerFunction() {
// Inner scope
let innerVar = "I'm inner";
console.log(globalVar); // Accessible
console.log(outerVar); // Accessible
console.log(innerVar); // Accessible
}
innerFunction();
// console.log(innerVar); // Error: not accessible here
}
outerFunction();
// Closure example
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
let counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
console.log(counter.decrement()); // 1
Rest Parameters and Spread Operator
// Rest parameters - collect arguments into array
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// Combine regular and rest parameters
function introduce(greeting, ...names) {
return `${greeting} ${names.join(", ")}!`;
}
console.log(introduce("Hello", "Alice", "Bob", "Charlie"));
// "Hello Alice, Bob, Charlie!"
// Spread operator in function calls
let numbers = [5, 2, 8, 1, 9];
console.log(Math.max(...numbers)); // 9
// Spread with arrays
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
Callback Functions
// Callback function example
function processArray(arr, callback) {
let result = [];
for (let item of arr) {
result.push(callback(item));
}
return result;
}
let numbers = [1, 2, 3, 4, 5];
// Using callback to double numbers
let doubled = processArray(numbers, function(n) {
return n * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
// Using callback to square numbers
let squared = processArray(numbers, n => n * n);
console.log(squared); // [1, 4, 9, 16, 25]
// Real-world example: setTimeout
setTimeout(function() {
console.log("This runs after 2 seconds");
}, 2000);
Higher-Order Functions
// Function that returns a function
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
let double = multiplier(2);
let triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Function that takes a function as parameter
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, console.log);
// Output: 0, 1, 2
// Practical example: logger wrapper
function logger(fn) {
return function(...args) {
console.log(`Calling function with args: ${args}`);
let result = fn(...args);
console.log(`Result: ${result}`);
return result;
};
}
let loggedAdd = logger(add);
loggedAdd(5, 3); // Logs: "Calling...", "Result: 8"
Arrow Functions (ES6)
Arrow functions provide a more concise syntax for writing function expressions.
// Traditional function expression
const add1 = function(a, b) {
return a + b;
};
// Arrow function - full syntax
const add2 = (a, b) => {
return a + b;
};
// Arrow function - concise (implicit return)
const add3 = (a, b) => a + b;
// Single parameter - parentheses optional
const square = x => x * x;
// No parameters - parentheses required
const greet = () => "Hello!";
// Multiple statements - need curly braces
const processNumber = (n) => {
let doubled = n * 2;
let squared = doubled * doubled;
return squared;
};
console.log(add3(5, 3)); // 8
console.log(square(4)); // 16
console.log(greet()); // "Hello!"
console.log(processNumber(2)); // 16
Arrow Functions with Arrays
let numbers = [1, 2, 3, 4, 5];
// Traditional way
let doubled1 = numbers.map(function(n) {
return n * 2;
});
// Arrow function way
let doubled2 = numbers.map(n => n * 2);
// More examples
let evens = numbers.filter(n => n % 2 === 0);
let sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(doubled2); // [2, 4, 6, 8, 10]
console.log(evens); // [2, 4]
console.log(sum); // 15
// Chaining with arrow functions
let result = numbers
.filter(n => n > 2)
.map(n => n * n)
.reduce((acc, n) => acc + n, 0);
console.log(result); // 50 (9 + 16 + 25)
this Keyword in Arrow Functions
// Regular function - 'this' depends on how it's called
let person1 = {
name: "Alice",
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
person1.greet(); // "Hello, I'm Alice"
// Arrow function - 'this' is lexically bound
let person2 = {
name: "Bob",
hobbies: ["reading", "coding"],
showHobbies: function() {
// 'this' refers to person2
this.hobbies.forEach(hobby => {
// Arrow function uses 'this' from showHobbies
console.log(`${this.name} likes ${hobby}`);
});
}
};
person2.showHobbies();
// "Bob likes reading"
// "Bob likes coding"
// Problem with regular function
let person3 = {
name: "Charlie",
hobbies: ["gaming", "music"],
showHobbies: function() {
this.hobbies.forEach(function(hobby) {
// 'this' is undefined in strict mode
// console.log(`${this.name} likes ${hobby}`); // Error!
});
}
};
JavaScript Events
Events are actions or occurrences that happen in the browser, which JavaScript can detect and respond to.
Adding Event Listeners
// HTML:
// <button id="myButton">Click Me</button>
// Method 1: addEventListener (recommended)
let button = document.getElementById("myButton");
button.addEventListener("click", function() {
alert("Button clicked!");
});
// Method 2: Using arrow function
button.addEventListener("click", () => {
console.log("Button was clicked");
});
// Method 3: Named function
function handleClick() {
console.log("Handled by named function");
}
button.addEventListener("click", handleClick);
// Removing event listener
button.removeEventListener("click", handleClick);
// Method 4: Inline HTML (not recommended)
// <button onclick="alert('Clicked!')">Click</button>
// Method 5: DOM property (can only have one handler)
button.onclick = function() {
console.log("Only one handler allowed this way");
};
Event Object
// Event object contains information about the event
button.addEventListener("click", function(event) {
console.log("Event type:", event.type); // "click"
console.log("Target element:", event.target); // button element
console.log("Current target:", event.currentTarget);
console.log("Mouse X:", event.clientX);
console.log("Mouse Y:", event.clientY);
console.log("Timestamp:", event.timeStamp);
});
// Prevent default behavior
let link = document.getElementById("myLink");
link.addEventListener("click", function(event) {
event.preventDefault(); // Prevent navigation
console.log("Link clicked but not followed");
});
// Stop event propagation
let parent = document.getElementById("parent");
let child = document.getElementById("child");
child.addEventListener("click", function(event) {
event.stopPropagation(); // Prevent bubbling to parent
console.log("Child clicked");
});
parent.addEventListener("click", function() {
console.log("This won't run if child is clicked");
});
Event Delegation
// Instead of adding listeners to each item
// Add one listener to parent and use event delegation
// HTML:
// <ul id="itemList">
// <li>Item 1</li>
// <li>Item 2</li>
// <li>Item 3</li>
// </ul>
let list = document.getElementById("itemList");
// One listener for all list items (present and future)
list.addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
console.log("Clicked:", event.target.textContent);
event.target.style.backgroundColor = "yellow";
}
});
// This works even for dynamically added items
let newItem = document.createElement("li");
newItem.textContent = "Item 4";
list.appendChild(newItem); // Event delegation still works!
Common Event Types
Mouse Events
let element = document.getElementById("myElement");
// Mouse events
element.addEventListener("click", () => {
console.log("Clicked");
});
element.addEventListener("dblclick", () => {
console.log("Double clicked");
});
element.addEventListener("mouseenter", () => {
console.log("Mouse entered");
});
element.addEventListener("mouseleave", () => {
console.log("Mouse left");
});
element.addEventListener("mousemove", (event) => {
console.log(`Mouse at: ${event.clientX}, ${event.clientY}`);
});
element.addEventListener("mousedown", () => {
console.log("Mouse button pressed");
});
element.addEventListener("mouseup", () => {
console.log("Mouse button released");
});
Keyboard Events
let input = document.getElementById("myInput");
// Keyboard events
input.addEventListener("keydown", (event) => {
console.log("Key pressed:", event.key);
console.log("Key code:", event.keyCode);
});
input.addEventListener("keyup", (event) => {
console.log("Key released:", event.key);
});
input.addEventListener("keypress", (event) => {
console.log("Key press:", event.key);
});
// Check for specific keys
document.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
console.log("Enter key pressed");
}
if (event.ctrlKey && event.key === "s") {
event.preventDefault();
console.log("Ctrl+S pressed");
}
});
Form Events
let form = document.getElementById("myForm");
let input = document.getElementById("username");
// Form submission
form.addEventListener("submit", (event) => {
event.preventDefault(); // Prevent page reload
console.log("Form submitted");
// Process form data here
});
// Input events
input.addEventListener("input", (event) => {
console.log("Input value:", event.target.value);
});
input.addEventListener("change", (event) => {
console.log("Input changed:", event.target.value);
});
input.addEventListener("focus", () => {
console.log("Input focused");
input.style.backgroundColor = "lightyellow";
});
input.addEventListener("blur", () => {
console.log("Input lost focus");
input.style.backgroundColor = "white";
});
Window/Document Events
// Page load event
window.addEventListener("load", () => {
console.log("Page fully loaded");
});
// DOM ready (can also use DOMContentLoaded)
document.addEventListener("DOMContentLoaded", () => {
console.log("DOM is ready");
// Safe to manipulate DOM here
});
// Scroll event
window.addEventListener("scroll", () => {
console.log("Scroll position:", window.scrollY);
});
// Resize event
window.addEventListener("resize", () => {
console.log("Window size:", window.innerWidth, window.innerHeight);
});
// Before unload (leaving page)
window.addEventListener("beforeunload", (event) => {
event.preventDefault();
// Some browsers require returnValue to be set
event.returnValue = "";
});
Practical Examples
Example 1: Interactive Counter
// Counter implementation
let count = 0;
let display = document.getElementById("counterDisplay");
document.getElementById("incrementBtn").addEventListener("click", () => {
count++;
display.textContent = count;
});
document.getElementById("decrementBtn").addEventListener("click", () => {
count--;
display.textContent = count;
});
document.getElementById("resetBtn").addEventListener("click", () => {
count = 0;
display.textContent = count;
});
Example 2: Form Validation
// HTML:
// <form id="registrationForm">
// <input type="text" id="username" required>
// <input type="email" id="email" required>
// <input type="password" id="password" required>
// <button type="submit">Register</button>
// </form>
let form = document.getElementById("registrationForm");
form.addEventListener("submit", function(event) {
event.preventDefault();
let username = document.getElementById("username").value;
let email = document.getElementById("email").value;
let password = document.getElementById("password").value;
// Validation
let errors = [];
if (username.length < 3) {
errors.push("Username must be at least 3 characters");
}
if (!email.includes("@")) {
errors.push("Invalid email address");
}
if (password.length < 8) {
errors.push("Password must be at least 8 characters");
}
if (errors.length > 0) {
alert("Errors:\n" + errors.join("\n"));
} else {
console.log("Form is valid!");
// Submit form data
}
});
Example 3: Dynamic Todo List
// HTML:
// <input type="text" id="todoInput" placeholder="Enter task">
// <button id="addBtn">Add</button>
// <ul id="todoList"></ul>
const todoInput = document.getElementById("todoInput");
const addBtn = document.getElementById("addBtn");
const todoList = document.getElementById("todoList");
// Add task
addBtn.addEventListener("click", addTask);
todoInput.addEventListener("keypress", (event) => {
if (event.key === "Enter") {
addTask();
}
});
function addTask() {
let taskText = todoInput.value.trim();
if (taskText === "") return;
// Create list item
let li = document.createElement("li");
li.textContent = taskText;
// Create delete button
let deleteBtn = document.createElement("button");
deleteBtn.textContent = "Delete";
deleteBtn.style.marginLeft = "10px";
deleteBtn.addEventListener("click", () => {
li.remove();
});
// Toggle complete on click
li.addEventListener("click", () => {
li.style.textDecoration =
li.style.textDecoration === "line-through" ? "none" : "line-through";
});
li.appendChild(deleteBtn);
todoList.appendChild(li);
todoInput.value = "";
}
Session Summary
Key Points
- Functions are reusable blocks of code that can accept parameters and return values
- Arrow functions provide concise syntax and lexical 'this' binding
- Callback functions and higher-order functions enable powerful programming patterns
- Events allow JavaScript to respond to user interactions
- addEventListener() is the recommended way to attach event handlers
- Event object contains information about the event and provides control methods
- Event delegation improves performance for dynamic content
- Common events include mouse, keyboard, form, and window events
Next Session Preview
In the next session, we will explore DOM Manipulation, learning how to dynamically create, modify, and delete HTML elements using JavaScript.