JavaScript Core Concepts & Advanced Techniques

JavaScript Fundamentals: Web Storage & Event Handling

This section covers essential client-side data storage mechanisms and advanced event management techniques in JavaScript.

1. Web Storage (Local Storage & Session Storage)

Web Storage allows web applications to store data locally within the user’s browser. Unlike cookies, Web Storage offers a larger storage capacity (typically 5MB or more) and the data is not sent to the server with every HTTP request.

A. Local Storage – Permanent Data Storage

Data stored in Local Storage persists even after the browser window is closed and reopened. It has no expiration date.

  • Save: localStorage.setItem("username", "JohnDoe"); localStorage.setItem("key", "value");
  • Retrieve: const user = localStorage.getItem("username");
    console.log(user); // "JohnDoe"
  • Remove: localStorage.removeItem("username");
  • Clear All: localStorage.clear();

B. Session Storage – Session-Specific Data Storage

Data stored in Session Storage is available only for the duration of the browser session (i.e., until the browser tab or window is closed). It is unique to each tab.

  • Save: sessionStorage.setItem("sessionID", "abc123");
  • Retrieve: const session = sessionStorage.getItem("sessionID");
    console.log(session); // "abc123"
  • Remove: sessionStorage.removeItem("sessionID");
  • Clear All: sessionStorage.clear();

2. Cookies (Data Sent with Every HTTP Request)

Cookies are small pieces of data stored on the user’s computer by the web browser. They are primarily used for tracking, session management, and personalization. Unlike Web Storage, cookies are sent with every HTTP request to the server.

  • Set (expires Jan 1, 2025): document.cookie = "username=JohnDoe; expires=" + new Date(2025,0,1).toUTCString();
  • Read: console.log(document.cookie);
  • Delete: document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC;";

3. Closures (A Function Inside a Function)

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In simpler terms, a closure gives you access to an outer function’s scope from an inner function, even after the outer function has finished executing.

Example:

function outerFunction(outerVariable) {
    return function innerFunction(innerVariable) {
        console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
    };
}
const closureExample = outerFunction("Hello");
closureExample("World"); // Output: "Outer: Hello, Inner: World"

4. Event Delegation

Event delegation is a technique where you attach a single event listener to a parent element, instead of attaching multiple listeners to individual child elements. This improves performance and simplifies code, especially for dynamically added elements.

Example:

document.getElementById("parent").addEventListener("click", function(event) {
    if (event.target && event.target.matches("button")) {
        console.log("Button clicked:", event.target.textContent);
    }
});

5. Preventing Default Actions

Many HTML elements have default behaviors (e.g., a link navigating to a new page, a form submitting). The event.preventDefault() method stops these default actions from occurring.

Example:

document.getElementById("myLink").addEventListener("click", function(event) {
    event.preventDefault();
    console.log("Link click prevented!");
});

6. Event Propagation (Bubbling & Stopping)

Event propagation describes the order in which events are fired on nested elements. By default, events “bubble up” from the target element to its ancestors. event.stopPropagation() can prevent this bubbling.

Example:

document.getElementById("child").addEventListener("click", function(event) {
    event.stopPropagation();
    console.log("Child clicked, but event won't bubble up.");
});
document.getElementById("parent").addEventListener("click", function() {
    console.log("Parent clicked!");
});

Summary of File 1: Local Storage provides permanent data storage; Session Storage offers temporary, session-specific data; Cookies are small data packets sent with HTTP requests; Closures allow inner functions to access outer scope variables; Event Delegation optimizes event handling; preventDefault() stops default browser actions; and Event Propagation controls how events move through the DOM.

Next Steps: Continue with the next set of JavaScript concepts.

jQuery Essentials: DOM Manipulation, Events & AJAX

This section introduces jQuery, a fast, small, and feature-rich JavaScript library that simplifies HTML document traversal and manipulation, event handling, animation, and Ajax interactions.

1. jQuery Library Integration

jQuery simplifies many common JavaScript tasks. To use it, you must include the library in your HTML document, typically via a Content Delivery Network (CDN).

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

2. Document Ready Event

The $(function(){...}); syntax ensures that your jQuery code runs only after the entire HTML document has been loaded and parsed, preventing issues with elements not yet existing in the DOM.

$(function() {
    alert("jQuery is loaded and ready!");
});

3. Click Events

Attaching click event handlers to elements is straightforward with jQuery.

$("#btn").click(function() {
    alert("Button clicked!");
});

4. Mouse Hover Events (Background Change)

jQuery provides convenient methods for handling mouse interactions like mouseenter and mouseleave.

  • Mouse Enter:
    $("#box").mouseenter(function() {
        $(this).css("background-color", "yellow");
    });
  • Mouse Leave:
    $("#box").mouseleave(function() {
        $(this).css("background-color", "white");
    });

