INTERVIEW PREP:
Pure Function:
1. Deterministic : that is for same input it produces the same output
For example
Function add (x,y) {
return x+y;
For same input it is going to produce the same output .
2 . pure function does not modify any external state, such as:
Changing global variables.
Modifying objects or arrays passed as arguments.
Performing I/O operations (e.g., logging to the console, making
network requests).
Mutating the DOM.
To make a function pure we can use shallow copy of array if arr is one
argument of that function.
Higher-Order Functions:
A higher-order function (HOF) is a function that either:
1. Takes one or more functions as arguments, or
2. Returns a function.
1. Function that accepts another function as an argument:
In this example, the function greet() takes another function greeting as an
argument and executes it.
function greet(name, greetingFunction) {
return greetingFunction(name);
function sayHello(name) {
return `Hello, ${name}!`;
function sayGoodbye(name) {
return `Goodbye, ${name}!`;
console.log(greet('Alice', sayHello)); // "Hello, Alice!"
console.log(greet('Bob', sayGoodbye)); // "Goodbye, Bob!"
2. Function that returns another function:
Here, we return a function from within another function.
For example,
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
const multiplyByTwo = multiplyBy(2);
const multiplyByThree = multiplyBy(3);
console.log(multiplyByTwo(5)); // 10
console.log(multiplyByThree(5)); // 15
Note :
A callback function is indeed the function that is passed as an argument
to another function. The higher-order function is the one that takes that
callback function as a parameter and can execute it or use it in some
way.
Function Currying :
Currying is a functional programming technique where a
function that takes multiple arguments is transformed
into a sequence of functions, each taking a single
argument. This allows for partial application of the
function, where you can fix some arguments and
generate a new function that takes the remaining
arguments.
For example:
// Non-curried function
function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // Output: 6
// Curried version of the same function
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(curriedAdd(1)(2)(3)); // Output: 6
// Partial application
const addOne = curriedAdd(1);
const addOneAndTwo = addOne(2);
console.log(addOneAndTwo(3)); // Output: 6
IMMUTABILITY AND SIDE EFFECTS :
Immutable – cannot be changes in this you make sure
that the array or object passed doesnot changes so we
create new copy but as we know array and objects are
passed by reference so simple copy will not work
mutable: can be changed
Some ways to make array and objects immutable :
Arrow Function :
Arrow functions, introduced in ES6 (ECMAScript 2015), are a
concise way to write functions in JavaScript. They provide a
shorter syntax compared to traditional function expressions and
also handle the this keyword differently. Here's a breakdown of
arrow functions:
See exmples of how to write arrow functions.
In map and reduce the callback function this is passed is
also as arrow functions.
This binding in arrow functions - this topic not done yet
No arguments Object
Arrow functions do not have their own arguments object. If you need to access arguments,
use the rest parameter (...args).
Example:
javascript
Copy
const traditional = function() {
console.log(arguments); // Works
};
const arrow = () => {
console.log(arguments); // ReferenceError: arguments is not defined
};
const arrowWithRest = (...args) => {
console.log(args); // Works
};
Scopes in js :
var can in redeclared in same scope
var is globally scoped that means if you declare inside a block it can
be assessable in outer that block also **if you redeclare it then
latest value will be shown**
example :
var car="ford";
console.log(car); //ford
console.log(car); //ford
console.log(car);//ford
example 2:
{
var car="ford";
console.log(car); //ford
var car="nissan";
console.log(car); //nissan
var car="ferrari";
console.log(car);//ferrari
example 3:
var car="ford";
// console.log(car); //ford
// {
// var car="nissan";
// console.log(car); //nissan
// }
console.log(car); Nissan in redeclaring case the latest var will be declared is having
effect
you cannot redeclare variables declared with let or const in
the same scope. Attempting to do so will result in a SyntaxError.
For example:
let car = "ford";
let car = "nissan"; // This is allowed because it's in a different scope
console.log(car); // Output: "nissan"
console.log(car); // Output: "ford"
Let and const are not accessible outside the block but for inside
block it is accessible but if you redeclare the variable in inside
block the outer block let and var will be overshadow same goes
for let
**For example:**
const car = "ford";
console.log(car); here there is error as const car is defined in this scope
but if const car =”nisaan” is removed will get ford and form all the three
console,log
const car = "nissan";
console.log(car); // Output: "nissan" (inner scope)
console.log(car); // Output: "ford" (outer scope)
few more examples
example :
const car = "ford";
const car="farrari";
console.log(car); //ferrari
const car = "nissan";
console.log(car); // Output: "nissan" (inner scope)
console.log(car);//ferrari
console.log(car); // Output: "ford" (outer scope)
example 2 : very imp scope concept
let car = "ford";
{
let car="farrari";
console.log(car); //ferrari
car = "nissan";
console.log(car); // Output: "nissan"
console.log(car);// output :nissan till here there is nissan because
here you have declared this variable
console.log(car); // Output: "ford" (outer scope)
example 3:
let car = "ford";
let car="farrari";
console.log(car); //ferrari
let car="bmw";
console.log(car); //bmw
car = "nissan";
console.log(car); // Output: "nissan"
console.log(car);//ferrari
}
console.log(car); // Output: "ford" (outer scope)
so assigning a let and const will change its value up to the outer scope where for first
time it is declared .
for example :
let car = "ford";
console.log(car);//ford
car="bmw";
console.log(car); //bmw
car = "nissan";
console.log(car); // Output: "nissan"
console.log(car);//nissan
console.log(car); // Output:nissan
in case of var :
var car = "ford";
console.log(car);//ford
car="bmw";
console.log(car); //bmw
car = "nissan";
console.log(car); // Output: "nissan"
console.log(car);//nissan
}
console.log(car); // Output:Nissan
example : for var
var car = "ford";
var car="farrari";
console.log(car); //ferrari
car = "nissan";
console.log(car); // Output: "nissan"
console.log(car);// output :nissan till here there is nissan because here you have
declared this variable
console.log(car); // Output: nissan
a good example :
Yes, in your code, all three var car declarations refer to the same memory
location. This is because the var keyword is function-scoped, not block-scoped. When
you declare a variable with var, it is hoisted to the top of its function or global scope,
and all references to that variable name within the same scope point to the same
memory location.
Let’s break down your code step by step to understand why this happens:
Code:
javascript
Copy
var car = "ford";
console.log(car); // Output: "ford"
var car = "bmw";
console.log(car); // Output: "bmw"
var car = "nissan";
console.log(car); // Output: "nissan"
console.log(car); // Output: "nissan"
console.log(car); // Output: "nissan"
Explanation:
1. var car = "ford";
o Declares a global variable car and assigns it the value "ford".
o This variable is hoisted to the top of the global scope.
2. First console.log(car);
o Outputs the value of the global car: "ford".
3. Inside the first block {}:
o var car = "bmw";
This reassigns the global car to "bmw".
(Note: This is not a new variable; it’s the same global car being
reassigned.)
o console.log(car);
Outputs the updated value of the global car: "bmw".
4. Inside the nested block {}:
o var car = "nissan";
This reassigns the global car to "nissan".
(Again, this is the same global car being reassigned.)
o console.log(car);
Outputs the updated value of the global car: "nissan".
5. Back in the outer block {}:
o console.log(car);
Outputs the value of the global car: "nissan".
6. Outside all blocks:
o console.log(car);
Outputs the value of the global car: "nissan".
Why Do All var car Refer to the Same Memory Location?
var is Function-Scoped, Not Block-Scoped:
o The var keyword does not respect block scope ({}). It is only scoped to
the function or global scope.
o In your code, there is no function, so all var car declarations are part of
the global scope.
Hoisting:
o All var declarations are hoisted to the top of their scope. This means that
the variable car is declared at the top of the global scope, and all
assignments to car modify the same variable.
Reassignment:
o Every time you write var car = ..., you are reassigning the same
global car variable, not creating a new one.
But if it is let /const
let car = "ford"; console.log(car);//ford { let car="bmw"; console.log(car);
//bmw { let car = "nissan"; console.log(car); // Output: "nissan" }
console.log(car);//bmw } console.log(car); // Output:ford so here three
different let efines three different memory locations
ChatGPT said:
Yes, you're absolutely right! In JavaScript, let creates block-scoped variables.
This means that each time you declare a new let variable in a new block (or
nested block), it creates a separate memory location for that variable, even if
they share the same name.
Here's a breakdown of what happens in your code:
1. Global Scope (let car = "ford";):
o You declare a variable car with the value "ford" in the global
scope. This variable exists in memory at this level.
2. First Block Scope (let car = "bmw";):
o Inside the first block {}, you declare a new variable car with the
value "bmw". This car is different from the one declared in the
global scope because it's scoped within this block. It's stored in a
different memory location.
3. Second Block Scope (let car = "nissan";):
o Inside the nested block {}, you declare another car variable with
the value "nissan". Again, this is a completely different variable
from the ones in the previous two blocks and exists in its own
memory space, scoped to this block.
4. Outside the Blocks (console.log(car);):
o Once you exit the blocks, the outer car variables are still
accessible, but the inner car variables are no longer in scope. So,
the second console.log(car) prints "bmw", and the third one prints
"ford" (the global car).
note imp example **
var car ='nissan';
console.log(car); //nissan;
var car="audi";
console.log(car);// output:audi ||no new memory space is ccreated it is
similar to assigning car the value audi now for all below console if
reassignment and redeclaration is not done for variable car it will be audi
only
console.log(car); //audi
why this happened?
so in case of let and const different memory is allocated to each
redeclaration in new scope but in var only one memory is located for
same name and if you are redeclaring it then it becomes same as
reassignment .
function scopes :
The scope of a variable is determined by where it is written in the
code, not by where the function is called.
For example:
Good example var is function scoped :
Let’s break down the code step by step to understand how it works and
why the outputs are what they are. The key concept here is variable
scope in JavaScript, specifically how the var keyword behaves in
different scopes.
Code:
javascript
Copy
var one = 10;
console.log(one); // 10
function fun1() {
var one = 20;
console.log(one); // 20
function fun2() {
var one = 30;
console.log(one); // 30
fun2();
console.log(one); // 20
fun1();
console.log(one); // 10
Explanation:
1. Global Scope
var one = 10;
Declares a global variable one and assigns it the value 10.
console.log(one);
Outputs the value of the global one: 10.
2. Inside fun1
var one = 20;
Declares a new variable one that is local to fun1. This one is
separate from the global one.
console.log(one);
Outputs the value of the local one in fun1: 20.
3. Inside fun2 (nested inside fun1)
var one = 30;
Declares a new variable one that is local to fun2. This one is
separate from both the global one and the one in fun1.
console.log(one);
Outputs the value of the local one in fun2: 30.
4. Back in fun1
After fun2 finishes execution, control returns to fun1.
console.log(one);
Outputs the value of the local one in fun1: 20.
(The one in fun2 does not affect the one in fun1 because they
are separate variables.)
5. Back in Global Scope
After fun1 finishes execution, control returns to the global scope.
console.log(one);
Outputs the value of the global one: 10.
(The one in fun1 and fun2 do not affect the global one because
they are separate variables.)
Next Example :
let x = 10;
function outer() {
let x = 20;
inner();
function inner() {
console.log(x); // Output: 10 (lexical scoping)
outer();
Scope Chain Behavior for let, const, and var:
1. Scope Chain Moves Outward for All:
o Whether you use var, let, or const, the scope chain
always moves outward (from the current scope to the
outer scope) to resolve variables.
o It never moves inward (into child scopes).
2. Example for let and const:
let globalVar = "I am global";
function outer() {
let outerVar = "I am outer";
function inner() {
let innerVar = "I am inner";
console.log(globalVar); // Accessible: "I am global"
console.log(outerVar); // Accessible: "I am outer"
console.log(innerVar); // Accessible: "I am inner"
inner();
console.log(innerVar); // ReferenceError: innerVar is not defined
}
outer();
o Here, innerVar is declared in the inner function's scope.
The outer function cannot access innerVar because the
scope chain does not move inward.
3. Example for var:
var globalVar = "I am global";
function outer() {
var outerVar = "I am outer";
function inner() {
var innerVar = "I am inner";
console.log(globalVar); // Accessible: "I am global"
console.log(outerVar); // Accessible: "I am outer"
console.log(innerVar); // Accessible: "I am inner"
inner();
console.log(innerVar); // ReferenceError: innerVar is not
defined
outer();
o Even with var, the outer function cannot
access innerVar because the scope chain does not
move inward.
Conclusion:
The scope chain always moves outward (from inner to outer
scopes) for all variable declarations (var, let, and const).
It never moves inward (into child scopes), regardless of
whether you use var, let, or const.
Inner function has all the access of outer function even if the
outer function has executed then also variable but outer function
has no access to inner function scope
Example :
function outer() {
let outerVar = "I am outer";
function inner() {
console.log(outerVar); // Accessible: "I am outer"
return inner;
const innerFunc = outer(); // outer() has finished executing
innerFunc(); // Output: "I am outer"
example :
function outer() {
function inner() {
let innerVar = "I am inner";
inner();
console.log(innerVar); // ReferenceError: innerVar is not defined
outer();
Var is function scoped lets see by example :
in JavaScript, variables declared with var are function-scoped, not
block-scoped. This means that if you declare a variable with var inside a
block (e.g., inside an if statement, for loop, or any other block {}), it is
accessible outside the block as well, as long as it is within the same
function or global scope.
Example: var Inside a Block
if (true) {
var x = 10; // Declared with var
console.log(x); // Output: 10
console.log(x); // Output: 10 (accessible outside the block)
In this example, the variable x is declared with var inside the if block, but
it is accessible outside the block because var is not block-scoped.
Comparison with let and const
Variables declared with let and const are block-scoped, meaning
they are confined to the block in which they are declared and are
not accessible outside the block.
if (true) {
let y = 20; // Declared with let
const z = 30; // Declared with const
console.log(y); // Output: 20
console.log(z); // Output: 30
console.log(y); // Error: y is not defined
console.log(z); // Error: z is not defined
Here, y and z are block-scoped and cannot be accessed outside
the if block.
var in Function Scope
While var is not block-scoped, it is function-scoped. This means that if
you declare a variable with var inside a function, it is not accessible
outside the function.
function myFunction() {
if (true) {
var a = 50; // Declared with var
console.log(a); // Output: 50
console.log(a); // Output: 50 (accessible within the function)
myFunction();
console.log(a); // Error: a is not defined
Function Scopes :
In JavaScript, scope determines the visibility and
accessibility of variables, functions, and objects
in your code. Function scope is one of the key
types of scope in JavaScript, and it is created
when a function is defined. Variables declared
inside a function are local to that function and
cannot be accessed outside of it.
Key Concepts of Function Scope
1.Local Variables:
Variables declared inside a function
are local to that function. They are only
accessible within the function and are
destroyed when the function finishes
execution.
For example:
function myFunction() {
let localVar = "I am local"; // same goes for var as var is not blocked scope
(you know )
console.log(localVar); // Works fine
myFunction();
console.log(localVar); // Error: localVar is not defined
Global Variables:
Variables declared outside of any function are global and can be accessed from
anywhere in the code, including inside functions.
let globalVar = "I am global";
function myFunction() {
console.log(globalVar); // Works fine
myFunction();
console.log(globalVar); // Works fine
1. Block Scope vs Function Scope:
o Variables declared with var are function-scoped, meaning they are
only confined to the function in which they are declared.
o Variables declared with let and const are block-scoped, meaning
they are confined to the block (e.g., {}) in which they are declared.
function myFunction() {
if (true) {
var varVar = "I am function-scoped";
let letVar = "I am block-scoped";
}
console.log(varVar); // Works fine
console.log(letVar); // Error: letVar is not defined
myFunction();
2. Nested Function Scopes:
Functions can be nested inside other functions, creating a scope chain.
Inner functions have access to variables in their own scope, as well as in
the outer (enclosing) function's scope.
function outerFunction() {
let outerVar = "I am outer";
function innerFunction() {
let innerVar = "I am inner";
console.log(outerVar); // Works fine
innerFunction();
console.log(innerVar); // Error: innerVar is not defined
outerFunction();
3. Lexical Scoping:
JavaScript uses lexical scoping, meaning the scope of a variable is
determined by its position in the source code. Inner functions have access
to variables in their outer (enclosing) functions, even after the outer
function has finished executing.
javascript
Copy
function outerFunction() {
let outerVar = "I am outer";
function innerFunction() {
console.log(outerVar); // Works fine
return innerFunction;
const inner = outerFunction();
inner(); // Output: "I am outer"
Variable Shadowing
If a variable in a local scope has the same name as a variable in
an outer scope, the local variable shadows the outer variable.
The outer variable is temporarily inaccessible within the local
scope.
Example :
let globalVar = "I am global";
function myFunction() {
let globalVar = "I am local";
console.log(globalVar); // Output: "I am local"
myFunction();
console.log(globalVar); // Output: "I am global"
Hoisting in Function Scope
Variables declared with var are hoisted to the top of their function
scope, meaning they are accessible (but undefined) before the line
where they are declared.
Variables declared with let and const are also hoisted but are not
initialized, resulting in a temporal dead zone until the declaration
is reached.
function myFunction() {
console.log(hoistedVar); // Output: undefined
var hoistedVar = "I am hoisted";
console.log(notHoisted); // Error: Cannot access 'notHoisted' before
initialization
let notHoisted = "I am not hoisted";
myFunction();
Objects in js Deep vs shallow Copy
In JavaScript, when working with objects, it's important
to understand the difference between shallow
copy and deep copy. These concepts determine how
objects are copied and whether changes to the copied
object affect the original object (and vice versa).
Shallow Copy
A shallow copy of an object creates a new object and
copies the top-level properties of the original object.
However, if the property values are objects (e.g., arrays,
nested objects), the references to those objects are
copied, not the objects themselves. This means that
changes to nested objects in the copied object will
affect the original object, and vice versa.
Methods to Create a Shallow Copy
1. Using the Spread Operator (...):
javascript
Copy
let original = { a: 1, b: { c: 2 } };
let shallowCopy = { ...original };
shallowCopy.a = 10; // Changes only the shallow copy
shallowCopy.b.c = 20; // Changes the nested object in both original and shallow
copy
console.log(original); // Output: { a: 1, b: { c: 20 } }
console.log(shallowCopy); // Output: { a: 10, b: { c: 20 } }
2. Using Object.assign():
let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original);
shallowCopy.a = 10; // Changes only the shallow copy
shallowCopy.b.c = 20; // Changes the nested object in both original and shallow
copy
console.log(original); // Output: { a: 1, b: { c: 20 } }
console.log(shallowCopy); // Output: { a: 10, b: { c: 20 } }
3. Using Array.prototype.slice() (for arrays):
let originalArray = [1, 2, { a: 3 }];
let shallowCopyArray = originalArray.slice();
shallowCopyArray[0] = 10; // Changes only the shallow copy
shallowCopyArray[2].a = 30; // Changes the nested object in both original and
shallow copy
console.log(originalArray); // Output: [1, 2, { a: 30 }]
console.log(shallowCopyArray); // Output: [10, 2, { a: 30 }]
Deep Copy
A deep copy of an object creates a new object and
recursively copies all nested objects as well. This means
that changes to nested objects in the copied object do
not affect the original object, and vice versa.
Methods to Create a Deep Copy
1. Using JSON.parse() and JSON.stringify():
This method works for objects that do not contain
functions, undefined, or special objects
like Date, RegExp, etc.
For example:
let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.a = 10; // Changes only the deep copy
deepCopy.b.c = 20; // Changes only the deep copy
console.log(original); // Output: { a: 1, b: { c: 2 } }
console.log(deepCopy); // Output: { a: 10, b: { c: 20 } }
Limitations:
o Does not work with functions, undefined, or special
objects.
o Loses the prototype chain.
2. Using a Recursive Function:
You can write a custom function to recursively copy
all properties and nested objects.
For example:
function deepCopy(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
let copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
return copy;
let original = { a: 1, b: { c: 2 } };
let deepCopy = deepCopy(original);
deepCopy.a = 10; // Changes only the deep copy
deepCopy.b.c = 20; // Changes only the deep copy
console.log(original); // Output: { a: 1, b: { c: 2 } }
console.log(deepCopy); // Output: { a: 10, b: { c: 20 } }
3. Using Libraries:
Libraries like Lodash provide utility functions for
deep copying.
For example:
let _ = require("lodash");
let original = { a: 1, b: { c: 2 } };
let deepCopy = _.cloneDeep(original);
deepCopy.a = 10; // Changes only the deep copy
deepCopy.b.c = 20; // Changes only the deep copy
console.log(original); // Output: { a: 1, b: { c: 2 } }
console.log(deepCopy); // Output: { a: 10, b: { c: 20 } }
Events in js :
In JavaScript, events are actions or occurrences that happen in the
browser, such as a user clicking a button, a page finishing loading, or an
input field being changed. JavaScript allows you to listen for these
events and execute code in response, enabling interactive and dynamic
web applications.
### Key Concepts in JavaScript Events
1. **Event Types**:
- There are many types of events in JavaScript, such as:
- **Mouse Events**: `click`, `dblclick`, `mouseover`, `mouseout`,
`mousedown`, `mouseup`, `mousemove`
- **Keyboard Events**: `keydown`, `keyup`, `keypress`
- **Form Events**: `submit`, `change`, `focus`, `blur`, `input`
- **Window Events**: `load`, `resize`, `scroll`, `unload`
- **Touch Events**: `touchstart`, `touchend`, `touchmove`
- **Custom Events**: You can create your own custom events using
the `Event` or `CustomEvent` constructor.
2. **Event Target**:
- The element or object that the event occurs on is called the **event
target**. For example, if a user clicks a button, the button is the event
target.
3. **Event Listeners**:
- An event listener is a function that waits for a specific event to occur
on a target element. You can attach event listeners using the
`addEventListener()` method.
- Syntax:
```javascript
element.addEventListener(eventType, callbackFunction,
useCapture);
```
- `eventType`: The type of event to listen for (e.g., `"click"`,
`"keydown"`).
- `callbackFunction`: The function to execute when the event occurs.
- `useCapture` (optional): A boolean indicating whether to use event
capturing (default is `false`, which means bubbling).
4. **Event Object**:
- When an event occurs, the browser creates an **event object**
containing details about the event. This object is passed as an
argument to the event handler function.
- Common properties and methods of the event object:
- `event.target`: The element that triggered the event.
- `event.type`: The type of event (e.g., `"click"`).
- `event.preventDefault()`: Prevents the default behavior of the
event (e.g., preventing a form submission).
- `event.stopPropagation()`: Stops the event from propagating
further in the DOM (bubbling or capturing).
5. **Event Propagation**:
- Events in JavaScript propagate through the DOM in three phases:
- **Capturing Phase**: The event travels from the root of the DOM
tree down to the target element.
- **Target Phase**: The event reaches the target element.
- **Bubbling Phase**: The event bubbles up from the target element
back to the root.
- You can control event propagation using `event.stopPropagation()`
or by setting the `useCapture` parameter in `addEventListener()`.
6. **Inline Event Handlers**:
- You can also define event handlers directly in HTML using inline
attributes like `onclick`, `onload`, etc. However, this approach is
generally discouraged because it mixes HTML and JavaScript, making
the code harder to maintain.
- Example:
```html
<button onclick="alert('Button clicked!')">Click Me</button>
```
7. **Removing Event Listeners**:
- You can remove an event listener using the `removeEventListener()`
method. It requires the same event type and callback function used in
`addEventListener()`.
- Example:
```javascript
function handleClick() {
console.log('Button clicked!');
element.addEventListener('click', handleClick);
element.removeEventListener('click', handleClick);
```
8. **Event Delegation**:
- Event delegation is a technique where you attach a single event
listener to a parent element to handle events for multiple child
elements. This is useful for dynamically added elements or improving
performance.
- Example:
```javascript
document.getElementById('parent').addEventListener('click',
function(event) {
if (event.target.tagName === 'BUTTON') {
console.log('Button clicked:', event.target.textContent);
});
```
### Example: Handling a Click Event
```html
<button id="myButton">Click Me</button>
<p id="output"></p>
<script>
// Get the button element
const button = document.getElementById('myButton');
const output = document.getElementById('output');
// Add an event listener for the click event
button.addEventListener('click', function(event) {
// Prevent default behavior (if any)
event.preventDefault();
// Update the output text
output.textContent = 'Button was clicked!';
// Log event details
console.log('Event type:', event.type);
console.log('Target element:', event.target);
});
</script>
```
### Custom Events
You can create and dispatch custom events using the `Event` or
`CustomEvent` constructor.
```javascript
// Create a custom event
const customEvent = new CustomEvent('myCustomEvent', {
detail: { message: 'This is a custom event' }
});
// Listen for the custom event
document.addEventListener('myCustomEvent', function(event) {
console.log('Custom event triggered:', event.detail.message);
});
// Dispatch the custom event
document.dispatchEvent(customEvent);
```
### Summary
- Events are actions or occurrences in the browser that JavaScript can
respond to.
- Use `addEventListener()` to attach event handlers to elements.
- The event object provides details about the event and methods to
control its behavior.
- Event propagation occurs in three phases: capturing, target, and
bubbling.
- Event delegation is a powerful technique for handling events
efficiently.
- Custom events allow you to create and dispatch your own events.
Understanding events is crucial for building interactive and responsive
web applications in JavaScript.
EVENT OBJECT:
The **event object** in JavaScript is a special object that is automatically
created by the browser whenever an event occurs. It contains detailed
information about the event, such as the type of event, the target element,
mouse position, key pressed, and more. This object is passed as an argument
to the event handler function (callback) by default, so you don't need to
explicitly pass it.
---
### **What is the Event Object?**
The event object is a built-in object in JavaScript that provides information
about the event and methods to control its behavior. For example:
- `event.type`: The type of event (e.g., `"click"`, `"keydown"`).
- `event.target`: The element that triggered the event.
- `event.preventDefault()`: Prevents the default behavior of the event (e.g.,
preventing a form submission).
- `event.stopPropagation()`: Stops the event from propagating further in the
DOM (bubbling or capturing).
---
### **How is the Event Object Passed?**
1. **Automatically Passed**:
- When you attach an event listener using `addEventListener()`, the browser
automatically passes the event object as the **first argument** to the event
handler function.
- You don't need to explicitly pass it; it happens by default.
Example:
```javascript
const button = document.querySelector('button');
button.addEventListener('click', function(event) {
// The event object is automatically passed as the first argument
console.log(event); // Logs the event object
console.log(event.type); // Logs "click"
console.log(event.target); // Logs the button element
});
```
2. **Accessing the Event Object**:
- You can access the event object by defining a parameter in your event
handler function. By convention, this parameter is often named `event`, `evt`,
or `e`.
Example:
```javascript
button.addEventListener('click', function(e) {
console.log(e); // Logs the event object
});
```
---
### **Do You Need to Explicitly Pass the Event Object?**
No, you **do not need to explicitly pass the event object**. The browser
automatically creates and passes it to the event handler function when the
event occurs. However, you must define a parameter in your event handler
function to access it.
---
### **How to Pass the Event Object Manually (Advanced Use Cases)**
In most cases, you don't need to manually pass the event object because the
browser does it for you. However, if you're calling a function manually and
want to simulate an event, you can create and pass an event object using the
`Event` or `CustomEvent` constructor.
#### Example: Manually Creating and Passing an Event Object
```javascript
// Create a custom event
const customEvent = new CustomEvent('myEvent', {
detail: { message: 'This is a custom event' }
});
// Define an event handler function
function handleEvent(event) {
console.log('Event type:', event.type); // Logs "myEvent"
console.log('Event detail:', event.detail.message); // Logs "This is a custom
event"
// Manually call the function and pass the event object
handleEvent(customEvent);
```
---
### **Key Points About the Event Object**
1. **Automatically Passed**:
- The event object is automatically passed to the event handler function by
the browser when an event occurs.
2. **Accessing the Event Object**:
- Define a parameter in your event handler function to access the event
object.
3. **Properties and Methods**:
- The event object contains properties like `event.type`, `event.target`, and
methods like `event.preventDefault()` and `event.stopPropagation()`.
4. **Custom Events**:
- You can create and dispatch custom events using the `Event` or
`CustomEvent` constructor, and manually pass the event object if needed.
---
### **Example: Using the Event Object in a Real-World Scenario**
```html
<form id="myForm">
<input type="text" id="username" placeholder="Enter your username">
<button type="submit">Submit</button>
</form>
<p id="message"></p>
<script>
const form = document.getElementById('myForm');
const message = document.getElementById('message');
form.addEventListener('submit', function(event) {
// Prevent the form from submitting
event.preventDefault();
// Access the input value
const username = document.getElementById('username').value;
// Display a message
if (username) {
message.textContent = `Hello, ${username}!`;
} else {
message.textContent = 'Please enter a username.';
});
</script>
```
In this example:
- The `event` object is automatically passed to the event handler function.
- `event.preventDefault()` is used to stop the form from submitting.
- The `event` object is not explicitly passed; it is provided by the browser.
---
### **Summary**
- The **event object** is automatically created by the browser and passed to
the event handler function when an event occurs.
- You don't need to explicitly pass it; it is provided as the first argument to the
event handler.
- You can access the event object by defining a parameter in your event
handler function.
- Use the event object to get information about the event and control its
behavior (e.g., prevent default actions or stop propagation).
Event propagation in js : three phases are
there?
1. Capturing - This is the first phase in the event propagation process. When an
event is triggered, it starts from the root of the DOM tree and then moves
down towards the target element. By default, event handlers are not executed
in this phase.
2. Targeting - This phase starts once the event reaches the element where the
event was triggered. Any event handlers associated with the target element
for the particular event get executed in this phase.
3. Bubbling - After targeting, the event traces back its path and moves back up
to the root of the DOM. It is in this phase that the event handlers execute.
JavaScript executes the associated event handlers in order as the event moves
up the DOM tree.
As you must have understood, to make our example work, we must stop the
propagation once the event handler for the button executes. We have two
utilities for this
- event.stopPropagation() and event.stopImmediatePropagation(). Both of
them block the propagation of the event once encountered. There is only one
difference in their work.
The event.stopPropagation() stops the event from going to the next element.
All handlers associated with the current element for that particular event will
still get executed. This does not happen
in event.stopImmediatePropagation() where propagation stops immediately,
and no event handlers execute even if they belong to the current element.
With this knowledge, let's update our solution.
Where I should write this event.stopPropagation( )??
You should write event.stopPropagation() in the place (event handler) from
where you want to stop the event from propagating further to its parent or
ancestor elements. This ensures that the event does not bubble up the DOM
tree beyond that point.
Great question! Let’s clarify how the **`useCapture`** parameter works in
`addEventListener()` and whether you need to add separate event listeners for
the **capturing** and **bubbling** phases.
---
### **What is `useCapture`?**
The `useCapture` parameter in `addEventListener()` determines **when** the
event handler is executed:
- If `useCapture` is **`true`**, the event handler is executed during the
**capturing phase** only not the bubbling phase .
- If `useCapture` is **`false`** (default), the event handler is executed during
the **bubbling phase** only and not the capturing phase.
---
### **Key Points About `useCapture`**
1. **Capturing Phase (`useCapture = true`)**:
- The event handler is triggered as the event travels **down** the DOM tree
(from the root to the target element).
- This phase happens before the **target phase**.
2. **Bubbling Phase (`useCapture = false`)**:
- The event handler is triggered as the event travels **up** the DOM tree
(from the target element back to the root).
- This phase happens after the **target phase**.
3. **Target Phase**:
- The event handler is triggered when the event reaches the **target
element**.
- Event handlers are executed **regardless** of the `useCapture` value.
---
### **Do You Need Separate Event Listeners for Capturing and Bubbling?**
Yes, if you want to handle the event in **both phases** (capturing and
bubbling), you need to add **two separate event listeners**:
- One with `useCapture = true` (for the capturing phase).
- One with `useCapture = false` (for the bubbling phase).
---
### **Example: Handling Events in Both Phases**
Let’s use an example to demonstrate this:
```html
<div id="grandparent">
Grandparent
<div id="parent">
Parent
<div id="child">
Child
</div>
</div>
</div>
<script>
// Get references to the elements
const grandparent = document.getElementById('grandparent');
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// Add event listeners for the capturing phase (useCapture = true)
grandparent.addEventListener('click', function(event) {
console.log('Grandparent (Capturing)');
}, true); // useCapture = true
parent.addEventListener('click', function(event) {
console.log('Parent (Capturing)');
}, true); // useCapture = true
child.addEventListener('click', function(event) {
console.log('Child (Capturing)');
}, true); // useCapture = true
// Add event listeners for the bubbling phase (useCapture = false)
grandparent.addEventListener('click', function(event) {
console.log('Grandparent (Bubbling)');
}, false); // useCapture = false
parent.addEventListener('click', function(event) {
console.log('Parent (Bubbling)');
}, false); // useCapture = false
child.addEventListener('click', function(event) {
console.log('Child (Bubbling)');
}, false); // useCapture = false
</script>
```
---
### **What Happens When You Click the "Child" Element?**
1. **Capturing Phase**:
- The event travels **down** the DOM tree (from `grandparent` to `parent` to
`child`).
- Event listeners with `useCapture = true` are triggered.
- Output:
```
Grandparent (Capturing)
Parent (Capturing)
Child (Capturing)
```
2. **Target Phase**:
- The event reaches the **target element** (`child`).
- Both capturing and bubbling event listeners on the target element are
triggered.
- Output:
```
Child (Capturing)
Child (Bubbling)
```
3. **Bubbling Phase**:
- The event travels **up** the DOM tree (from `child` to `parent` to
`grandparent`).
- Event listeners with `useCapture = false` are triggered.
- Output:
```
Parent (Bubbling)
Grandparent (Bubbling)
```
---
### **Do You Need to Add Separate Event Listeners?**
Yes, if you want to handle the event in **both phases**, you need to add **two
separate event listeners**:
- One with `useCapture = true` (for capturing).
- One with `useCapture = false` (for bubbling).
If you only add one event listener with `useCapture = true`, it will **only
handle the capturing phase**, and the bubbling phase will not be handled
unless you add another event listener with `useCapture = false`.
---
### **Example: Handling Only the Capturing Phase**
If you only want to handle the event during the **capturing phase**, you can
set `useCapture = true` and skip adding a second event listener for the
bubbling phase.
```javascript
grandparent.addEventListener('click', function(event) {
console.log('Grandparent (Capturing)');
}, true); // useCapture = true
```
- In this case, the event will **not** trigger during the bubbling phase.
---
### **Example: Handling Only the Bubbling Phase**
If you only want to handle the event during the **bubbling phase**, you can
set `useCapture = false` (or omit it, as `false` is the default).
```javascript
grandparent.addEventListener('click', function(event) {
console.log('Grandparent (Bubbling)');
}, false); // useCapture = false
```
- In this case, the event will **not** trigger during the capturing phase.
---
### **Summary**
- **`useCapture = true`**: The event handler is triggered during the
**capturing phase**.
- **`useCapture = false`**: The event handler is triggered during the
**bubbling phase**.
- If you want to handle the event in **both phases**, you need to add **two
separate event listeners**:
- One with `useCapture = true`.
- One with `useCapture = false`.
- If you only want to handle the event in **one phase**, you can add a single
event listener with the appropriate `useCapture` value.
By understanding this, you can control exactly when your event handlers are
executed! 🚀