JavaScript Tutorial: The Brains of the Web

Section 1: What is JavaScript?

If HTML is the skeleton of a webpage and CSS is its clothing and appearance, then JavaScript (JS) is the brain and nervous system. It's a full-fledged programming language that runs directly in the user's web browser, allowing you to create dynamic, interactive experiences. Without JavaScript, the web would be a static collection of documents. With it, we can build complex applications, games, and responsive user interfaces.

JavaScript's primary role in web development is to manipulate the Document Object Model (DOM), which is the browser's object-based representation of your HTML. By changing the DOM, JavaScript can add, remove, or modify any element on the page in response to user actions.

Section 2: The Core Language - Fundamentals

Variables: let, const, and var

Variables are named containers for storing data. In modern JavaScript, you have two primary ways to declare them:

  • let: Used for variables whose value might change later. For example, a counter in a loop or a user's current score.
  • const: Used for constants, which are variables that should never be reassigned after their initial declaration. Using const is a best practice as it prevents accidental changes to your variables.

You may also see var in older code. It has different scoping rules (function scope instead of block scope) which can lead to confusing bugs. It is recommended to always use let and const in modern code.

let score = 0; // Can be changed
score = 10;

const playerName = "Alice"; // Cannot be reassigned
// playerName = "Bob"; // This would cause an error

Data Types

JavaScript has several primitive data types:

  • String: Text, enclosed in single quotes, double quotes, or backticks (`).
  • Number: Both integers and floating-point numbers. There is no separate "int" or "float" type.
  • Boolean: true or false.
  • Null: Represents the intentional absence of any object value.
  • Undefined: A variable that has been declared but not yet assigned a value.

Section 3: Control Flow

Control flow structures allow you to direct the execution of your code based on certain conditions.

Conditional Statements

The if...else if...else chain works just like in other languages, allowing your program to make decisions.

let hour = 14;

if (hour < 12) {
  console.log("Good morning!");
} else if (hour < 18) {
  console.log("Good afternoon!");
} else {
  console.log("Good evening!");
}

Loops

Loops are used to execute a block of code repeatedly. The most common is the for loop, which is great for when you know how many times you want to iterate.

// A standard for loop
for (let i = 0; i < 5; i++) {
  console.log("The number is " + i);
}

// Looping over an array (more on arrays later)
const colors = ['red', 'green', 'blue'];
for (const color of colors) {
  console.log(color);
}

Section 4: Functions

Functions are reusable blocks of code that perform a specific task. They are fundamental to writing organized and maintainable code.

Defining and Calling Functions

// Function declaration
function greet(name) {
  return `Hello, ${name}!`;
}

// Calling the function
const message = greet("Pietopy");
console.log(message); // Outputs: Hello, Pietopy!

Arrow Functions

Modern JavaScript (ES6) introduced a more concise syntax for writing functions, called arrow functions. They are especially useful for simple, one-line functions.

// The same greet function as an arrow function
const greetArrow = (name) => {
  return `Hello, ${name}!`;
};

// If the function only has one line that returns a value, it can be even shorter:
const add = (a, b) => a + b;

console.log(add(5, 3)); // Outputs: 8

Section 5: The Document Object Model (DOM)

The DOM is the most important concept for client-side JavaScript. It is a tree-like structure created by the browser that represents your HTML document. JavaScript can interact with this tree to change the structure, style, and content of the webpage dynamically.

Selecting Elements

To manipulate an element, you first need to select it.

  • document.getElementById('some-id'): Selects the single element with a specific ID.
  • document.querySelector('.some-class'): Selects the *first* element that matches a CSS selector.
  • document.querySelectorAll('.some-class'): Selects *all* elements that match a CSS selector, returning them in a list-like object called a NodeList.

Manipulating Elements

Once you have an element selected, you can change it.

// HTML: 

Hello

const title = document.getElementById('main-title'); // Change its text content title.textContent = "Goodbye!"; // Change its style title.style.color = 'red'; title.style.backgroundColor = '#333'; // Change its CSS classes title.classList.add('highlight'); title.classList.remove('old-class');

Section 6: Events

Events are actions that happen in the browser, such as a user clicking a button, hovering over an element, or pressing a key. JavaScript can "listen" for these events and execute a function in response.

Event Listeners

The modern way to handle events is with addEventListener. You tell an element which event to listen for and which function (a "callback" function) to run when the event occurs.

// HTML: 
const myButton = document.getElementById('my-button');

function handleClick() {
  alert("Button was clicked!");
}

// Add the event listener
myButton.addEventListener('click', handleClick);

Common events include click, mouseover, mouseout, keydown, keyup, and submit (for forms).

Section 7: Asynchronous JavaScript

By default, JavaScript runs one command at a time. If a command takes a long time to complete (like fetching a large file from a server), it will "block" the rest of the code from running, freezing the webpage. Asynchronous JavaScript is the solution to this problem.

Promises and `fetch`

A Promise is an object representing the eventual completion (or failure) of an asynchronous operation. It's a placeholder for a value you don't have yet.

The modern fetch API is used to make network requests (e.g., to get data from another server) and it returns a Promise.

fetch('https://api.example.com/data')
  .then(response => response.json()) // This also returns a promise
  .then(data => {
    console.log(data); // This runs only after the data has been received and parsed
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });

async / await

async and await are modern syntactic sugar that make working with Promises much cleaner and easier to read. An async function is a function that is guaranteed to return a promise. The await keyword can only be used inside an async function, and it pauses the function's execution until the promise is settled (resolved or rejected).

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}