5. Keypress Events

Respond to keyboard input using the keypress event.

$("#inputBox").keypress(function() {
    console.log("Key pressed!");
});

6. Toggle Elements Visibility

The toggle() method can show or hide elements with a sliding animation.

$("#btn").click(function() {
    $("#content").toggle();
});

7. Animations

jQuery offers simple methods for common animations.

A. Fade In/Out

Gradually show or hide elements by fading them in or out.

  • Fade In:
    $("#fadeInBtn").click(function() {
        $("#box").fadeIn();
    });
  • Fade Out:
    $("#fadeOutBtn").click(function() {
        $("#box").fadeOut();
    });

B. Slide Toggle

Slide elements up or down to show or hide them.

$("#slideBtn").click(function() {
    $("#panel").slideToggle();
});

8. Change Text/HTML Content

Easily update the text or HTML content of elements.

$("#btn").click(function() {
    $("#heading").text("New Text");
    $("#content").html("New HTML Content");
});

9. Change Attributes

Modify HTML attributes of elements.

$("img").attr("src", "new-image.jpg");

10. CSS Class Manipulation

Add, remove, or toggle CSS classes on elements to change their styling.

  • Add Class: $("#box").addClass("highlight");
  • Remove Class: $("#box").removeClass("highlight");
  • Toggle Class: $("#box").toggleClass("highlight");

11. DOM Navigation

jQuery provides powerful methods to traverse the DOM tree.

  • Parent: $("#child").parent().css("border", "2px solid red");
  • Children: $("#parent").children().css("color", "blue");
  • Siblings: $("#sibling").siblings().hide();

12. AJAX Search Implementation

Perform asynchronous HTTP requests (AJAX) to fetch data from a server without reloading the entire page, often used for live search functionality.

$("#searchBox").keyup(function() {
    let query = $(this).val();
    if (query.length > 2) {
        $.ajax({
            url: "someURL",
            method: "GET",
            data: { query: query },
            success: function(response) {
                $("#results").html(response);
            }
        });
    } else {
        $("#results").html("");
    }
});

Summary of File 2: This section covered jQuery’s readiness function, various event handlers (click, mouse, keypress), element toggling and animations (fade, slide), methods for changing text, HTML, and attributes, CSS class manipulation, DOM navigation, and an example of an AJAX live search implementation.

Next Steps: Continue with modern JavaScript features.

Modern JavaScript: Arrays, Destructuring & DOM

This section delves into powerful ES6+ features for array manipulation, object/array destructuring, and direct DOM manipulation techniques.

1. Array Methods

Modern JavaScript provides a rich set of built-in methods for efficient array manipulation.

  • A. forEach(): Executes a provided function once for each array element.
    const numbers = [1, 2, 3, 4];
    numbers.forEach(num => console.log(num * 2)); // Output: 2, 4, 6, 8
  • B. map(): Creates a new array populated with the results of calling a provided function on every element in the calling array.
    const squared = numbers.map(num => num * num); // Result: [1, 4, 9, 16]
  • C. filter(): Creates a new array with all elements that pass the test implemented by the provided function.
    const evenNumbers = numbers.filter(num => num % 2 === 0); // Result: [2, 4]
  • D. reduce(): Executes a reducer function on each element of the array, resulting in a single output value.
    const sum = numbers.reduce((acc, num) => acc + num, 0); // Result: 10
  • E. shift(): Removes the first element from an array and returns that removed element. This changes the length of the array.
    const names = ["Alice", "Bob", "Charlie"];
    const one = names.shift(); // "Alice", names now ["Bob", "Charlie"]
  • F. unshift(): Adds one or more elements to the beginning of an array and returns the new length of the array.
    names.unshift("David"); // names now ["David", "Bob", "Charlie"]
  • G. push(): Adds one or more elements to the end of an array and returns the new length of the array.
    names.push("Eve"); // names now ["David", "Bob", "Charlie", "Eve"]
  • H. pop(): Removes the last element from an array and returns that element. This changes the length of the array.
    const last = names.pop(); // "Eve", names now ["David", "Bob", "Charlie"]
  • I. find(): Returns the value of the first element in the provided array that satisfies the provided testing function.
    let xyz = [5, 6, 8, 4, 7, 9, 8];
    const found = xyz.find(num => num > 2); // Result: 5
  • J. findIndex(): Returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1.
    const index = xyz.findIndex(num => num > 2); // Result: 0

2. Spread Operator (…)

The spread syntax allows an iterable (like an array or string) to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. It also allows an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

  • Copying Arrays (Shallow Copy):
    const arr1 = [1, 2, 3, [100, 200, 300]];
    const arr2 = [...arr1];
    arr2[3][0] = 500; // Note: This is a shallow copy, nested arrays are still referenced.
  • Merging Arrays:
    const mer1 = [1, 2];
    const mer2 = [3, 4];
    const merged = [...mer1, ...mer2]; // Result: [1, 2, 3, 4]

