Category: Web Development

  • JavaScript Regex Replace: Patterns, Flags, Groups, and Callbacks

    JavaScript Regex Replace: Patterns, Flags, Groups, and Callbacks

    JavaScript regex replace uses String.replace() to find a pattern and return a new string with replacement text. The original string remains unchanged. Use a plain search string for a literal match, or a regular expression when you need flags, groups, or pattern rules.

    For example, “red red”.replace(/red/, “blue”) returns “blue red”. The regex matches the first occurrence only because it does not include the global flag.

    JavaScript regex replace: Replace one match and return a new string

    A string search finds the exact sequence you provide: “cat”.replace(“cat”, “dog”) returns “dog”. A regex can express a broader rule, such as /cat/ or /cat[0-9]/.

    The replacement argument can be a fixed string. In “Order 42”.replace(/[0-9]+/, “complete”), the digits are replaced, producing “Order complete”. Without g, a regex replacement stops after the first match. This behavior applies even when several matches exist.

    Because replace() returns a string, assign its result when you need to keep it: const updated = source.replace(/old/, “new”). Calling the method does not modify source.

    Regex replacement in JavaScript: Use g and i to replace all matches

    The g flag means global matching. It makes the replacement continue through every match instead of stopping at the first one. For example, “Error error ERROR”.replace(/error/gi, “notice”) returns “notice notice notice”.

    The i flag makes matching case-insensitive. It allows /error/i to match error, Error, and ERROR. Combine flags when needed: /error/gi replaces all case variations.

    • /word/ replaces the first matching occurrence.
    • /word/g replaces every matching occurrence with matching case.
    • /word/i replaces the first case-insensitive occurrence.
    • /word/gi replaces every case-insensitive occurrence.

    A global regex does not change the original string, and the returned value is still a new string. Use a replacement string when every match should receive the same output.

    How do you replace text in JavaScript with captured groups?

    Capturing groups preserve parts of the match so the replacement can reuse them. In “Smith, Ada”.replace(/(\w+),\s*(\w+)/, “$2 $1”), group one captures Smith and group two captures Ada. The result is “Ada Smith”.

    Replacement references begin with a dollar sign. $1 inserts the first captured group, $2 inserts the second, and $& inserts the complete match. Add g when the same grouped structure can occur more than once: text.replace(/(\w+),\s*(\w+)/g, “$2 $1”).

    Use noncapturing parentheses, (?:…), for grouping that should not create a replacement reference. This keeps group numbers stable when the pattern becomes more complex.

    How do callbacks compute replacements and escape literal input?

    Pass a function instead of a replacement string when the output depends on the match. The callback receives the complete match first, followed by captured groups, the match position, and the complete input string.

    For example, “item-7 item-12”.replace(/item-(\d+)/g, (match, number) => “product ” + Number(number)) returns “product 7 product 12”. The callback can format values, perform calculations, or choose different output for each match.

    A regex pattern is different from escaped literal text. If user input is “a+b”, putting it directly into new RegExp(userInput, “g”) treats + as a regex operator rather than a literal character. For literal matching, use a string search or escape regex metacharacters first:

    const escaped = userInput.replace(/[.*+?^${}()|[\]\\]/g, “\\$&”);

    Then create the pattern with new RegExp(escaped, “g”). This preserves the input as literal text while still allowing global replacement.

  • Rounding Numbers in JavaScript: Math.round(), floor(), ceil(), and trunc()

    Rounding Numbers in JavaScript: Math.round(), floor(), ceil(), and trunc()

    To round a number in JavaScript, use Math.round() for the nearest integer: Math.round(4.6) returns 5. To round decimal places, multiply by a power of 10, round, then divide by the same factor. The method you choose matters for negative values, midpoint ties, and non-finite inputs.

    JavaScript rounding methods return numeric values. They do not change the original variable unless you assign the result back to it.

    How does rounding numbers in JavaScript work?

    Math.round(value) selects the nearest integer. When a value is exactly halfway between integers, JavaScript rounds toward positive infinity. Therefore, Math.round(3.5) returns 4, while Math.round(-3.5) returns -3, not -4.

    This comparison uses the same positive, negative, and midpoint inputs for each method. The values appear in this order: 3.7, -3.7, 3.5, -3.5.

    • Math.round(): 4, -4, 4, -3
    • Math.floor(): 3, -4, 3, -4
    • Math.ceil(): 4, -3, 4, -3
    • Math.trunc(): 3, -3, 3, -3

    How do you round a JavaScript number to an integer?

    To round a JavaScript number to an integer, choose the function that matches the desired direction:

    • Math.round(value): nearest integer, with midpoint ties toward positive infinity.
    • Math.floor(value): the next integer toward negative infinity. For example, Math.floor(-2.1) is -3.
    • Math.ceil(value): the next integer toward positive infinity. For example, Math.ceil(-2.1) is -2.
    • Math.trunc(value): removes the fractional portion toward zero. For example, Math.trunc(-2.9) is -2.

    Floor and truncation are not equivalent for negative values: Math.floor(-2.9) returns -3, whereas Math.trunc(-2.9) returns -2.

    How do you round decimal places in JavaScript?

    For a simple decimal-place calculation, scale the number before rounding:

    function roundTo(value, places) { const factor = 10 ** places; return Math.round(value * factor) / factor; }

    roundTo(12.346, 2) returns 12.35. With two decimal places, the factor is 100: JavaScript rounds 12.346 × 100 to 1235, then divides by 100. Replace Math.round() with Math.floor(), Math.ceil(), or Math.trunc() when directional rounding is required.

    Scaling also follows the selected method’s negative-number behavior. For example, scaling -12.346 by 100 and applying Math.round() produces -12.35; an exact negative midpoint follows Math.round’s tie rule.

    Why do negative values and floating-point numbers surprise you?

    Negative values expose the difference between direction and distance. Math.floor(-3.2) moves farther from zero to -4, while Math.trunc(-3.2) removes only the fraction and returns -3. Math.ceil(-3.2) also returns -3.

    Decimal scaling has a floating-point limitation. JavaScript stores ordinary numbers in binary floating-point, so many decimal fractions are represented slightly above or below their written value. For example, Math.round(1.005 * 100) / 100 can return 1 because the scaled value may be just below 100.5.

    For strict decimal rules, use integer units such as cents or decimal-aware arithmetic instead of assuming multiplication guarantees exact precision. Non-finite values pass through these methods: Math.round(NaN) returns NaN, Math.floor(Infinity) returns Infinity, Math.ceil(-Infinity) returns -Infinity, and Math.trunc(NaN) returns NaN.

  • JavaScript API Call with fetch(): GET and JSON POST Requests

    JavaScript API Call with fetch(): GET and JSON POST Requests

    Use fetch() to make a JavaScript API call with the browser’s built-in, promise-based request function. This guide shows how to call an API in JavaScript with async/await for GET and JSON POST requests.

    An API call from JavaScript returns a promise, so await keeps the control flow readable while the browser waits for a response.

    Build a JavaScript API call with fetch()

    fetch() starts a request and returns a promise that resolves to a Response object. The complete async GET function below adds URL parameters, checks response.ok before parsing, and rethrows failures so calling code can handle them.

    GET example: async function getUsers(limit, role) { try { const params = new URLSearchParams({ limit: String(limit), role }); const url = “https://api.example.com/users?” + params.toString(); const response = await fetch(url); if (!response.ok) { throw new Error(“GET failed: ” + response.status); } return await response.json(); } catch (error) { console.error(“Could not load users:”, error); throw error; } }

    GET is the default HTTP method, so this request does not need an options object. await fetch(url) waits for the response promise, while await response.json() waits for the separate promise that parses the response body.

    How to call an API in JavaScript with GET parameters

    Place query parameters after a question mark in the URL. URLSearchParams creates that query string and encodes spaces and special characters safely. In the example, limit and role become parameters such as ?limit=10&role=admin.

    Call the function with getUsers(10, “admin”). To set the method explicitly, pass a second argument to fetch(): fetch(url, { method: “GET” }). The server’s status is available through response.status, while response.ok is true only for successful 2xx responses.

    Send JSON with POST requests and headers

    Use POST when the request sends data to the server. Set the method to “POST”, identify the body as JSON with a Content-Type header, and convert the JavaScript object with JSON.stringify() before sending it.

    JSON POST example: async function createUser(user) { try { const response = await fetch(“https://api.example.com/users”, { method: “POST”, headers: { “Content-Type”: “application/json”, “Accept”: “application/json” }, body: JSON.stringify(user) }); if (!response.ok) { throw new Error(“POST failed: ” + response.status); } return await response.json(); } catch (error) { console.error(“Could not create user:”, error); throw error; } }

    For example, pass an object with createUser({ name: “Ada”, email: “[email protected]” }). The request headers tell the server how to interpret the body and what response format the client prefers.

    Handle network, HTTP, and parsing errors

    Handle these failures separately:

    • Network errors: fetch() rejects when the request cannot be completed, such as when the network is unavailable or the request is blocked. The surrounding try…catch handles that rejection.
    • HTTP errors: A 404 or 500 response does not automatically reject the fetch promise. The promise resolves normally, so check response.ok and throw an error when it is false.
    • Parsing errors: response.json() returns a promise and rejects when the body is not valid JSON. Using return await response.json() inside the try block sends that failure to catch.

    If an endpoint returns an empty or non-JSON body, use await response.text() instead of response.json().

  • Sort in JavaScript: Ascending and Descending Arrays

    Sort in JavaScript: Ascending and Descending Arrays

    sort in JavaScript uses the Array.prototype.sort() method. By default, it converts array elements to strings and orders their UTF-16 values, so string arrays often sort as expected but number arrays can produce surprising results.

    For numbers, pass a comparator: (a, b) => a – b sorts ascending, while (a, b) => b – a sorts descending. The same comparator pattern works for numeric properties on objects.

    How does sort in JavaScript behave by default for strings and numbers?

    Without a comparator, sort() compares string representations. For example, [‘pear’, ‘apple’, ‘orange’].sort() returns [‘apple’, ‘orange’, ‘pear’]. Uppercase and lowercase characters can appear in separate groups because their character codes differ.

    The same rule causes numeric errors. [10, 2, 1, 21].sort() becomes [1, 10, 2, 21], because the values are compared as ’10’, ‘2’, ‘1’, and ’21’. Use a numeric comparator whenever the array contains numbers. Default sorting is appropriate only when its string-based ordering matches the intended result.

    How does a JavaScript numeric sort handle ascending and descending values?

    A comparator receives two elements, a and b. A negative return value places a before b; a positive value places b first; zero treats them as equal. This return-value contract lets JavaScript calculate numeric order directly.

    For ascending order, subtract the second value from the first:

    const ascending = numbers.sort((a, b) => a – b);

    If a is smaller than b, the result is negative, so a moves earlier. If a is larger, the result is positive, so it moves later. This is the standard JavaScript numeric sort for low-to-high values.

    To sort descending in JavaScript, reverse the subtraction:

    const descending = numbers.sort((a, b) => b – a);

    Now a larger value produces a negative result and moves toward the start of the array. Both calls mutate numbers; use a copy when the original order matters.

    How do you sort objects by a property?

    For objects, the comparator should read the property used for ordering. This example sorts players by a numeric score from lowest to highest:

    players.sort((a, b) => a.score – b.score);

    For highest to lowest, switch the operands:

    players.sort((a, b) => b.score – a.score);

    String properties need a string comparator. For example, players.sort((a, b) => a.name.localeCompare(b.name)) orders names alphabetically according to the available locale rules. For descending names, compare b.name with a.name. Keep the property types consistent so the comparator returns reliable results.

    What changes when sort() mutates, and when should you use toSorted()?

    sort() changes the existing array and returns that same array. Declaring the array with const does not prevent this mutation; it only prevents assigning a different array to the variable.

    Copy the array before sorting when other code needs the original order:

    const ascending = […numbers].sort((a, b) => a – b);

    toSorted() provides the non-mutating alternative in modern JavaScript. It returns a new sorted array while leaving the source unchanged:

    const descending = numbers.toSorted((a, b) => b – a);

    Use sort() when changing the current array is intentional. Use toSorted(), or a spread copy followed by sort(), when immutability is required.

  • JavaScript Practice: Progressive Coding Challenges with Test Cases

    JavaScript Practice: Progressive Coding Challenges with Test Cases

    Build JavaScript practice through eight focused problems that increase in difficulty. Each exercise defines an input, required output, constraints, sample cases, and review criteria, so you can test behavior instead of relying on an open-ended project brief.

    Start with the first section, then move forward only when your implementation passes the listed edge cases. These JavaScript coding challenges are short enough for repeated attempts and code reviews.

    JavaScript Practice: Beginner String and Number Challenges

    Reverse a string

    Input: A string. Required output: The characters in reverse order. Constraints: Preserve every character, including spaces, and support empty input. Samples: "JavaScript" → "tpircSavaJ"; "" → "". Hint: Use a loop or reverse and join. Review: Check that no characters are lost and the source value is not mutated.

    Count vowels

    Input: A string. Required output: The number of a, e, i, o, and u characters. Constraints: Count case-insensitively and ignore nonletters. Samples: "JavaScript" → 3; "rhythm!" → 0. Hint: Normalize the string and use a vowel lookup. Review: Empty input must return 0, and uppercase vowels must count.

    Build FizzBuzz

    Input: A positive integer n. Required output: An array from 1 through n, replacing multiples of 3 with "Fizz", multiples of 5 with "Buzz", and multiples of both with "FizzBuzz". Constraints: 1 ≤ n ≤ 1,000. Samples: 5 → [1, 2, "Fizz", 4, "Buzz"]; 15 → [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]. Hint: Test divisibility by 15 first. Review: Preserve order and the number-versus-string value types.

    JavaScript Coding Challenges for Arrays and Objects

    Keep first occurrences

    Input: An integer array. Required output: A unique array preserving first-seen order. Constraints: Do not sort the values or change the source array. Samples: [3, 1, 3, 2, 1] → [3, 1, 2]; [] → []. Hint: Track seen values with a Set or lookup. Review: Test duplicates at the beginning, middle, and end, and verify the original array remains unchanged.

    Group records

    Input: An array of objects with name and team properties. Required output: An object mapping each team to an array of names. Constraints: Preserve input order and create no empty groups. Sample: [{name: "Ana", team: "red"}, {name: "Bo", team: "blue"}, {name: "Cy", team: "red"}] → {red: ["Ana", "Cy"], blue: ["Bo"]}. Hint: Build an accumulator as you iterate. Review: Check exact keys, name order, and behavior with an empty array.

    JavaScript Code Challenges for Functions and Data Transformation

    Compose unary functions

    Input: A list of synchronous unary functions and a value x. Required output: The result after applying functions from left to right. Constraints: Functions accept and return one value. Samples: [x=>x+2, x=>x*3], 4 → 18; [], 4 → 4. Hint: Reduce over the function list. Review: Confirm execution order and avoid mutating the list.

    Summarize a cart

    Input: An array of {price, qty} items and a tax rate. Required output: The total after tax, rounded to two decimals. Constraints: Prices, quantities, and tax are nonnegative; apply tax after calculating the subtotal. Samples: [{price: 10, qty: 2}], 0.1 → 22; [], 0.2 → 0. Hint: Reduce the subtotal before rounding. Review: Prevent string concatenation, handle zero items, and account for floating-point rounding.

    Async Extension: Reliable Fetch Behavior

    Retry an async request

    Input: A promise factory and a maximum-attempts integer. Required output: The first resolved value, or the final rejection after all attempts fail. Constraints: Call the factory once per attempt, require maxAttempts ≥ 1, and stop immediately after success. Samples: A factory yielding Error("E1"), Error("E2"), then "ok" with 3 attempts resolves to "ok" on call 3; two failures with maxAttempts=2 reject Error("E2"). Hint: Use an await loop with try/catch. Review: Preserve the final error and verify the exact attempt count.

  • JavaScript absolute value: How Math.abs() handles common inputs

    JavaScript absolute value: How Math.abs() handles common inputs

    JavaScript absolute value uses Math.abs() to return a number’s distance from zero. Negative values become positive, positive values stay unchanged, and zero remains zero. In other words, absolute value in JavaScript represents magnitude without a sign.

    The syntax is Math.abs(value). The function accepts one argument and applies JavaScript’s numeric conversion before calculating the result, so its behavior includes more than number literals.

    How does JavaScript absolute value work with Math.abs()?

    For ordinary numeric input, Math.abs() returns the input’s nonnegative magnitude. It does not change the original variable; use the returned value when the normalized number is needed.

    • Math.abs(-12) returns 12.
    • Math.abs(12) returns 12.
    • Math.abs(0) returns 0.
    • Math.abs(-3.75) returns 3.75.

    These results follow the mathematical definition: both -12 and 12 are 12 units from zero. The function removes the sign only when the value is negative; it does not round decimals or otherwise alter a finite number.

    What does absolute value in JavaScript return for strings and null?

    The JavaScript Math.abs function converts its argument to a number before applying absolute value. A string containing a valid numeric representation therefore produces a numeric result, while a string that cannot be converted produces NaN.

    • Math.abs(“-18”) returns 18.
    • Math.abs(“6.5”) returns 6.5.
    • Math.abs(” -9 “) returns 9; surrounding whitespace is ignored during conversion.
    • Math.abs(null) returns 0, because numeric conversion treats null as zero.
    • Math.abs(“18px”) returns NaN, because the complete string is not a valid number.
    • Math.abs(“hello”) returns NaN.

    An empty string also converts to zero, so Math.abs(“”) returns 0. By contrast, Math.abs(undefined) returns NaN. NaN signals that the conversion did not produce a usable numeric result; it is not a positive or negative number.

    How can Math.abs(a – b) find the distance between two numbers?

    Use Math.abs(a – b) to calculate the absolute difference between two numbers. Subtraction happens first, and Math.abs() then removes the direction of that difference. This makes the result the same regardless of which number is larger.

    For example, Math.abs(18 – 25) returns 7. Reversing the operands, as in Math.abs(25 – 18), also returns 7. The same pattern works with variables:

    const distance = Math.abs(currentValue – targetValue);

    If currentValue is 42 and targetValue is 50, distance is 8. This pattern is useful for comparing measurements, positions, scores, or other numeric values where only the size of the difference matters.

    How does JavaScript Math.abs handle NaN, Infinity, and -0?

    Special numeric values follow predictable rules:

    • Math.abs(NaN) returns NaN. An unknown numeric result remains unknown.
    • Math.abs(Infinity) returns Infinity.
    • Math.abs(-Infinity) returns Infinity.
    • Math.abs(-0) returns positive zero, displayed as 0.

    JavaScript distinguishes positive and negative zero internally even though both usually display as 0. After Math.abs(-0), the sign is positive. The function also returns NaN when called without an argument: Math.abs() has no value to convert.

  • Check If String Is Empty in JavaScript

    Check If String Is Empty in JavaScript

    Use strict equality to check if string is empty when the input is already known to be a string: value === ”. To check if a string is empty in a validation rule, choose a different test when spaces should count as no content or when the value may be missing.

    The practical answer to how to check if a string is empty depends on the definition of “empty”: an exact zero-character value, whitespace-only text, or an absent value.

    Check if string is empty: value === ” or value.length === 0

    For a value guaranteed to be a primitive string, these two checks identify an exact empty string:

    • value === ” compares the value directly with JavaScript’s empty string literal. It does not coerce numbers, booleans, or other values.
    • value.length === 0 checks whether the string contains zero characters. It produces the same result for a known string.

    Strict equality is usually the clearest option because it states the intended value directly. The length form is useful when you are already working with string properties or want to emphasize the character count.

    Neither check treats a string containing spaces as empty. For example, ‘ ‘ === ” is false, and the string’s length is greater than zero.

    Treat whitespace-only text as empty with value.trim().length === 0

    Use value.trim().length === 0 when leading and trailing whitespace should not count as content. The trim() method removes whitespace from both ends of a string, so this condition matches all of these examples:

    • ”
    • ‘ ‘
    • ‘ ’
    • ‘ text ‘ does not match because text remains after trimming

    trim() returns a new string and does not modify the original value. Use the exact equality or length check instead when spaces are meaningful, such as in a fixed-format value where padding must be preserved.

    For checking an empty string in JavaScript when the input type is not guaranteed, combine trimming with a type guard:

    typeof value === ‘string’ && value.trim().length === 0

    This prevents trim() from being called on a number, object, or other non-string value.

    Handle null or undefined before checking

    null and undefined are missing-value states, not empty strings. They require a separate decision. Accessing value.length or calling value.trim() on either value throws a TypeError, so guard them before using string methods.

    An explicit null or undefined guard is:

    value === null || value === undefined

    For an optional field where missing values should be rejected separately from empty text, use conditions such as:

    • value === null || value === undefined for a missing value
    • typeof value === ‘string’ && value.length === 0 for an exact empty string
    • typeof value === ‘string’ && value.trim().length === 0 for empty or whitespace-only text

    If your rule defines both missing and blank as invalid, combine those cases explicitly:

    value === null || value === undefined || (typeof value === ‘string’ && value.trim().length === 0)

    This condition does not silently classify unrelated types as empty. The shorter expression value == null also matches only null and undefined, but the explicit form makes that intent easier to read.

    Avoid truthiness surprises with !value and optional values

    !value tests whether a value is falsy, not whether it is an empty string. It evaluates to true for an empty string, but also for:

    • 0
    • false
    • null
    • undefined
    • NaN

    It also does not identify whitespace-only text: !’ ‘ is false because a non-empty string is truthy. Use !value only when every falsy value should be treated as invalid. If zero or false are valid inputs, use a strict string, trimming, or null check that matches the required definition of empty.

  • JavaScript object methods explained: this, ownership, and prototypes

    JavaScript object methods explained: this, ownership, and prototypes

    JavaScript object methods are functions stored as object properties. The call site determines the value of this, while the method’s location determines whether it is an own property or inherited through a prototype.

    The same ideas apply to object literals, instances created by classes, and built-in APIs such as Object.keys. A small set of concrete examples makes the differences clear.

    JavaScript object methods: function properties, shorthand syntax, and calls

    A function becomes an object method when it is assigned to a property and invoked through that object. The traditional function-property syntax looks like this:

    const user = { name: “Mina”, greet: function () { return “Hi, ” + this.name; } };

    Calling user.greet() executes the function with user as its receiver. The method shorthand provides the same behavior with less syntax:

    const user = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

    Here, user.greet() returns “Hi, Mina”. Shorthand is not a special kind of function. It is concise syntax for defining a regular method in an object literal. The important detail is the property access followed by parentheses: user.greet().

    How this gets its value from the call-site receiver

    For regular object methods, this usually comes from the object before the dot. In user.greet(), the receiver is user. Calling the same function through another object changes the receiver:

    const colleague = { name: “Ravi” }; user.greet.call(colleague);

    This call returns “Hi, Ravi”. The function has not changed; only its receiver has.

    Detaching the function removes that receiver:

    const speak = user.greet; speak();

    In strict-mode code, including JavaScript modules and class methods, this is undefined in the detached call. The method therefore cannot read this.name. Use user.greet() when the receiver should remain attached, or create a bound function with const speak = user.greet.bind(user).

    An arrow function does not acquire its own receiver from a method call. Its this is lexical, inherited from the surrounding scope. Therefore, greet: () => this.name is not a replacement for a regular receiver-based method when the function needs the object that appears before the dot.

    Object methods: own properties versus instance and class prototype methods

    Methods defined directly in an object literal are own properties. The function belongs to that particular object:

    const literalUser = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

    A class uses a different ownership model:

    class User { constructor(name) { this.name = name; } greet() { return “Hi, ” + this.name; } } const mina = new User(“Mina”);

    mina.name is an own property because the constructor creates it on the instance. mina.greet works, but the function is stored on User.prototype, not copied onto mina. JavaScript looks along the prototype chain when it does not find a property directly on the instance.

    Object.hasOwn(mina, “name”) returns true, while Object.hasOwn(mina, “greet”) returns false. Prototype sharing lets every User instance use one method function. Assigning a function inside the constructor would instead create a separate own method for every instance.

    JS object methods and useful static Object methods

    Some object methods are static methods on the built-in Object constructor. They are called as Object.method(), not as methods of the data object. For example:

    Object.keys(user) returns an array of the object’s enumerable own property names, such as [“name”, “greet”]. Related static methods include Object.values(user) for values and Object.entries(user) for key-value pairs.

    Object.hasOwn(user, “name”) checks own-property ownership without searching the prototype chain. Object.assign(target, source) copies enumerable properties into a target object, while Object.create(prototype) creates an object with a chosen prototype.

    These static APIs differ from instance or prototype methods such as user.greet() and user.toString(). The first group is called on Object; the second is reached through a specific object and may use that object as the receiver.

  • JavaScript Calendar: Build a Month Grid with Events

    JavaScript Calendar: Build a Month Grid with Events

    Build a JavaScript calendar that renders a navigable month grid and marks events from structured data with native JavaScript. Keep month state separate from event data so each navigation click can rerender the grid without losing the current view.

    Keep the calendar JavaScript in one module: the same render function can serve the initial load and every previous or next month action.

    Set up the JavaScript calendar HTML and event data

    Give the interface three hooks: a month label, previous and next buttons, and an empty grid. For example, use data-calendar-label, data-calendar-previous, data-calendar-next, and data-calendar-grid attributes. Query them with document.querySelector so the widget does not depend on a particular framework.

    Store events with normalized local date keys. The date uses a four-digit year, two-digit month, and two-digit day:

    const events = [{ date: ‘2025-01-08’, title: ‘Planning review’ }, { date: ‘2025-01-21’, title: ‘Release’ }];

    Build an index once, outside the renderer, so multiple events can share a date:

    const eventsByDate = events.reduce((map, event) => { const list = map.get(event.date) || []; list.push(event); map.set(event.date, list); return map; }, new Map());

    Use a state object such as const state = { year: 2025, month: 0 }; JavaScript months are zero-based: 0 means January and 11 means December. The displayed month number is therefore the internal month plus one.

    Calculate first weekdays, month lengths, and grid cells

    Calculate the first weekday and number of days whenever the month is rendered:

    const firstWeekday = new Date(state.year, state.month, 1).getDay();
    const daysInMonth = new Date(state.year, state.month + 1, 0).getDate();

    getDay() returns Sunday as 0 and Saturday as 6. The second expression uses day 0 of the following month, which returns the final day of the selected month. This avoids hard-coded month lengths and handles leap years.

    Reserve enough cells for the leading blank cells before day 1 and the trailing cells after the final day:

    const cellCount = Math.ceil((firstWeekday + daysInMonth) / 7) * 7;

    For cell index i, calculate the visible day with const day = i – firstWeekday + 1;. Values below 1 are leading cells; values above daysInMonth are trailing cells. Render those positions as empty, noninteractive cells rather than inventing dates.

    Render a JavaScript calendar widget with event markers

    Clear the grid at the start of every render, then loop from 0 to cellCount – 1. For each valid day, create a button or cell and generate a stable key:

    const toKey = (year, month, day) => `${year}-${String(month + 1).padStart(2, ‘0’)}-${String(day).padStart(2, ‘0’)}`;

    For each valid day, create a button, set button.dataset.date = key, and set its visible text to the day number. Retrieve matching events with const dayEvents = eventsByDate.get(key) || []; Then add an event marker when the array is not empty, such as a bullet or count. Use the event titles for the button’s accessible label or title, for example, dayEvents.map(event => event.title).join(‘, ‘).

    This stable date key lets click handlers identify a day without parsing its displayed number. It also keeps events attached correctly when the month changes. A JavaScript calendar widget can use the same structure for birthdays, appointments, or release dates.

    Add previous and next controls to a JavaScript event calendar

    Attach click handlers that update the zero-based month and then call render():

    previous.addEventListener(‘click’, () => { if (state.month === 0) { state.month = 11; state.year–; } else state.month–; render(); });

    next.addEventListener(‘click’, () => { if (state.month === 11) { state.month = 0; state.year++; } else state.month++; render(); });

    Update the label inside render() with new Intl.DateTimeFormat(‘en’, { month: ‘long’, year: ‘numeric’ }).format(new Date(state.year, state.month, 1)). Keep the state object and event index outside the renderer. Reinitializing either one inside render() would reset navigation or discard event data. Call render() once after wiring the controls to display the initial month.

  • JavaScript replaceAll: How to Replace Every Match

    JavaScript replaceAll: How to Replace Every Match

    JavaScript replaceAll replaces every occurrence of matching text and returns a new string. It does not modify the original string, so you must store or use the returned value.

    Use replaceAll in JavaScript with a literal string when the text is known, or with a global regular expression when matching needs flags or pattern logic.

    How JavaScript replaceAll Replaces Every Literal Match

    Pass a string as the first argument to search for literal text. Regular-expression characters in that argument, such as + or ., have no special meaning.

    Example: const source = “red, red, blue”;

    const updated = source.replaceAll(“red”, “green”);

    console.log(updated); // “green, green, blue”

    console.log(source); // “red, red, blue”

    The returned value contains every replacement, while source remains unchanged. Literal matching is case-sensitive, so searching for “red” does not replace “Red”. A replacement can be a fixed string or a function that calculates the result for each match.

    How Does JS replaceAll Differ from replace?

    JS replaceAll replaces every literal match by default. The replace() method replaces only the first literal match when its search value is a string.

    Example: const source = “red, red, blue”;

    source.replace(“red”, “green”); // “green, red, blue”

    source.replaceAll(“red”, “green”); // “green, green, blue”

    With a regular expression, replace() replaces every match only when the expression has the global g flag:

    source.replace(/red/g, “green”); // “green, green, blue”

    Use replaceAll() for clear literal replacement. Use replace() when you intentionally need only the first match, or when existing code already handles a regular-expression pattern.

    How to Use replaceAll() in JavaScript with a Global Regex

    A regular expression passed to replaceAll() must be global. The expression must include the g flag, or JavaScript throws a TypeError instead of performing a partial replacement.

    Example: const text = “Red red RED”;

    const result = text.replaceAll(/red/gi, “blue”);

    // “blue blue blue”

    The i flag makes the pattern case-insensitive, while g makes it apply to every match. A non-global expression such as /red/i is not valid for replaceAll().

    For dynamic text that should be treated literally, pass the value directly as a string:

    const term = “red+blue”;

    text.replaceAll(term, “color”);

    If a dynamic value must be included in a larger regular expression, escape its pattern characters before constructing the expression:

    function escapeRegExp(value) {

    return value.replace(/[.*+?^${}()|[\]\\]/g, “\\$&”);

    }

    const pattern = new RegExp(escapeRegExp(term), “g”);

    text.replaceAll(pattern, “color”);

    This keeps characters from the user-provided value from changing the regex’s meaning and preserves the required global behavior.

    How Do Callbacks and Special Replacement Tokens Work?

    Pass a function as the replacement to calculate a different result for each match. The callback receives the complete match first, followed by captured groups and other match details.

    Example: const prices = “Item: $5, Item: $12”;

    const raised = prices.replaceAll(/\$(\d+)/g, (match, amount) => “$” + (Number(amount) + 1));

    // “Item: $6, Item: $13”

    When the replacement is a string, JavaScript recognizes special replacement tokens. $& inserts the complete match, $1 inserts the first captured group, $2 inserts the second, $` inserts the text before the match, $’ inserts the text after the match, and $$ inserts a literal dollar sign.

    Example: const marked = “red blue”.replaceAll(/(\w+)/g, “[$&]”);

    // “[red] [blue]”

    These tokens are interpreted when the replacement is a string, including when the search value is a literal string. A callback’s returned value is inserted as-is, so dollar signs returned by the function are not treated as replacement tokens.