1.
Sum of Two Numbers
Objective: Variables, number conversion, output
Ask the user for two numbers and display their sum.
const num1 = Number(prompt("Enter first number:"));
const num2 = Number(prompt("Enter second number:"));
alert("The sum is: " + (num1 + num2));
Why it works:
prompt() returns a string, so Number() converts it. The numbers are added, and the
result is shown with alert().
2. Even or Odd?
Objective: Conditionals, modulo operator
const value = Number(prompt("Enter a number:"));
if (value % 2 === 0) [Link]("Even");
else [Link]("Odd");
Why it works: % 2 gives the remainder—0 means even.
3. Countdown
Objective: Loops, backwards iteration
const n = Number(prompt("Enter a number:"));
for (let i = n; i >= 1; i--) {
[Link](i);
}
Why it works:
A for loop counts down until it reaches 1.
4. Maximum of an Array
Objective: Arrays, tracking max values
const nums = [3, 17, 9, 42, 5];
let max = nums[0];
for (let i = 1; i < [Link]; i++) {
if (nums[i] > max) max = nums[i];
Get more Apps Script Content at [Link] by Laurence Svekis
}
[Link](max);
5. Greeting Function
Objective: Defining and calling functions
function greet(name) {
[Link]("Hello, " + name);
}
greet("Lars");
6. Change Page Text (DOM)
Objective: DOM selection & modification
const heading = [Link]("title");
[Link] = "Updated via JavaScript!";
7. Button Click Counter
Objective: Events, state management
let count = 0;
[Link]("click", () => {
count++;
[Link] = count;
});
8. Shopping Cart Total
Objective: Objects, arrays, totals
const cart = [
{ name: "Book", price: 12.99 },
{ name: "Headphones", price: 29.99 }
];
Get more Apps Script Content at [Link] by Laurence Svekis
let total = 0;
for (const item of cart) total += [Link];
[Link](total);
9. Convert Celsius to Fahrenheit (map)
Objective: Higher-order functions
const c = [0, 10, 20];
const f = [Link](n => n * 9/5 + 32);
[Link](f);
10. Simple Timer
Objective: setInterval, clearInterval
let secs = 0;
const timer = setInterval(() => {
[Link](secs++);
if (secs > 10) clearInterval(timer);
}, 1000);
💡 Exercises 11–20 — Intermediate
JavaScript & DOM Skills
11. Reverse a String
Objective: Strings, loops
const input = prompt("Enter a word:");
let reversed = "";
for (let i = [Link] - 1; i >= 0; i--) {
reversed += input[i];
}
[Link](reversed);
Get more Apps Script Content at [Link] by Laurence Svekis
12. Word Counter
Objective: Split, trim, whitespace handling
const text = prompt("Enter text:").trim();
const words = text ? [Link](/\s+/) : [];
[Link]([Link]);
13. Number Guessing Game
Objective: Random numbers, loops, conditionals
const secret = [Link]([Link]() * 20) + 1;
let guess;
while (guess !== secret) {
guess = Number(prompt("Guess the number:"));
if (guess < secret) alert("Too low");
else if (guess > secret) alert("Too high");
else alert("Correct!");
}
14. Filter Even Numbers (filter)
Objective: Array filtering
const nums = [1, 4, 7, 10];
const evens = [Link](n => n % 2 === 0);
[Link](evens);
15. Sum with reduce
Objective: Functional patterns
const nums = [5, 10, 15];
const total = [Link]((acc, n) => acc + n, 0);
[Link](total);
16. Unique Values with Set
Get more Apps Script Content at [Link] by Laurence Svekis
Objective: Set, deduplicating arrays
const vals = [1, 2, 2, 4, 4];
const unique = [...new Set(vals)];
[Link](unique);
17. Generate List Items (DOM)
Objective: Create & insert elements
const tasks = ["Learn JS", "Practice DOM"];
const ul = [Link]("taskList");
[Link](t => {
const li = [Link]("li");
[Link] = t;
[Link](li);
});
18. Show / Hide Toggle
Objective: [Link]
[Link]("click", () => {
[Link]("hidden");
});
19. Simple Form Validation
Objective: submit event, preventing submission
[Link]("submit", e => {
[Link]();
if (![Link] || ![Link]) {
[Link] = "Fill in all fields.";
} else {
[Link] = "Success!";
}
});
Get more Apps Script Content at [Link] by Laurence Svekis
20. Persistent Click Counter (localStorage)
Objective: Saving data in the browser
let total = Number([Link]("count")) || 0;
[Link] = total;
[Link]("click", () => {
total++;
[Link] = total;
[Link]("count", total);
});
21) FizzBuzz (Loops + Conditionals) — Console
Goal: Print numbers 1–100. For multiples of 3 print “Fizz”, multiples of 5 print “Buzz”,
both → “FizzBuzz”.
for (let i = 1; i <= 100; i++) {
if (i % 15 === 0) {
[Link]("FizzBuzz");
} else if (i % 3 === 0) {
[Link]("Fizz");
} else if (i % 5 === 0) {
[Link]("Buzz");
} else {
[Link](i);
}
}
Explanation
● % checks divisibility via remainder.
● Check 15 first so numbers like 30 don’t get caught by the 3 check early.
● else prints the number when no rule applies.
Get more Apps Script Content at [Link] by Laurence Svekis
22) Palindrome Checker (Strings) — Console
Goal: Check if a word reads the same forward and backward.
function isPalindrome(word) {
const cleaned = [Link]().replace(/[^a-z0-9]/g, "");
const reversed = [Link]("").reverse().join("");
return cleaned === reversed;
}
[Link](isPalindrome("Racecar")); // true
[Link](isPalindrome("hello")); // false
[Link](isPalindrome("A man, a plan!"));// true-ish based on
cleaning
Explanation
● toLowerCase() makes comparison case-insensitive.
● replace(/[^a-z0-9]/g, "") removes non-alphanumeric characters.
● split("") → array of chars, reverse(), then join("") back to a string.
23) Factorial (Loops) — Console
Goal: Compute n! (e.g., 5! = 120).
function factorial(n) {
if ( || n < 0) return null;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}
[Link](factorial(5)); // 120
[Link](factorial(0)); // 1
[Link](factorial(-2)); // null
Get more Apps Script Content at [Link] by Laurence Svekis
Explanation
● Validates input (must be a non-negative integer).
● Starts at 1 and multiplies by every number up to n.
● 0! is defined as 1, which this code naturally produces.
24) Fibonacci Sequence (Array) — Console
Goal: Generate the first n Fibonacci numbers.
function fibonacci(n) {
if ( || n <= 0) return [];
if (n === 1) return [0];
const seq = [0, 1];
while ([Link] < n) {
const next = seq[[Link] - 1] + seq[[Link] - 2];
[Link](next);
}
return seq;
}
[Link](fibonacci(10));
Explanation
● Starts with [0, 1].
● Each next value is the sum of the last two.
● Uses while until the array reaches length n.
25) Count Character Frequency (Objects) — Console
Goal: Count how many times each character appears in a string.
function charFrequency(text) {
const freq = {};
Get more Apps Script Content at [Link] by Laurence Svekis
for (const ch of text) {
freq[ch] = (freq[ch] || 0) + 1;
}
return freq;
}
[Link](charFrequency("banana"));
Explanation
● freq is an object where keys are characters.
● freq[ch] || 0 defaults to 0 if the key doesn’t exist.
● Adds 1 for each occurrence.
26) Sort Numbers Without .sort() (Algorithm) —
Console
Goal: Sort an array ascending using Bubble Sort.
function bubbleSort(arr) {
const a = [...arr]; // copy so we don’t mutate input
for (let i = 0; i < [Link] - 1; i++) {
for (let j = 0; j < [Link] - 1 - i; j++) {
if (a[j] > a[j + 1]) {
[a[j], a[j + 1]] = [a[j + 1], a[j]]; // swap
}
}
}
return a;
}
[Link](bubbleSort([5, 3, 8, 1, 2]));
Explanation
● Outer loop controls passes.
Get more Apps Script Content at [Link] by Laurence Svekis
● Inner loop compares adjacent pairs and swaps if out of order.
● - i optimization: after each pass, the largest value is already at the end.
27) Remove Duplicates Manually (No Set) — Console
Goal: Return unique values in order.
function uniqueValues(arr) {
const result = [];
for (const item of arr) {
if () {
[Link](item);
}
}
return result;
}
[Link](uniqueValues([1, 2, 2, 3, 1, 4]));
Explanation
● Uses [Link](item) to check if already added.
● Preserves first-seen order.
● Not the fastest for huge arrays, but perfect for learning.
28) Flatten One Level of Nested Arrays — Console
Goal: Turn [1,[2,3],[4],5] into [1,2,3,4,5].
function flattenOneLevel(arr) {
const out = [];
for (const item of arr) {
if ([Link](item)) [Link](...item);
else [Link](item);
}
return out;
Get more Apps Script Content at [Link] by Laurence Svekis
}
[Link](flattenOneLevel([1, [2, 3], [4], 5]));
Explanation
● [Link] detects nested arrays.
● ...item spreads array elements into out.
● Only flattens one level (by design).
29) Group Items by Property — Console
Goal: Group objects by category.
function groupBy(arr, key) {
const grouped = {};
for (const obj of arr) {
const k = obj[key];
if (!grouped[k]) grouped[k] = [];
grouped[k].push(obj);
}
return grouped;
}
const products = [
{ name: "Apple", category: "Fruit" },
{ name: "Carrot", category: "Veg" },
{ name: "Banana", category: "Fruit" },
];
[Link](groupBy(products, "category"));
Explanation
● Uses an object of arrays.
● Each unique key (like "Fruit") becomes a bucket.
Get more Apps Script Content at [Link] by Laurence Svekis
● Push each item into the correct bucket.
30) Safe JSON Parse (Try/Catch) — Console
Goal: Parse JSON without crashing your program.
function safeJsonParse(str) {
try {
return { ok: true, data: [Link](str) };
} catch (err) {
return { ok: false, error: [Link] };
}
}
[Link](safeJsonParse('{"a":1}'));
[Link](safeJsonParse("{bad json}"));
Explanation
● [Link] throws if the string isn’t valid JSON.
● try/catch prevents the error from stopping execution.
● Returns a consistent result object.
31) Debounce (Functions + Timers) — Console/Browser
Goal: Only run a function after the user “stops” triggering it for X ms.
function debounce(fn, delayMs) {
let timerId;
return function (...args) {
clearTimeout(timerId);
timerId = setTimeout(() => fn(...args), delayMs);
};
}
// Example:
Get more Apps Script Content at [Link] by Laurence Svekis
const debouncedLog = debounce((msg) => [Link](msg), 500);
debouncedLog("A");
debouncedLog("B");
debouncedLog("C"); // only "C" logs after 500ms
Explanation
● A closure keeps timerId between calls.
● Each call clears the previous timeout and sets a new one.
● Only the final call survives long enough to run.
32) Throttle (Functions + Timers) — Console/Browser
Goal: Run a function at most once every X ms.
function throttle(fn, intervalMs) {
let lastTime = 0;
return function (...args) {
const now = [Link]();
if (now - lastTime >= intervalMs) {
lastTime = now;
fn(...args);
}
};
}
// Example:
const throttledLog = throttle(() => [Link]("tick"), 1000);
setInterval(() => throttledLog(), 100); // logs about once per
second
Explanation
● lastTime tracks the last allowed execution time.
● If not enough time passed, calls are ignored.
Get more Apps Script Content at [Link] by Laurence Svekis
● Useful for scroll/resize events.
33) Build a URL Query String — Console
Goal: Convert an object to ?key=value&....
function toQueryString(params) {
const parts = [];
for (const key in params) {
const value = encodeURIComponent(params[key]);
[Link](`${encodeURIComponent(key)}=${value}`);
}
return [Link] ? "?" + [Link]("&") : "";
}
[Link](toQueryString({ q: "js exercises", page: 2 }));
Explanation
● encodeURIComponent makes values safe for URLs.
● parts becomes an array of key=value pairs.
● Joins them with & and adds a leading ?.
34) Deep Clone Simple JSON Objects — Console
Goal: Copy nested JSON-safe objects without shared references.
function deepCloneJson(obj) {
return [Link]([Link](obj));
}
const original = { a: 1, b: { c: 2 } };
const clone = deepCloneJson(original);
clone.b.c = 999;
[Link](original.b.c); // 2
[Link](clone.b.c); // 999
Get more Apps Script Content at [Link] by Laurence Svekis
Explanation
● [Link] converts to a string.
● [Link] creates a brand new structure.
● Limitation: won’t preserve functions, Dates, Maps, etc. (fine for learning + JSON
data).
35) Validate Email (Basic Regex) — Console
Goal: Basic email format check.
function isEmail(str) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
}
[Link](isEmail("test@[Link]")); // true
[Link](isEmail("bad@email")); // false
Explanation
● This regex checks:
○ something before @
○ something after @
○ at least one dot section
● Not “perfect” for every valid email ever, but solid for beginner validation.
Exercises 36–50 (DOM + Practical Web
Skills)
For these, use this HTML template once, then swap scripts per exercise:
<!doctype html>
<html>
Get more Apps Script Content at [Link] by Laurence Svekis
<head>
<meta charset="utf-8" />
<title>JS Exercises</title>
<style>
body { font-family: Arial, sans-serif; padding: 16px; }
.hidden { display: none; }
.error { color: #b00020; }
.ok { color: #0a7a0a; }
.box { padding: 12px; border: 1px solid #ddd;
border-radius: 8px; margin: 12px 0; }
</style>
</head>
<body>
<div id="app"></div>
<script src="[Link]"></script>
</body>
</html>
36) Live Character Counter — DOM
Goal: Count characters as the user types.
[Link]
[Link]("app").innerHTML = `
<div class="box">
<h2>Live Character Counter</h2>
<textarea id="txt" rows="4" cols="40" placeholder="Type
here..."></textarea>
<p>Characters: <span id="count">0</span></p>
</div>
`;
const txt = [Link]("txt");
const count = [Link]("count");
[Link]("input", () => {
[Link] = [Link];
Get more Apps Script Content at [Link] by Laurence Svekis
});
Explanation
● input event fires on every change (typing, paste, delete).
● [Link] gives current character count.
● Updates the DOM span instantly.
37) To-Do List Add Items — DOM
Goal: Add items to a list from an input field.
[Link]("app").innerHTML = `
<div class="box">
<h2>To-Do List</h2>
<input id="taskInput" placeholder="New task" />
<button id="addBtn">Add</button>
<ul id="list"></ul>
</div>
`;
const input = [Link]("taskInput");
const addBtn = [Link]("addBtn");
const list = [Link]("list");
[Link]("click", () => {
const text = [Link]();
if (!text) return;
const li = [Link]("li");
[Link] = text;
[Link](li);
[Link] = "";
[Link]();
});
Get more Apps Script Content at [Link] by Laurence Svekis
Explanation
● trim() prevents empty/space-only tasks.
● createElement("li") creates new list items.
● Resetting and focusing improves user experience.
38) To-Do Remove Items (Event Delegation) — DOM
Goal: Click a list item to remove it.
[Link]("app").innerHTML = `
<div class="box">
<h2>Click-to-Remove List</h2>
<input id="taskInput" placeholder="New item" />
<button id="addBtn">Add</button>
<ul id="list"></ul>
<p><em>Tip: click an item to remove it.</em></p>
</div>
`;
const input = [Link]("taskInput");
const addBtn = [Link]("addBtn");
const list = [Link]("list");
[Link]("click", () => {
const text = [Link]();
if (!text) return;
const li = [Link]("li");
[Link] = text;
[Link](li);
[Link] = "";
});
Get more Apps Script Content at [Link] by Laurence Svekis
// Event delegation: one listener on the parent <ul>
[Link]("click", (e) => {
if ([Link] === "LI") {
[Link]();
}
});
Explanation
● Instead of adding a click handler to every <li>, we add one to the <ul>.
● [Link] is the clicked element.
● .remove() deletes it from the DOM.
39) Tabs UI (Switch Sections) — DOM
Goal: Click buttons to show different content panels.
[Link]("app").innerHTML = `
<div class="box">
<h2>Tabs</h2>
<button data-tab="one">Tab 1</button>
<button data-tab="two">Tab 2</button>
<button data-tab="three">Tab 3</button>
<div id="one" class="panel">Content for Tab 1</div>
<div id="two" class="panel hidden">Content for Tab 2</div>
<div id="three" class="panel hidden">Content for Tab 3</div>
</div>
`;
function showTab(id) {
[Link](".panel").forEach(p =>
[Link]("hidden"));
[Link](id).[Link]("hidden");
}
Get more Apps Script Content at [Link] by Laurence Svekis
[Link]("button[data-tab]").forEach(btn => {
[Link]("click", () => showTab([Link]));
});
Explanation
● Panels share class panel; we hide all, then reveal the chosen one.
● [Link] reads data-tab="...".
● This pattern scales to many tabs.
40) Modal Popup (Open/Close) — DOM
Goal: Open a modal overlay and close it.
[Link]("app").innerHTML = `
<div class="box">
<h2>Modal</h2>
<button id="open">Open Modal</button>
</div>
<div id="overlay" class="hidden" style="
position:fixed; inset:0; background:rgba(0,0,0,.5);
display:flex; align-items:center; justify-content:center;">
<div style="background:#fff; padding:16px;
border-radius:10px; width:300px;">
<h3>Modal Title</h3>
<p>This is a simple modal.</p>
<button id="close">Close</button>
</div>
</div>
`;
const overlay = [Link]("overlay");
[Link]("open").addEventListener("click", () =>
{
Get more Apps Script Content at [Link] by Laurence Svekis
[Link]("hidden");
});
[Link]("close").addEventListener("click", () =>
{
[Link]("hidden");
});
// Close if user clicks outside the modal box
[Link]("click", (e) => {
if ([Link] === overlay) [Link]("hidden");
});
Explanation
● Overlay covers the screen and centers the modal content.
● Clicking the overlay (not the inner box) closes it.
● Uses .hidden to control visibility.
41) Form Validation with Inline Errors — DOM
Goal: Validate name + email and show errors.
[Link]("app").innerHTML = `
<div class="box">
<h2>Signup</h2>
<form id="form">
<div>
<label>Name</label><br/>
<input id="name" />
<div id="nameErr" class="error"></div>
</div>
<div style="margin-top:8px;">
<label>Email</label><br/>
<input id="email" />
<div id="emailErr" class="error"></div>
Get more Apps Script Content at [Link] by Laurence Svekis
</div>
<button style="margin-top:10px;">Submit</button>
<p id="status"></p>
</form>
</div>
`;
const form = [Link]("form");
const nameEl = [Link]("name");
const emailEl = [Link]("email");
const nameErr = [Link]("nameErr");
const emailErr = [Link]("emailErr");
const status = [Link]("status");
function isEmail(str) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
}
[Link]("submit", (e) => {
[Link]();
[Link] = "";
[Link] = "";
[Link] = "";
const name = [Link]();
const email = [Link]();
let ok = true;
if ([Link] < 2) {
[Link] = "Name must be at least 2 characters.";
ok = false;
}
if (!isEmail(email)) {
Get more Apps Script Content at [Link] by Laurence Svekis
[Link] = "Please enter a valid email
address.";
ok = false;
}
[Link] = ok ? " ✅ Submitted (demo)!" : "❌ Fix
errors above.";
[Link] = ok ? "ok" : "error";
});
Explanation
● Prevents page reload via preventDefault().
● Clears old errors each submit.
● Uses ok flag to track validation results.
● Provides immediate, targeted feedback.
42) Theme Toggle (Dark/Light) — DOM
Goal: Switch page theme with a button.
[Link]("app").innerHTML = `
<div class="box">
<h2>Theme Toggle</h2>
<button id="toggle">Toggle Theme</button>
<p>Click the button to switch styles.</p>
</div>
`;
let dark = false;
[Link]("toggle").addEventListener("click", ()
=> {
dark = !dark;
Get more Apps Script Content at [Link] by Laurence Svekis
[Link] = dark ? "#111" : "#fff";
[Link] = dark ? "#fff" : "#111";
});
Explanation
● Tracks state with dark.
● Updates inline styles on the body.
● Simple pattern that can later be upgraded to CSS classes.
43) Random Quote Generator (Array + DOM) — DOM
Goal: Show a random quote each click.
[Link]("app").innerHTML = `
<div class="box">
<h2>Random Quote</h2>
<button id="new">New Quote</button>
<p id="quote" style="margin-top:10px;"></p>
</div>
`;
const quotes = [
"Small steps, daily.",
"Make it work, then make it better.",
"Practice beats theory.",
"Debugging is learning in disguise."
];
const quoteEl = [Link]("quote");
function showRandomQuote() {
const idx = [Link]([Link]() * [Link]);
[Link] = quotes[idx];
}
Get more Apps Script Content at [Link] by Laurence Svekis
[Link]("new").addEventListener("click",
showRandomQuote);
showRandomQuote();
Explanation
● Random index: [Link]([Link]() * length).
● Updates quote text.
● Calls once at start so the page isn’t empty.
44) Simple Stopwatch (Start/Stop/Reset) — DOM
Goal: Build a stopwatch with interval control.
[Link]("app").innerHTML = `
<div class="box">
<h2>Stopwatch</h2>
<h3 id="time">0.0</h3>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>
</div>
`;
const timeEl = [Link]("time");
let t = 0; // tenths of a second
let timerId = null;
function render() {
[Link] = (t / 10).toFixed(1);
}
[Link]("start").addEventListener("click", () =>
{
if (timerId !== null) return; // already running
timerId = setInterval(() => {
Get more Apps Script Content at [Link] by Laurence Svekis
t++;
render();
}, 100);
});
[Link]("stop").addEventListener("click", () =>
{
clearInterval(timerId);
timerId = null;
});
[Link]("reset").addEventListener("click", () =>
{
t = 0;
render();
});
render();
Explanation
● Uses tenths of seconds (100ms) for a smoother display.
● Stores interval id in timerId so it can be stopped.
● Prevents multiple intervals with the if (timerId !== null) guard.
45) Countdown Timer (Input + Interval) — DOM
Goal: User enters seconds; countdown to 0.
[Link]("app").innerHTML = `
<div class="box">
<h2>Countdown</h2>
<input id="secs" type="number" min="1" placeholder="Seconds"
/>
<button id="go">Start</button>
<p id="out"></p>
</div>
Get more Apps Script Content at [Link] by Laurence Svekis
`;
const secsInput = [Link]("secs");
const out = [Link]("out");
let id = null;
[Link]("go").addEventListener("click", () => {
let remaining = Number([Link]);
if ( || remaining <= 0) {
[Link] = "Enter a positive number.";
return;
}
clearInterval(id);
[Link] = `Remaining: ${remaining}s`;
id = setInterval(() => {
remaining--;
[Link] = `Remaining: ${remaining}s`;
if (remaining <= 0) {
clearInterval(id);
[Link] = " ✅ Done!";
}
}, 1000);
});
Explanation
● Validates input and converts to number.
● Clears any existing countdown to avoid multiple timers.
● Decrements once per second until 0, then stops interval.
Get more Apps Script Content at [Link] by Laurence Svekis
46) LocalStorage Notes App — DOM + Storage
Goal: Save notes in the browser.
[Link]("app").innerHTML = `
<div class="box">
<h2>Notes (Saved in Browser)</h2>
<textarea id="note" rows="5" cols="40" placeholder="Write
notes..."></textarea>
<div>
<button id="save">Save</button>
<button id="clear">Clear</button>
</div>
<p id="msg"></p>
</div>
`;
const note = [Link]("note");
const msg = [Link]("msg");
[Link] = [Link]("notes") || "";
[Link]("save").addEventListener("click", () =>
{
[Link]("notes", [Link]);
[Link] = " ✅ Saved!";
[Link] = "ok";
});
[Link]("clear").addEventListener("click", () =>
{
[Link] = "";
[Link]("notes");
[Link] = " 🗑️ Cleared!";
[Link] = "";
});
Get more Apps Script Content at [Link] by Laurence Svekis
Explanation
● Loads saved notes on startup.
● Saves text to localStorage under a key ("notes").
● Clear removes both UI text and stored data.
47) Simple Search Filter (List Filtering) — DOM
Goal: Filter visible items based on a search box.
[Link]("app").innerHTML = `
<div class="box">
<h2>Search Filter</h2>
<input id="search" placeholder="Search..." />
<ul id="items"></ul>
</div>
`;
const data = ["JavaScript", "HTML", "CSS", "DOM", "Events",
"Arrays", "Objects"];
const ul = [Link]("items");
const search = [Link]("search");
function render(list) {
[Link] = "";
for (const item of list) {
const li = [Link]("li");
[Link] = item;
[Link](li);
}
}
[Link]("input", () => {
const q = [Link]().trim();
const filtered = [Link](x =>
[Link]().includes(q));
Get more Apps Script Content at [Link] by Laurence Svekis
render(filtered);
});
render(data);
Explanation
● Renders from an array to the DOM.
● On each input:
○ Normalize query to lowercase.
○ Filter items by includes.
○ Re-render the list with matching results.
48) Keyboard Shortcut (Ctrl/Cmd + K) — DOM
Goal: Open a “search” UI using keyboard shortcuts.
[Link]("app").innerHTML = `
<div class="box">
<h2>Keyboard Shortcut</h2>
<p>Press <strong>Ctrl+K</strong> (Windows) or
<strong>Cmd+K</strong> (Mac).</p>
<input id="search" class="hidden" placeholder="Type to
search..." />
</div>
`;
const search = [Link]("search");
[Link]("keydown", (e) => {
const isMac =
[Link]().includes("mac");
const combo = (isMac && [Link] && [Link]() ===
"k")
Get more Apps Script Content at [Link] by Laurence Svekis
|| (!isMac && [Link] && [Link]() ===
"k");
if (combo) {
[Link]();
[Link]("hidden");
[Link]();
}
if ([Link] === "Escape") {
[Link]("hidden");
[Link] = "";
}
});
Explanation
● Listens globally on document.
● Detects Ctrl+K or Cmd+K.
● preventDefault() stops browser’s default search behavior.
● Escape hides and clears the input.
49) Fetch JSON (Async/Await) — Browser
Goal: Load data from an API and display it.
[Link]("app").innerHTML = `
<div class="box">
<h2>Fetch Demo</h2>
<button id="load">Load Data</button>
<pre id="out" style="white-space:pre-wrap;"></pre>
</div>
`;
const out = [Link]("out");
Get more Apps Script Content at [Link] by Laurence Svekis
[Link]("load").addEventListener("click", async
() => {
[Link] = "Loading...";
try {
const res = await
fetch("[Link]
if (![Link]) throw new Error("HTTP " + [Link]);
const data = await [Link]();
[Link] = [Link](data, null, 2);
} catch (err) {
[Link] = "Error: " + [Link];
}
});
Explanation
● fetch(url) returns a Promise for the response.
● await [Link]() parses response body as JSON.
● try/catch handles network errors and failed status codes.
● Displays formatted JSON using [Link](..., null, 2).
50) Simple Pagination (Slice + Render) — DOM
Goal: Show items 5 at a time with Next/Prev.
[Link]("app").innerHTML = `
<div class="box">
<h2>Pagination</h2>
<ul id="list"></ul>
<button id="prev">Prev</button>
<button id="next">Next</button>
<p id="info"></p>
Get more Apps Script Content at [Link] by Laurence Svekis
</div>
`;
const items = [Link]({ length: 23 }, (_, i) => `Item ${i +
1}`);
const pageSize = 5;
let page = 0;
const list = [Link]("list");
const info = [Link]("info");
function render() {
[Link] = "";
const start = page * pageSize;
const pageItems = [Link](start, start + pageSize);
for (const it of pageItems) {
const li = [Link]("li");
[Link] = it;
[Link](li);
}
const totalPages = [Link]([Link] / pageSize);
[Link] = `Page ${page + 1} of ${totalPages}`;
}
[Link]("prev").addEventListener("click", () =>
{
page = [Link](0, page - 1);
render();
});
[Link]("next").addEventListener("click", () =>
{
const totalPages = [Link]([Link] / pageSize);
Get more Apps Script Content at [Link] by Laurence Svekis
page = [Link](totalPages - 1, page + 1);
render();
});
render();
Explanation
● slice(start, end) grabs only items for the current page.
● page * pageSize computes where the page starts.
● [Link] computes total pages.
● Prev/Next clamp the page so it doesn’t go out of range.
Get more Apps Script Content at [Link] by Laurence Svekis