3. Destructuring Assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

  • Array Destructuring:
    const arr = [10, 20, 30];
    const [first, second] = arr; // first = 10, second = 20
  • Object Destructuring:
    const person = { name: "Bob", age: 30 };
    const { name, age } = person; // name = "Bob", age = 30

4. DOM Manipulation

Directly interact with and modify the Document Object Model (DOM) to change content, structure, and style of web pages.

  • Update Text Content:
    document.getElementById("demo").innerText = "Hello, World!";
  • Update HTML Content:
    document.getElementById("demo").innerHTML = "Hello, World!";
  • Modify Styles:
    document.getElementById("box").style.backgroundColor = "blue";

5. Creating & Removing Elements

Dynamically add new elements to the DOM or remove existing ones.

  • Create Element:
    let newElement = document.createElement("p");
    newElement.innerText = "This is a new paragraph.";
    document.body.appendChild(newElement);
  • Remove Element:
    let rm = document.getElementById("removeMe");
    rm.remove();

6. Event Handling (Native JavaScript)

Attach event listeners directly using native JavaScript methods.

document.getElementById("myButton").addEventListener("click", function() {
    alert("Button Clicked!");
});

Summary of File 3: This section covered essential array methods (forEach, map, filter, reduce, shift, unshift, push, pop, find, findIndex), the powerful spread operator for copying and merging, destructuring for cleaner variable assignments, direct DOM manipulation for text, HTML, and styles, dynamic element creation and removal, and native JavaScript event handling.

Next Steps: Delve deeper into JavaScript functions and their advanced concepts.

JavaScript Functions: Scope, Callbacks & Timers

This section explores various ways to define and use functions in JavaScript, including arrow functions, lexical scope, higher-order functions, callbacks, and asynchronous timers.

1. Function Declarations

The traditional way to define a named function in JavaScript. Function declarations are hoisted, meaning they can be called before they are defined in the code.

function myFunc() {
    console.log("Hello World");
}
myFunc();

2. Function Expressions

Functions can also be defined as expressions and assigned to variables. Function expressions are not hoisted in the same way as declarations.

const someFunc = function() {
    console.log("Let's Play");
}
someFunc();

3. Arrow Functions (ES6)

A more concise syntax for writing function expressions, especially useful for short, single-line functions. Arrow functions do not have their own this context.

const sum = (a, b) => a + b;
console.log(sum(4, 5)); // Output: 9

4. Arrow Functions and ‘this’ Context

Arrow functions capture the this value from their surrounding (lexical) context at the time they are created, unlike regular functions which determine this based on how they are called.

const arrowFunc = () => {
    console.log(this); // 'this' refers to the global object (window in browsers) or undefined in strict mode.
}
arrowFunc();

5. Lexical Scope in Objects

Lexical scoping means that the scope of a variable is determined by its position in the source code. Arrow functions are particularly useful within object methods to maintain the correct this context.

const obj = {
    name: "Ayaz",
    coins: 30,
    strength: 20,
    display: function() {
        const lexicalScoping = () => {
            console.log(this.name); // 'this' correctly refers to 'obj' due to arrow function's lexical 'this'.
        }
        lexicalScoping();
    }
};
obj.display(); // Output: "Ayaz"

6. Updating Object Properties

Objects in JavaScript are mutable, and their properties can be easily added, modified, or deleted.

obj.coins += 20;
obj.color = "blue";
obj["hair"] = "yes";
console.log(obj); // obj now has updated coins, and new color and hair properties.

7. Higher-Order Functions (Callbacks)

A higher-order function is a function that takes one or more functions as arguments or returns a function as its result. Callbacks are functions passed as arguments to other functions, to be executed later.

function greeting(func) {
    console.log("Hello");
    func(); // Execute the callback function
}
function bye() {
    console.log("Bye");
}
greeting(bye); // Output: "Hello", then "Bye"

8. Higher-Order Functions with Arrow Callbacks

Combining higher-order functions with arrow functions for concise callback definitions.

function greeting(func, name) {
    console.log("Hello " + name);
    func();
}
greeting(() => {
    console.log("Bye");
}, "Ayaz"); // Output: "Hello Ayaz", then "Bye"

9. Timers (Asynchronous Execution)

JavaScript provides built-in functions to execute code after a specified delay or at regular intervals, enabling asynchronous operations.

A. setTimeout()

Executes a function or specified piece of code once after a delay.

setTimeout(() => {
    console.log("Time starts now");
}, 2000); // Executes after 2000 milliseconds (2 seconds)

B. setTimeout() Example in Flow

Demonstrates how setTimeout is non-blocking and allows other code to execute first.

console.log("Start");
setTimeout(() => {
    console.log("This runs after 3 seconds");
}, 3000);
console.log("End");
// Output: "Start", "End", then (after 3 seconds) "This runs after 3 seconds"

Summary of File 4: This section covered function declarations, expressions, and arrow functions, emphasizing their differences in this binding and lexical scope. It also introduced higher-order functions and callbacks as fundamental patterns for asynchronous JavaScript, along with the use of setTimeout for delayed execution.

Next Steps: Reinforce function concepts and explore more object interactions.

JavaScript Functions & Objects: Core Concepts

This section reiterates and reinforces fundamental JavaScript concepts related to functions, arrow functions, object manipulation, higher-order functions, and timers, building upon previous discussions.

Note: This section is largely similar to the previous “JavaScript Functions: Scope, Callbacks & Timers” section, serving as a reinforcement or slightly different perspective on these core topics.

1. Basic Functions

Review of different ways to define functions.

  • Declaration:
    function myFunc() {
        console.log("Hello World");
    }
    myFunc();
  • Expression:
    const someFunc = function() {
        console.log("Let's Play");
    }
    someFunc();
  • Arrow:
    const sum = (a, b) => a + b;
    console.log(sum(4, 5)); // Output: 9

2. Arrow Functions and ‘this’ Context

Understanding how arrow functions handle the this keyword lexically.

const arrowFunc = () => {
    console.log(this); // Refers to the surrounding lexical context.
}
arrowFunc();

3. Lexical Scope in Objects

How arrow functions within object methods preserve the object’s this context.

const obj = {
    name: "Ayaz",
    coins: 30,
    strength: 20,
    display: function() {
        const lexicalScoping = () => {
            console.log(this.name);
        }
        lexicalScoping();
    }
};
obj.display(); // Output: "Ayaz"

4. Updating Object Properties

Adding and modifying properties of JavaScript objects.

obj.coins += 20;
obj.color = "blue";
obj["hair"] = "yes";
console.log(obj);

5. Higher-Order Functions

Functions that operate on other functions, either by taking them as arguments or by returning them.

function greeting(func) {
    console.log("Hello");
    func();
}
function bye() {
    console.log("Bye");
}
greeting(bye);

6. Higher-Order Functions with Arrow Callbacks

Using arrow functions as concise callbacks in higher-order functions.

function greeting(func, name) {
    console.log("Hello " + name);
    func();
}
greeting(() => {
    console.log("Bye");
}, "Ayaz");

7. Timers

Asynchronous execution using setTimeout.

  • Basic setTimeout:
    setTimeout(() => {
        console.log("Time starts now");
    }, 2000);
  • setTimeout Execution Flow:
    console.log("Start");
    setTimeout(() => {
        console.log("This runs after 3 seconds");
    }, 3000);
    console.log("End");

Summary of File 5: This section reinforced the understanding of various function types, the behavior of this with arrow functions, lexical scope within objects, dynamic object property updates, higher-order functions with both traditional and arrow callbacks, and the use of setTimeout for asynchronous operations.

Next Steps: Conclude with practical DOM manipulation and CSS toggling.

DOM Interaction: Selectors, Events & CSS Toggling

This final section focuses on practical DOM manipulation using modern JavaScript, including element selection, event handling, and dynamic CSS class management for interactive user interfaces.

1. Selecting Elements with Query Selector

The document.querySelector() method returns the first element within the document that matches the specified selector (group of selectors).

let btn = document.querySelector('.btn');
let body = document.body; // Direct access to the body element

2. Event Listener for User Interaction

Attach an event listener to an element to respond to user actions, such as clicks.

btn.addEventListener('click', () => {
    // Code to execute on click
});

3. Toggling Classes for Dynamic Styles

Dynamically add, remove, or replace CSS classes on elements to change their appearance or behavior. This is a common pattern for implementing features like dark mode.

if (body.classList.contains('bdy')) {
    body.classList.replace('bdy', 'newBdy');
} else {
    body.classList.replace('newBdy', 'bdy');
}

Explanation: Initially, if the <body> element has the class 'bdy', it will be replaced with 'newBdy' on the first click. On subsequent clicks, it will toggle between 'newBdy' and 'bdy'.

Example Usage for Dark Mode:

HTML:

<button class="btn">Toggle Theme</button>

CSS:

.bdy {
    background-color: white;
    color: black;
}
.newBdy {
    background-color: black;
    color: white;
}

Summary of File 6: This section demonstrated how to select DOM elements using querySelector, attach event listeners for interactivity, and manipulate CSS classes with classList.contains() and classList.replace() to dynamically toggle styles, such as for a dark mode feature.