update js cheatsheet
· 1 year ago
56689ccb34d9e1b705d959f90835c72e04b43b19
Parent:
2fbebd27a
2 files changed +412 −409
- javascript-for-architects.html +71 −409
- javascript-for-architects.js +341 −0
Diff
--- a/javascript-for-architects.html +++ b/javascript-for-architects.html @@ -1,3 +1,4 @@ + <!DOCTYPE html> <html lang="en"> <head> @@ -342,6 +343,11 @@ .card-svelte { --db-category-color: var(--js-color-svelte); } </style> + + <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> +<!-- Note: The script below contains the logic for interactivity (filters, search, lines, collapse) --> +<!-- It assumes Prism.js and Bootstrap JS are loaded before it executes. --> +<script src="javascript-for-architects.js"></script> </head> <body> <header class="page-header"> @@ -366,7 +372,7 @@ <h2 class="section-title" id="section-core-title">JavaScript Core Fundamentals</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-overview"> + <div class="info-card card-core" id="card-js-overview" data-keywords="single-threaded, event loop, dynamic, interpreted, prototype, multi-paradigm"> <div class="card-body"> <h5><i class="bi bi-code-slash" aria-hidden="true"></i> JS Overview</h5> <div class="card-content-wrapper"> @@ -389,7 +395,7 @@ </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-vars"> + <div class="info-card card-core" id="card-js-vars" data-keywords="let, const, var, scope, block scope, function scope, global scope, hoisting, tdz, temporal dead zone, closure, lexical"> <div class="card-body"> <h5><i class="bi bi-bounding-box-circles" aria-hidden="true"></i> Variables & Scope</h5> <div class="card-content-wrapper"> @@ -433,7 +439,7 @@ scopeTest(); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-types"> + <div class="info-card card-core" id="card-js-types" data-keywords="primitive, object, string, number, boolean, null, undefined, symbol, bigint, typeof, coercion, equality, ===, =="> <div class="card-body"> <h5><i class="bi bi-tags" aria-hidden="true"></i> Data Types</h5> <div class="card-content-wrapper"> @@ -482,7 +488,7 @@ console.log(0 === false); // false (Types are different) </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-operators"> + <div class="info-card card-core" id="card-js-operators" data-keywords="arithmetic, assignment, comparison, logical, bitwise, string, ternary, typeof, instanceof, spread, rest, optional chaining, nullish coalescing, precedence"> <div class="card-body"> <h5><i class="bi bi-calculator" aria-hidden="true"></i> Operators</h5> <div class="card-content-wrapper"> @@ -511,7 +517,7 @@ console.log(0 === false); // false (Types are different) </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-control-flow"> + <div class="info-card card-core" id="card-js-control-flow" data-keywords="if, else, switch, for, while, do while, for in, for of, break, continue, try, catch, finally, conditional, loop"> <div class="card-body"> <h5><i class="bi bi-alt" aria-hidden="true"></i> Control Flow</h5> <div class="card-content-wrapper"> @@ -557,7 +563,7 @@ for (const prop in car) { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-functions"> <!-- Enhanced --> + <div class="info-card card-core" id="card-js-functions" data-keywords="declaration, expression, arrow function, iife, closure, this, call, apply, bind, parameters, default, rest, arguments"> <div class="card-body"> <h5><i class="bi bi-braces" aria-hidden="true"></i> Functions</h5> <div class="card-content-wrapper"> @@ -609,7 +615,7 @@ counter2(); // 1 (independent count) </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-arrays"> <!-- Enhanced --> + <div class="info-card card-core" id="card-js-arrays" data-keywords="map, filter, reduce, forEach, find, findIndex, some, every, push, pop, shift, unshift, splice, sort, reverse, slice, concat, includes, indexOf, join, isArray, iteration, mutation"> <div class="card-body"> <h5><i class="bi bi-list-ol" aria-hidden="true"></i> Arrays</h5> <div class="card-content-wrapper"> @@ -661,7 +667,7 @@ const sum = numbers.reduce((acc, curr) => acc + curr, 0); // 15 </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-core" id="card-js-objects"> <!-- Enhanced --> + <div class="info-card card-core" id="card-js-objects" data-keywords="key-value, prototype, prototypal inheritance, __proto__, getPrototypeOf, Object.create, Object.keys, Object.values, Object.entries, Object.assign, hasOwnProperty, hasOwn, freeze"> <div class="card-body"> <h5><i class="bi bi-diagram-3" aria-hidden="true"></i> Objects & Prototypes</h5> <div class="card-content-wrapper"> @@ -730,7 +736,7 @@ buddy.speak(); <h2 class="section-title" id="section-modern-title">Modern JavaScript Features (ES6+)</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-arrow-func"> + <div class="info-card card-modern" id="card-js-arrow-func" data-keywords="arrow function, =>, lexical this, concise, es6, es2015"> <div class="card-body"> <h5><i class="bi bi-arrow-right-short" aria-hidden="true"></i> Arrow Functions <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -786,7 +792,7 @@ myObj.getValueArrow(); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-classes"> <!-- Updated --> + <div class="info-card card-modern" id="card-js-classes" data-keywords="class, constructor, extends, super, static, getter, setter, inheritance, oop, es6, es2015, private fields"> <div class="card-body"> <h5><i class="bi bi-building" aria-hidden="true"></i> Classes <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -845,7 +851,7 @@ console.log(myCar.speed); // 15 (using getter) </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-modules"> <!-- Updated --> + <div class="info-card card-modern" id="card-js-modules" data-keywords="esm, import, export, default, named, namespace, dynamic import, es6, es2015, commonjs, cjs, mjs"> <div class="card-body"> <h5><i class="bi bi-box-arrow-in-down" aria-hidden="true"></i> Modules (ESM) <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -899,7 +905,7 @@ console.log(Utils.apiKey); // Access via namespace </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-promises"> <!-- Updated --> + <div class="info-card card-modern" id="card-js-promises" data-keywords="promise, async, asynchronous, then, catch, finally, resolve, reject, pending, fulfilled, rejected, Promise.all, Promise.race, Promise.allSettled, Promise.any, es6, es2015"> <div class="card-body"> <h5><i class="bi bi-hourglass-split" aria-hidden="true"></i> Promises <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -956,7 +962,7 @@ console.log(Utils.apiKey); // Access via namespace </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-async-await"> <!-- Updated --> + <div class="info-card card-modern" id="card-js-async-await" data-keywords="async, await, promise, asynchronous, try catch, error handling, es2017, es8"> <div class="card-body"> <h5><i class="bi bi-lightning" aria-hidden="true"></i> Async/Await <span class="version-tag">ES2017+</span></h5> <div class="card-content-wrapper"> @@ -1023,7 +1029,7 @@ fetchMultiple(); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-destructuring"> <!-- Updated --> + <div class="info-card card-modern" id="card-js-destructuring" data-keywords="destructuring, array destructuring, object destructuring, unpack, assignment, rest syntax, default values, es6, es2015"> <div class="card-body"> <h5><i class="bi bi-arrows-angle-contract" aria-hidden="true"></i> Destructuring <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -1082,7 +1088,7 @@ createUser({ email: '[email protected]', name: 'Test User' }); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-template-literals"> + <div class="info-card card-modern" id="card-js-template-literals" data-keywords="template literal, backtick, string interpolation, multiline string, tagged template, es6, es2015"> <div class="card-body"> <h5><i class="bi bi-file-text" aria-hidden="true"></i> Template Literals <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -1129,7 +1135,7 @@ console.log(tagged); // Learn <mark>JavaScript</mark>, it's <mark>awesome</mark> </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-optional-chaining"> + <div class="info-card card-modern" id="card-js-optional-chaining" data-keywords="optional chaining, ?., nullish coalescing, ??, null, undefined, safe navigation, default value, es2020, es11"> <div class="card-body"> <h5><i class="bi bi-question-circle" aria-hidden="true"></i> Opt. Chaining & Nullish Coalescing <span class="version-tag">ES2020+</span></h5> <div class="card-content-wrapper"> @@ -1176,7 +1182,7 @@ console.log(`Missing (??): ${missingSetting_NC}`); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-modern" id="card-js-map-set"> + <div class="info-card card-modern" id="card-js-map-set" data-keywords="Map, Set, WeakMap, WeakSet, collection, iterable, unique values, key-value pairs, es6, es2015"> <div class="card-body"> <h5><i class="bi bi-collection" aria-hidden="true"></i> Map & Set <span class="version-tag">ES6+</span></h5> <div class="card-content-wrapper"> @@ -1229,7 +1235,7 @@ const unique = [...new Set(numbers)]; // [5, 6, 7] <h2 class="section-title" id="section-browser-title">Browser Environment & Web APIs</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-dom"> <!-- Updated --> + <div class="info-card card-browser" id="card-browser-dom" data-keywords="dom, document object model, node tree, document, element, text, attribute, traversal, parentNode, childNodes, querySelector, getElementById"> <div class="card-body"> <h5><i class="bi bi-diagram-2" aria-hidden="true"></i> The DOM</h5> <div class="card-content-wrapper"> @@ -1251,7 +1257,7 @@ const unique = [...new Set(numbers)]; // [5, 6, 7] </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-dom-manip"> <!-- Updated --> + <div class="info-card card-browser" id="card-browser-dom-manip" data-keywords="dom manipulation, querySelector, querySelectorAll, getElementById, textContent, innerHTML, setAttribute, getAttribute, classList, style, createElement, appendChild, remove, insertBefore, closest"> <div class="card-body"> <h5><i class="bi bi-pencil-square" aria-hidden="true"></i> DOM Manipulation</h5> <div class="card-content-wrapper"> @@ -1284,7 +1290,7 @@ const unique = [...new Set(numbers)]; // [5, 6, 7] </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-events"> <!-- Updated --> + <div class="info-card card-browser" id="card-browser-events" data-keywords="event handling, addEventListener, removeEventListener, event object, target, currentTarget, preventDefault, stopPropagation, bubbling, capturing, delegation, click, mouseover, keydown, submit, load"> <div class="card-body"> <h5><i class="bi bi-mouse" aria-hidden="true"></i> Event Handling</h5> <div class="card-content-wrapper"> @@ -1338,7 +1344,7 @@ button.addEventListener('click', handleClick); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-fetch"> <!-- Updated --> + <div class="info-card card-browser" id="card-browser-fetch" data-keywords="fetch api, http, request, response, promise, async, await, get, post, headers, body, json, text, cors, ajax, XMLHttpRequest, XHR"> <div class="card-body"> <h5><i class="bi bi-cloud-download" aria-hidden="true"></i> Fetch API</h5> <div class="card-content-wrapper"> @@ -1394,7 +1400,7 @@ createUser({ name: 'Jane Doe', job: 'Architect' }); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-storage"> + <div class="info-card card-browser" id="card-browser-storage" data-keywords="web storage, localStorage, sessionStorage, getItem, setItem, removeItem, clear, cookies, indexeddb, cache"> <div class="card-body"> <h5><i class="bi bi-hdd" aria-hidden="true"></i> Web Storage</h5> <div class="card-content-wrapper"> @@ -1435,7 +1441,7 @@ localStorage.removeItem('username'); </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-timers"> + <div class="info-card card-browser" id="card-browser-timers" data-keywords="timers, animation, setTimeout, clearTimeout, setInterval, clearInterval, requestAnimationFrame, rAF, cancelAnimationFrame, delay, interval"> <div class="card-body"> <h5><i class="bi bi-clock" aria-hidden="true"></i> Timers & Animation</h5> <div class="card-content-wrapper"> @@ -1489,7 +1495,7 @@ requestAnimationFrame(step); // Start the animation loop </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-browser" id="card-browser-bom"> + <div class="info-card card-browser" id="card-browser-bom" data-keywords="bom, browser object model, window, navigator, location, history, screen, alert, prompt, open, userAgent, geolocation, pushState, replaceState"> <div class="card-body"> <h5><i class="bi bi-window" aria-hidden="true"></i> Browser Object Model (BOM)</h5> <div class="card-content-wrapper"> @@ -1533,7 +1539,7 @@ requestAnimationFrame(step); // Start the animation loop <div class="row"> <!-- React Cards --> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-overview"> <!-- Updated --> + <div class="info-card card-react" id="card-react-overview" data-keywords="react, virtual dom, components, declarative, jsx, hooks, props, state, unidirectional data flow, facebook"> <div class="card-body"> <h5><i class="bi bi-gem" aria-hidden="true"></i> React Overview</h5> <div class="card-content-wrapper"> @@ -1557,7 +1563,7 @@ requestAnimationFrame(step); // Start the animation loop </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-jsx"> <!-- Updated --> + <div class="info-card card-react" id="card-react-jsx" data-keywords="jsx, javascript xml, react, createElement, className, htmlFor, style attribute, fragment, expressions"> <div class="card-body"> <h5><i class="bi bi-filetype-jsx" aria-hidden="true"></i> JSX</h5> <div class="card-content-wrapper"> @@ -1592,7 +1598,7 @@ requestAnimationFrame(step); // Start the animation loop </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-components"> + <div class="info-card card-react" id="card-react-components" data-keywords="react components, functional components, class components, props, state, composition, reusable, ui"> <div class="card-body"> <h5><i class="bi bi-puzzle" aria-hidden="true"></i> Components (React)</h5> <div class="card-content-wrapper"> @@ -1644,7 +1650,7 @@ function Counter() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-hooks"> <!-- Updated --> + <div class="info-card card-react" id="card-react-hooks" data-keywords="react hooks, useState, useEffect, useContext, useReducer, useCallback, useMemo, useRef, useLayoutEffect, state, lifecycle, side effects, context, refs, memoization"> <div class="card-body"> <h5><i class="bi bi-life-preserver" aria-hidden="true"></i> State & Lifecycle (Hooks) <span class="version-tag">React 16.8+</span></h5> <div class="card-content-wrapper"> @@ -1672,7 +1678,7 @@ function Counter() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-events"> + <div class="info-card card-react" id="card-react-events" data-keywords="react events, event handling, onClick, onChange, syntheticEvent, preventDefault, stopPropagation, event listeners"> <div class="card-body"> <h5><i class="bi bi-cursor" aria-hidden="true"></i> Handling Events (React)</h5> <div class="card-content-wrapper"> @@ -1727,7 +1733,7 @@ function Parent() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-conditional"> + <div class="info-card card-react" id="card-react-conditional" data-keywords="react conditional rendering, if else, ternary operator, logical AND, &&, null rendering"> <div class="card-body"> <h5><i class="bi bi-toggles" aria-hidden="true"></i> Conditional Rendering</h5> <div class="card-content-wrapper"> @@ -1774,7 +1780,7 @@ function UserProfile(){ return <span>User Profile</span>; } </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-react" id="card-react-lists"> + <div class="info-card card-react" id="card-react-lists" data-keywords="react lists, keys, map, array iteration, unique key, stable key, list rendering"> <div class="card-body"> <h5><i class="bi bi-list-task" aria-hidden="true"></i> Lists & Keys</h5> <div class="card-content-wrapper"> @@ -1827,7 +1833,7 @@ function TodoList({ todos }) { </div> <!-- Other Framework Cards --> <div class="col-lg-4 col-md-6"> - <div class="info-card card-vue" id="card-vue-overview"> <!-- Updated --> + <div class="info-card card-vue" id="card-vue-overview" data-keywords="vue, vue.js, progressive framework, templates, reactivity, components, options api, composition api, v-bind, v-on, v-model, sfc, single file components"> <div class="card-body"> <h5><i class="bi bi-triangle" aria-hidden="true"></i> Vue.js Overview</h5> <div class="card-content-wrapper"> @@ -1849,7 +1855,7 @@ function TodoList({ todos }) { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-angular" id="card-angular-overview"> <!-- Updated --> + <div class="info-card card-angular" id="card-angular-overview" data-keywords="angular, typescript, components, modules, ngmodules, dependency injection, di, services, routing, rxjs, observables, angular cli, decorators, data binding, directives, pipes"> <div class="card-body"> <h5><i class="bi bi-shield-shaded" aria-hidden="true"></i> Angular Overview</h5> <div class="card-content-wrapper"> @@ -1875,7 +1881,7 @@ function TodoList({ todos }) { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-svelte" id="card-svelte-overview"> <!-- Updated --> + <div class="info-card card-svelte" id="card-svelte-overview" data-keywords="svelte, sveltekit, compiler, no virtual dom, reactive assignments, stores, transitions, animations, .svelte files, context api"> <div class="card-body"> <h5><i class="bi bi-fire" aria-hidden="true"></i> Svelte Overview</h5> <div class="card-content-wrapper"> @@ -1889,7 +1895,7 @@ function TodoList({ todos }) { <li><strong>Compiler Approach:</strong> Shifts work to build time, resulting in smaller bundles and faster runtime performance.</li> <li><strong>No Virtual DOM:</strong> Generates highly optimized imperative code to update the DOM directly when state changes.</li> <li><strong>Reactivity:</strong> Variable assignments (`count = 1`, `user.name = 'X'`) automatically trigger updates where those variables are used. Use `$: ` for reactive declarations/statements.</li> - <li><strong>Single File Components (`.svelte`):</strong> Combine `<script>`, template (HTML-like), and `<style>` (scoped by default).</li> + <li><strong>Single File Components (`.svelte`):</strong> Combine `<script>`, template (HTML-like), and `<style>` (scoped by default).</li> <li><strong>Stores:</strong> Built-in system (`writable`, `readable`, `derived`) for managing state outside components and sharing it reactively.</li> <li><strong>Transitions & Animations:</strong> First-class directives (`transition:`, `animate:`, `in:`, `out:`) for smooth UI effects.</li> <li><strong>Context API:</strong> Similar to React/Vue for avoiding prop drilling (`setContext`, `getContext`).</li> @@ -1906,7 +1912,7 @@ function TodoList({ todos }) { <h2 class="section-title" id="section-state-title">State Management Patterns & Libraries</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state" id="card-state-concepts"> <!-- Updated --> + <div class="info-card card-state" id="card-state-concepts" data-keywords="state management, local state, global state, prop drilling, complexity, debugging, sharing state"> <div class="card-body"> <h5><i class="bi bi-diagram-3-fill" aria-hidden="true"></i> State Management Concepts</h5> <div class="card-content-wrapper"> @@ -1932,7 +1938,7 @@ function TodoList({ todos }) { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state card-react" id="card-state-context"> <!-- Updated --> + <div class="info-card card-state card-react" id="card-state-context" data-keywords="react context api, createContext, Provider, useContext, Consumer, prop drilling, global state, theme, authentication"> <div class="card-body"> <h5><i class="bi bi-share" aria-hidden="true"></i> React Context API <span class="version-tag">React 16.3+</span></h5> <div class="card-content-wrapper"> @@ -1995,7 +2001,7 @@ function MyComponent() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state" id="card-state-redux"> <!-- Updated --> + <div class="info-card card-state" id="card-state-redux" data-keywords="redux, rtk, redux toolkit, store, action, reducer, dispatch, selector, middleware, immutable, createSlice, configureStore, useSelector, useDispatch, createAsyncThunk, immer"> <div class="card-body"> <h5><i class="bi bi-arrow-repeat" aria-hidden="true"></i> Redux / RTK</h5> <div class="card-content-wrapper"> @@ -2072,7 +2078,7 @@ function CounterComponent() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state card-react" id="card-state-zustand"> <!-- Updated --> + <div class="info-card card-state card-react" id="card-state-zustand" data-keywords="zustand, react state management, hooks, minimalist, create, set, get, store, global state, pmndrs"> <div class="card-body"> <h5><i class="bi bi-lightbulb" aria-hidden="true"></i> Zustand</h5> <div class="card-content-wrapper"> @@ -2146,7 +2152,7 @@ function Controls() { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state card-vue" id="card-state-pinia"> + <div class="info-card card-state card-vue" id="card-state-pinia" data-keywords="pinia, vue state management, vuex, defineStore, state, getters, actions, storeToRefs, composition api, options api"> <div class="card-body"> <h5><i class="bi bi-pin-map" aria-hidden="true"></i> Pinia (Vue)</h5> <div class="card-content-wrapper"> @@ -2203,7 +2209,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-state card-angular" id="card-state-ngrx"> + <div class="info-card card-state card-angular" id="card-state-ngrx" data-keywords="ngrx, angular state management, redux, rxjs, store, action, reducer, effect, selector, createAction, createReducer, createEffect, createSelector, observable"> <div class="card-body"> <h5><i class="bi bi-recycle" aria-hidden="true"></i> NgRx (Angular)</h5> <div class="card-content-wrapper"> @@ -2233,7 +2239,7 @@ const { increment } = counterStore; <h2 class="section-title" id="section-tooling-title">Ecosystem & Tooling</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-npm"> <!-- Updated --> + <div class="info-card card-tooling" id="card-tooling-npm" data-keywords="npm, yarn, pnpm, package manager, node_modules, package.json, package-lock.json, dependencies, devDependencies, scripts, npx"> <div class="card-body"> <h5><i class="bi bi-box" aria-hidden="true"></i> Package Managers</h5> <div class="card-content-wrapper"> @@ -2269,7 +2275,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-bundlers"> <!-- Updated --> + <div class="info-card card-tooling" id="card-tooling-bundlers" data-keywords="module bundler, vite, webpack, parcel, rollup, hmr, hot module replacement, code splitting, tree shaking, dev server, build tool, assets"> <div class="card-body"> <h5><i class="bi bi-boxes" aria-hidden="true"></i> Module Bundlers</h5> <div class="card-content-wrapper"> @@ -2305,7 +2311,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-transpilers"> + <div class="info-card card-tooling" id="card-tooling-transpilers" data-keywords="transpiler, babel, typescript, tsc, es6+, jsx, polyfill, core-js, preset-env, preset-react, preset-typescript, esbuild"> <div class="card-body"> <h5><i class="bi bi-arrow-left-right" aria-hidden="true"></i> Transpilers (Babel)</h5> <div class="card-content-wrapper"> @@ -2334,7 +2340,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-linters"> <!-- Updated --> + <div class="info-card card-tooling" id="card-tooling-linters" data-keywords="linter, formatter, eslint, prettier, code style, code quality, static analysis, eslintrc, prettierrc, husky, lint-staged"> <div class="card-body"> <h5><i class="bi bi-check2-square" aria-hidden="true"></i> Linters & Formatters</h5> <div class="card-content-wrapper"> @@ -2362,7 +2368,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-node"> + <div class="info-card card-tooling" id="card-tooling-node" data-keywords="node.js, nodejs, runtime, npm, build tools, task runner, testing framework, ssr, server-side rendering, nvm"> <div class="card-body"> <h5><i class="bi bi-motherboard" aria-hidden="true"></i> Node.js (Frontend Tooling)</h5> <div class="card-content-wrapper"> @@ -2384,7 +2390,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-devtools"> <!-- Updated --> + <div class="info-card card-tooling" id="card-tooling-devtools" data-keywords="browser devtools, chrome devtools, firefox devtools, elements, console, sources, network, performance, memory, application, lighthouse, debugger, inspector"> <div class="card-body"> <h5><i class="bi bi-tools" aria-hidden="true"></i> Browser DevTools</h5> <div class="card-content-wrapper"> @@ -2410,7 +2416,7 @@ const { increment } = counterStore; </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-testing"> <!-- Updated --> + <div class="info-card card-tooling" id="card-tooling-testing" data-keywords="testing, unit testing, integration testing, component testing, e2e testing, end-to-end, jest, vitest, mocha, testing library, cypress, playwright, assertion, mock, stub, tdd, bdd"> <div class="card-body"> <h5><i class="bi bi-clipboard2-check" aria-hidden="true"></i> Testing Frameworks</h5> <div class="card-content-wrapper"> @@ -2456,7 +2462,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-tooling" id="card-tooling-ts"> <!-- Updated - Placed TS here as Tooling --> + <div class="info-card card-tooling" id="card-tooling-ts" data-keywords="typescript, ts, static typing, types, interfaces, generics, enums, tsc, tsconfig.json, type checking, type inference"> <div class="card-body"> <h5><i class="bi bi-filetype-tsx" aria-hidden="true"></i> TypeScript Integration</h5> <div class="card-content-wrapper"> @@ -2496,7 +2502,7 @@ test('adds 1 + 2 to equal 3', () => { <h2 class="section-title" id="section-advanced-title">Advanced Concepts & Patterns</h2> <div class="row"> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-perf"> <!-- Updated --> + <div class="info-card card-advanced" id="card-advanced-perf" data-keywords="performance, optimization, code splitting, lazy loading, memoization, virtualization, image optimization, critical css, caching, preloading, prefetching, web workers, debouncing, throttling, core web vitals, lcp, fid, inp, cls"> <div class="card-body"> <h5><i class="bi bi-speedometer2" aria-hidden="true"></i> Performance Optimization</h5> <div class="card-content-wrapper"> @@ -2530,7 +2536,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-web-perf-metrics"> + <div class="info-card card-advanced" id="card-advanced-web-perf-metrics" data-keywords="web performance metrics, core web vitals, lcp, largest contentful paint, fid, first input delay, inp, interaction to next paint, cls, cumulative layout shift, ttfb, time to first byte, fcp, first contentful paint, tti, time to interactive, lighthouse, rum, crux"> <div class="card-body"> <h5><i class="bi bi-graph-up" aria-hidden="true"></i> Web Performance Metrics</h5> <div class="card-content-wrapper"> @@ -2560,7 +2566,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-security"> + <div class="info-card card-advanced" id="card-advanced-security" data-keywords="security, xss, cross-site scripting, csrf, cross-site request forgery, owasp, sanitize input, csp, content security policy, https, dompurify, clickjacking, idor, dependency vulnerabilities, npm audit"> <div class="card-body"> <h5><i class="bi bi-shield-lock" aria-hidden="true"></i> Security Best Practices</h5> <div class="card-content-wrapper"> @@ -2601,7 +2607,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-a11y"> <!-- Updated --> + <div class="info-card card-advanced" id="card-advanced-a11y" data-keywords="accessibility, a11y, wcag, pour, semantic html, aria, keyboard navigation, focus management, color contrast, screen reader, axe, lighthouse"> <div class="card-body"> <h5><i class="bi bi-person-check" aria-hidden="true"></i> Accessibility (a11y)</h5> <div class="card-content-wrapper"> @@ -2632,7 +2638,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-pwa"> <!-- Updated --> + <div class="info-card card-advanced" id="card-advanced-pwa" data-keywords="pwa, progressive web app, service worker, web app manifest, offline, installable, cache storage api, push notifications, workbox"> <div class="card-body"> <h5><i class="bi bi-phone" aria-hidden="true"></i> Progressive Web Apps (PWAs)</h5> <div class="card-content-wrapper"> @@ -2661,7 +2667,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-ssr-ssg"> + <div class="info-card card-advanced" id="card-advanced-ssr-ssg" data-keywords="ssr, server-side rendering, ssg, static site generation, csr, client-side rendering, isr, incremental static regeneration, hydration, ttfb, fcp, tti, seo, next.js, nuxt, sveltekit, remix, astro, islands architecture"> <div class="card-body"> <h5><i class="bi bi-server" aria-hidden="true"></i> SSR / SSG / CSR</h5> <div class="card-content-wrapper"> @@ -2698,7 +2704,7 @@ test('adds 1 + 2 to equal 3', () => { </div> </div> <div class="col-lg-4 col-md-6"> - <div class="info-card card-advanced" id="card-advanced-design-patterns"> + <div class="info-card card-advanced" id="card-advanced-design-patterns" data-keywords="design patterns, creational, structural, behavioral, module, singleton, observer, facade, command, factory, software patterns"> <div class="card-body"> <h5><i class="bi bi-bricks" aria-hidden="true"></i> Design Patterns in JS</h5> <div class="card-content-wrapper"> @@ -2748,7 +2754,7 @@ export function getCount() { return count; } <h2 class="section-title" id="section-arch-title">Key Considerations for Frontend Developers & Architects</h2> <div class="row"> <div class="col-12"> - <div class="info-card card-arch" id="card-arch-summary"> <!-- Updated --> + <div class="info-card card-arch" id="card-arch-summary" data-keywords="frontend architecture, scalability, maintainability, performance, security, developer experience, dx, framework selection, state management, rendering strategy, component architecture, data fetching, build tooling, testing, typescript, monorepo, styling, deployment, ci/cd"> <div class="card-body"> <h5 class="text-center"><i class="bi bi-building-gear" aria-hidden="true"></i> Architectural Decision Points</h5> <div class="card-content-wrapper"> @@ -2784,377 +2790,33 @@ export function getCount() { return count; } </div> <!-- /container#main-container --> <footer class="container text-center pb-3"> - <p class="mb-1">© <span id="currentYear"></span> David Veksler Cheatsheets</p> + <p class="mb-2" style="font-size: 0.8em;">Last Updated: <span id="lastUpdatedDate">May 12, 2025</span></p> <!-- Updated Date --> <div> - <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" title="External link: MDN JavaScript Guide" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" title="MDN JavaScript Guide" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-book" aria-hidden="true"></i> MDN JS Docs </a> - <a href="https://react.dev/" title="External link: Official React Documentation" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://react.dev/" title="Official React Documentation" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-gem" aria-hidden="true"></i> React Docs </a> - <a href="https://web.dev/" title="External link: web.dev by Google" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://web.dev/" title="web.dev by Google" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-google" aria-hidden="true"></i> web.dev </a> - <a href="https://roadmap.sh/frontend" title="External link: Frontend Developer Roadmap" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://roadmap.sh/frontend" title="Frontend Developer Roadmap" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-map" aria-hidden="true"></i> Frontend Roadmap </a> </div> <div> - <a href="https://www.linkedin.com/in/davidveksler/" title="External link: David Veksler on LinkedIn" target="_blank" rel="noopener noreferrer" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://www.linkedin.com/in/davidveksler/" title="David Veksler on LinkedIn" target="_blank" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-linkedin" aria-hidden="true"></i> LinkedIn </a> - <a href="https://cheatsheets.davidveksler.com/" title="External link: Browse All Cheatsheets by David Veksler" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> + <a href="https://cheatsheets.davidveksler.com/" title="Browse All Cheatsheets by David Veksler" class="mx-2 link-secondary" data-bs-toggle="tooltip" data-bs-placement="top"> <i class="bi bi-collection" aria-hidden="true"></i> All Cheatsheets </a> </div> </footer> -<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> -<!-- Note: The script below contains the logic for interactivity (filters, search, lines, collapse) --> -<!-- It assumes Prism.js and Bootstrap JS are loaded before it executes. --> -<script> -document.addEventListener('DOMContentLoaded', () => { - // --- Initial Prism.js highlighting for visible code blocks --- - if (typeof Prism !== 'undefined') { - try { - Prism.highlightAll(); - } catch (e) { - console.warn("Prism.highlightAll() error on initial load:", e); - } - } - - const mainContainer = document.getElementById('main-container'); - const searchBox = document.getElementById('search-box'); - const categoryFiltersContainer = document.getElementById('category-filters'); - const noResultsDiv = document.getElementById('no-results'); - const allSchemaContainers = Array.from(document.querySelectorAll('.schema-container')); - // const allInfoCards = Array.from(document.querySelectorAll('.info-card')); // Not directly used, cards are queried per section - - let currentHoverState = { card: null, line: null }; - let activeFilter = 'all'; // To store the currently active category filter - - // Initialize Bootstrap Tooltips (Defensive Check) - if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { - const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); - [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); - } else { - console.warn('Bootstrap Tooltip component not found. Static tooltips may not work or might throw errors if initialized later.'); - } - - - // --- Initialize Filters & Search --- - function initializeFiltersAndSearch() { - // Create filter buttons - const allButton = document.createElement('button'); - allButton.type = 'button'; - allButton.classList.add('btn', 'btn-outline-secondary', 'filter-btn', 'active'); - allButton.textContent = 'All Categories'; - allButton.dataset.filter = 'all'; - allButton.setAttribute('data-bs-toggle', 'tooltip'); - allButton.setAttribute('data-bs-placement', 'top'); - allButton.setAttribute('title', 'Show all categories'); - categoryFiltersContainer.appendChild(allButton); - if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { // Dynamic tooltip init - new bootstrap.Tooltip(allButton); - } - - - const btnGroup = document.createElement('div'); - btnGroup.classList.add('btn-group', 'flex-wrap'); - btnGroup.setAttribute('role', 'group'); - - allSchemaContainers.forEach(section => { - const sectionId = section.dataset.sectionId; - const sectionName = section.dataset.sectionName || section.querySelector('.section-title').textContent.trim().replace(' Features (ES6+)', '').replace(' Fundamentals', '').replace(' Environment & Web APIs','').replace(' & Libraries','').replace(' Patterns & Libraries','').replace(' Ecosystem & Tooling','').replace(' Concepts & Patterns','').replace(' Considerations for Frontend Developers & Architects',''); - const button = document.createElement('button'); - button.type = 'button'; - button.classList.add('btn', 'btn-outline-secondary', 'filter-btn'); - button.textContent = sectionName; - button.dataset.filter = sectionId; - button.setAttribute('data-bs-toggle', 'tooltip'); - button.setAttribute('data-bs-placement', 'top'); - button.setAttribute('title', `Filter by ${sectionName}`); - btnGroup.appendChild(button); - if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { // Dynamic tooltip init - new bootstrap.Tooltip(button); - } - }); - categoryFiltersContainer.appendChild(btnGroup); - - // Event listener for search box - searchBox.addEventListener('input', () => { - applyFiltersAndSearch(); - }); - - // Event listener for filter buttons (using event delegation) - categoryFiltersContainer.addEventListener('click', (event) => { - if (event.target.classList.contains('filter-btn')) { - document.querySelectorAll('#category-filters .filter-btn').forEach(btn => btn.classList.remove('active')); - event.target.classList.add('active'); - activeFilter = event.target.dataset.filter; - applyFiltersAndSearch(); - clearHoverState(true); // Clear lines when filtering - setTimeout(positionLines, 50); // Allow DOM changes to settle before repositioning if needed - } - }); - } - - function applyFiltersAndSearch() { - const searchTerm = searchBox.value.toLowerCase().trim(); - let itemsFound = 0; - // let sectionsWithVisibleItems = 0; // Not strictly needed for current logic - - allSchemaContainers.forEach(section => { - const sectionId = section.dataset.sectionId; - let sectionHasVisibleCards = false; - - const cardsInSection = Array.from(section.querySelectorAll('.info-card')); - - cardsInSection.forEach(card => { - const cardTitle = card.querySelector('h5') ? card.querySelector('h5').textContent.toLowerCase() : ''; - const cardSummary = card.querySelector('p.summary') ? card.querySelector('p.summary').textContent.toLowerCase() : ''; - const cardDetailsCollapse = card.querySelector('.collapse-content'); - const cardDetailsText = (cardDetailsCollapse && !cardDetailsCollapse.textContent.includes('<!-- Detailed content needed -->') && !cardDetailsCollapse.textContent.includes('Content placeholder for')) ? cardDetailsCollapse.textContent.toLowerCase() : ''; - const versionTag = card.querySelector('.version-tag') ? card.querySelector('.version-tag').textContent.toLowerCase() : ''; - const keywords = card.dataset.keywords ? card.dataset.keywords.toLowerCase() : ''; // Added keyword search - - - const cardTextContent = `${cardTitle} ${cardSummary} ${cardDetailsText} ${versionTag} ${keywords}`; // Include keywords - - const matchesSearch = searchTerm === '' || cardTextContent.includes(searchTerm); - const matchesFilter = activeFilter === 'all' || sectionId === activeFilter; - - const column = card.closest('.col-lg-4.col-md-6'); // Get parent column - - if (matchesSearch && matchesFilter) { - if(column) column.style.display = ''; // Show column - sectionHasVisibleCards = true; - itemsFound++; - } else { - if(column) column.style.display = 'none'; // Hide column - } - }); - - // Show/hide entire section based on filter and if it has visible cards - if ((activeFilter === 'all' || sectionId === activeFilter) && sectionHasVisibleCards) { - section.style.display = ''; - // sectionsWithVisibleItems++; // Increment if tracking this - } else if (searchTerm !== '' && (activeFilter === 'all' || sectionId === activeFilter) && !sectionHasVisibleCards) { - // Section matches filter, but no cards match search, so hide section - section.style.display = 'none'; - } else if (activeFilter !== 'all' && sectionId !== activeFilter) { - // Section does not match filter - section.style.display = 'none'; - } else if (searchTerm === '' && activeFilter === 'all') { - // No search, all filter, show/hide based only on whether it has cards - section.style.display = sectionHasVisibleCards ? '' : 'none'; - } else if (searchTerm === '' && sectionId === activeFilter) { - // No search, specific filter, show/hide based only on whether it has cards - section.style.display = sectionHasVisibleCards ? '' : 'none'; - } else { - // Default case: hide section if none of the above conditions met - section.style.display = 'none'; - } - }); - - noResultsDiv.style.display = itemsFound === 0 && (searchTerm !== '' || activeFilter !== 'all') ? 'block' : 'none'; - - // Re-highlight code blocks if Prism is loaded and items are visible - if (typeof Prism !== 'undefined' && (itemsFound > 0 || (searchTerm === '' && activeFilter === 'all'))) { - try { - Prism.highlightAll(); - } catch (e) { - console.warn("Prism.highlightAll() error during filter/search:", e); - } - } - } - - // --- LeaderLine Drawing Logic --- - function debounce(func, wait) { - let timeout; - return function executedFunction(...args) { - const later = () => { - clearTimeout(timeout); - func(...args); - }; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - }; - } - - function getElementColor(element) { - if (!element) return '#6c757d'; // Default tooling color - // Get color directly from the element first (for framework overrides) - let color = window.getComputedStyle(element).getPropertyValue('--db-category-color').trim(); - // If not set directly or invalid, try the parent section - if (!color || color === 'initial' || color === 'inherit' || color === 'var(--js-color-frameworks)') { // Check against default framework color too - const section = element.closest('.schema-container'); - if (section) { - color = window.getComputedStyle(section).getPropertyValue('--db-category-color').trim(); - } - } - return color || '#6c757d'; // Fallback to default - } - - - function clearHoverState(forceClear = false) { - const isMouseStillOverCard = !forceClear && currentHoverState.card && currentHoverState.card.matches(':hover'); - - if (forceClear || !isMouseStillOverCard) { - if (currentHoverState.line) { - try { currentHoverState.line.remove(); } catch (e) { /* Ignore error if line already gone */ } - currentHoverState.line = null; - } - if (currentHoverState.card) { - currentHoverState.card.classList.remove('is-highlighted'); - currentHoverState.card = null; - } - } - } - - function applyHoverState(card) { - // Ensure card is visible before drawing line - const column = card.closest('.col-lg-4.col-md-6'); - if (!card || card === currentHoverState.card || (column && column.style.display === 'none')) return; - - - clearHoverState(true); // Force clear previous before applying new - - const schemaContainer = card.closest('.schema-container'); - // Ensure the container itself is visible too - if (!schemaContainer || schemaContainer.style.display === 'none') return; - - const sectionHeader = schemaContainer.querySelector('.section-title'); - - // Check if LeaderLine is available - const canDrawLine = sectionHeader && card.id && sectionHeader.id && - sectionHeader.offsetParent !== null && card.offsetParent !== null && - typeof LeaderLine !== 'undefined'; - - currentHoverState.card = card; - card.classList.add('is-highlighted'); - - if (canDrawLine) { - try { - const cardColor = getElementColor(card); - currentHoverState.line = new LeaderLine( // Assign to currentHoverState.line - sectionHeader, card, - { - color: cardColor, size: 2.5, path: 'fluid', - startSocket: 'bottom', endSocket: 'top', - startSocketGravity: [0, -25], endSocketGravity: [0, 25], - dash: { animation: true, len: 8, gap: 4 }, - } - ); - } catch (e) { - console.error("LeaderLine error:", e); - clearHoverState(true); // Clear if line creation failed - } - } - } - - mainContainer.addEventListener('mouseover', (event) => { - const targetCard = event.target.closest('.info-card'); - const column = targetCard ? targetCard.closest('.col-lg-4.col-md-6') : null; - if (targetCard && column && column.style.display !== 'none' && targetCard !== currentHoverState.card) { - applyHoverState(targetCard); - } - }); - - mainContainer.addEventListener('mouseout', (event) => { - const currentCard = currentHoverState.card; // Card that had the line - if (currentCard && event.target.closest('.info-card') === currentCard) { // Mouse moved out of the card itself - const relatedTarget = event.relatedTarget; // Element mouse is moving to - // Clear if mouse moves out of the card and not into another part of the same card or a tooltip/line - if (!currentCard.contains(relatedTarget) && (!relatedTarget || !relatedTarget.closest('.info-card'))) { - setTimeout(() => { // Delay to allow for re-entry or movement to related elements - // Double check if mouse isn't back over the card before clearing - if (currentHoverState.card && !currentHoverState.card.matches(':hover')) { // Only clear if mouse is *really* off the card - clearHoverState(false); // Not forcing, check if mouse is still over - } - }, 50); - } - } - }); - - const positionLines = debounce(() => { - if (currentHoverState.line && currentHoverState.card) { // Ensure card and line still exist - try { - if (typeof currentHoverState.line.position === 'function') { - const startElem = currentHoverState.line.start; - const endElem = currentHoverState.line.end; - const endElemColumn = endElem ? endElem.closest('.col-lg-4.col-md-6') : null; - - // Check if both elements are still in DOM, visible, and card's column is visible - if (startElem && endElem && document.body.contains(startElem) && document.body.contains(endElem) && - startElem.offsetParent !== null && endElem.offsetParent !== null && - window.getComputedStyle(startElem).display !== 'none' && window.getComputedStyle(endElem).display !== 'none' && - endElemColumn && endElemColumn.style.display !== 'none' ) { - currentHoverState.line.position(); - } else { - // If elements are gone or hidden, remove the line and clear state - clearHoverState(true); // Force clear - } - } else { - // If line object is invalid for some reason - clearHoverState(true); // Force clear - } - } catch (e) { - console.warn("LeaderLine reposition error:", e); - clearHoverState(true); // Force clear on error - } - } - }, 100); - - window.addEventListener('resize', positionLines); - window.addEventListener('scroll', positionLines, { passive: true }); - - // --- Collapse Handling --- - const collapseElements = document.querySelectorAll('.collapse'); - collapseElements.forEach(collapseEl => { - const button = document.querySelector(`.details-toggle[data-bs-target="#${collapseEl.id}"]`); - const iconEl = button ? button.querySelector('.bi') : null; - - if (button && iconEl) { - const updateIconStateAndHighlight = () => { - const isShown = collapseEl.classList.contains('show'); - if (isShown) { - iconEl.classList.remove('bi-chevron-down'); iconEl.classList.add('bi-chevron-up'); - button.setAttribute('aria-expanded', 'true'); - // Highlight code only when shown and Prism exists - if (typeof Prism !== 'undefined') { - try { - // Use highlightAllUnder for efficiency on specific section - Prism.highlightAllUnder(collapseEl); - } catch (e) { - console.warn("Prism.highlightAllUnder() error on collapse show:", e); - } - } - } else { - iconEl.classList.remove('bi-chevron-up'); iconEl.classList.add('bi-chevron-down'); - button.setAttribute('aria-expanded', 'false'); - } - }; - updateIconStateAndHighlight(); // Initial state check - - // Use bootstrap events for reliability - collapseEl.addEventListener('show.bs.collapse', () => { updateIconStateAndHighlight(); setTimeout(positionLines, 50); }); // Update icon immediately, reposition line slightly after animation starts - collapseEl.addEventListener('shown.bs.collapse', positionLines); // Reposition line fully after animation ends - collapseEl.addEventListener('hide.bs.collapse', () => { updateIconStateAndHighlight(); setTimeout(positionLines, 50); }); // Update icon immediately, reposition line slightly after animation starts - collapseEl.addEventListener('hidden.bs.collapse', positionLines); // Reposition line fully after animation ends - } - }); - - // --- Footer Year --- - document.getElementById('currentYear').textContent = new Date().getFullYear(); - - // --- Initial Setup Calls --- - initializeFiltersAndSearch(); - applyFiltersAndSearch(); // Apply initial filter/search state (which also triggers Prism highlighting) -}); -</script> </body> -</html> \ No newline at end of file +</html> --- /dev/null +++ b/javascript-for-architects.js @@ -0,0 +1,341 @@ +document.addEventListener('DOMContentLoaded', () => { + // --- Initial Prism.js highlighting for visible code blocks --- + if (typeof Prism !== 'undefined') { + try { + Prism.highlightAll(); + } catch (e) { + console.warn("Prism.highlightAll() error on initial load:", e); + } + } + + const mainContainer = document.getElementById('main-container'); + const searchBox = document.getElementById('search-box'); + const categoryFiltersContainer = document.getElementById('category-filters'); + const noResultsDiv = document.getElementById('no-results'); + const allSchemaContainers = Array.from(document.querySelectorAll('.schema-container')); + + let currentHoverState = { card: null, line: null }; + let activeFilter = 'all'; // To store the currently active category filter + + // Initialize Bootstrap Tooltips (Defensive Check) + if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + } else { + console.warn('Bootstrap Tooltip component not found. Static tooltips may not work or might throw errors if initialized later.'); + } + + + // --- Initialize Filters & Search --- + function initializeFiltersAndSearch() { + // Create filter buttons + const allButton = document.createElement('button'); + allButton.type = 'button'; + allButton.classList.add('btn', 'btn-outline-secondary', 'filter-btn', 'active'); + allButton.textContent = 'All Categories'; + allButton.dataset.filter = 'all'; + allButton.setAttribute('data-bs-toggle', 'tooltip'); + allButton.setAttribute('data-bs-placement', 'top'); + allButton.setAttribute('title', 'Show all categories'); + categoryFiltersContainer.appendChild(allButton); + if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { // Dynamic tooltip init + new bootstrap.Tooltip(allButton); + } + + + const btnGroup = document.createElement('div'); + btnGroup.classList.add('btn-group', 'flex-wrap'); + btnGroup.setAttribute('role', 'group'); + + allSchemaContainers.forEach(section => { + const sectionId = section.dataset.sectionId; + // Use data-section-name directly for button text, it's cleaner + const sectionName = section.dataset.sectionName || section.querySelector('.section-title').textContent.trim(); + const button = document.createElement('button'); + button.type = 'button'; + button.classList.add('btn', 'btn-outline-secondary', 'filter-btn'); + button.textContent = sectionName; + button.dataset.filter = sectionId; + button.setAttribute('data-bs-toggle', 'tooltip'); + button.setAttribute('data-bs-placement', 'top'); + button.setAttribute('title', `Filter by ${sectionName}`); + btnGroup.appendChild(button); + if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip === 'function') { // Dynamic tooltip init + new bootstrap.Tooltip(button); + } + }); + categoryFiltersContainer.appendChild(btnGroup); + + // Event listener for search box + searchBox.addEventListener('input', () => { + applyFiltersAndSearch(); + }); + + // Event listener for filter buttons (using event delegation) + categoryFiltersContainer.addEventListener('click', (event) => { + if (event.target.classList.contains('filter-btn')) { + document.querySelectorAll('#category-filters .filter-btn').forEach(btn => btn.classList.remove('active')); + event.target.classList.add('active'); + activeFilter = event.target.dataset.filter; + applyFiltersAndSearch(); + clearHoverState(true); // Clear lines when filtering + setTimeout(positionLines, 50); // Allow DOM changes to settle before repositioning if needed + } + }); + } + + function applyFiltersAndSearch() { + const searchTerm = searchBox.value.toLowerCase().trim(); + let itemsFound = 0; + + allSchemaContainers.forEach(section => { + const sectionId = section.dataset.sectionId; + const isSectionActiveDueToFilter = (activeFilter === 'all' || sectionId === activeFilter); + + if (!isSectionActiveDueToFilter) { + section.style.display = 'none'; // Hide section if it doesn't match the category filter + // Also hide all its card columns to ensure they don't affect layout or future calcs + Array.from(section.querySelectorAll('.info-card')).forEach(card => { + const column = card.closest('.col-lg-4.col-md-6'); + if (column) column.style.display = 'none'; + }); + return; // Move to the next section + } + + // If we reach here, the section matches the category filter. + // Now, check cards within this section against the search term. + let sectionHasVisibleCardsMatchingSearch = false; + const cardsInSection = Array.from(section.querySelectorAll('.info-card')); + + cardsInSection.forEach(card => { + const cardTitle = card.querySelector('h5') ? card.querySelector('h5').textContent.toLowerCase() : ''; + const cardSummary = card.querySelector('p.summary') ? card.querySelector('p.summary').textContent.toLowerCase() : ''; + const cardDetailsCollapse = card.querySelector('.collapse-content'); + // Ensure cardDetailsText is empty string if content is placeholder to avoid matching placeholders + let cardDetailsText = ''; + if (cardDetailsCollapse) { + const tempText = cardDetailsCollapse.textContent.toLowerCase(); + if (!tempText.includes('<!-- detailed content needed -->') && !tempText.includes('content placeholder for')) { + cardDetailsText = tempText; + } + } + const versionTag = card.querySelector('.version-tag') ? card.querySelector('.version-tag').textContent.toLowerCase() : ''; + const keywords = card.dataset.keywords ? card.dataset.keywords.toLowerCase() : ''; + + const cardTextContent = `${cardTitle} ${cardSummary} ${cardDetailsText} ${versionTag} ${keywords}`; + const cardMatchesSearch = searchTerm === '' || cardTextContent.includes(searchTerm); + const column = card.closest('.col-lg-4.col-md-6'); + + if (cardMatchesSearch) { + if (column) column.style.display = ''; // Show column + sectionHasVisibleCardsMatchingSearch = true; + itemsFound++; + } else { + if (column) column.style.display = 'none'; // Hide column + } + }); + + // Finally, set the section's visibility + if (sectionHasVisibleCardsMatchingSearch) { + section.style.display = ''; // Show section if it has cards matching the search + } else { + // Hide section if it's active by filter but has no cards matching search (or if it was empty) + section.style.display = 'none'; + } + }); + + noResultsDiv.style.display = itemsFound === 0 && (searchTerm !== '' || activeFilter !== 'all') ? 'block' : 'none'; + + // Re-highlight code blocks if Prism is loaded and items are visible + if (typeof Prism !== 'undefined' && (itemsFound > 0 || (searchTerm === '' && activeFilter === 'all'))) { + try { + Prism.highlightAll(); + } catch (e) { + console.warn("Prism.highlightAll() error during filter/search:", e); + } + } + } + + + // --- LeaderLine Drawing Logic --- + function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + + function getElementColor(element) { + if (!element) return '#6c757d'; // Default tooling color + let color = window.getComputedStyle(element).getPropertyValue('--db-category-color').trim(); + if (!color || color === 'initial' || color === 'inherit' || color === 'var(--js-color-frameworks)') { + const section = element.closest('.schema-container'); + if (section) { + color = window.getComputedStyle(section).getPropertyValue('--db-category-color').trim(); + } + } + return color || '#6c757d'; // Fallback to default + } + + + function clearHoverState(forceClear = false) { + const isMouseStillOverCard = !forceClear && currentHoverState.card && currentHoverState.card.matches(':hover'); + + if (forceClear || !isMouseStillOverCard) { + if (currentHoverState.line) { + try { currentHoverState.line.remove(); } catch (e) { /* Ignore error if line already gone */ } + currentHoverState.line = null; + } + if (currentHoverState.card) { + currentHoverState.card.classList.remove('is-highlighted'); + currentHoverState.card = null; + } + } + } + + function applyHoverState(card) { + const column = card.closest('.col-lg-4.col-md-6'); + if (!card || card === currentHoverState.card || (column && column.style.display === 'none')) return; + + clearHoverState(true); + + const schemaContainer = card.closest('.schema-container'); + if (!schemaContainer || schemaContainer.style.display === 'none') return; + + const sectionHeader = schemaContainer.querySelector('.section-title'); + const canDrawLine = sectionHeader && card.id && sectionHeader.id && + sectionHeader.offsetParent !== null && card.offsetParent !== null && + typeof LeaderLine !== 'undefined'; + + currentHoverState.card = card; + card.classList.add('is-highlighted'); + + if (canDrawLine) { + try { + const cardColor = getElementColor(card); + currentHoverState.line = new LeaderLine( + sectionHeader, card, + { + color: cardColor, size: 2.5, path: 'fluid', + startSocket: 'bottom', endSocket: 'top', + startSocketGravity: [0, -25], endSocketGravity: [0, 25], + dash: { animation: true, len: 8, gap: 4 }, + } + ); + } catch (e) { + console.error("LeaderLine error:", e); + clearHoverState(true); + } + } + } + + mainContainer.addEventListener('mouseover', (event) => { + const targetCard = event.target.closest('.info-card'); + if (targetCard) { // Simplified condition: if it's a card, try to apply hover state. applyHoverState has internal checks. + applyHoverState(targetCard); + } + }); + + mainContainer.addEventListener('mouseout', (event) => { + const currentCard = currentHoverState.card; + if (currentCard && event.target.closest('.info-card') === currentCard) { + const relatedTarget = event.relatedTarget; + if (!currentCard.contains(relatedTarget) && (!relatedTarget || !relatedTarget.closest('.info-card'))) { + setTimeout(() => { + if (currentHoverState.card && !currentHoverState.card.matches(':hover')) { + clearHoverState(false); + } + }, 50); + } + } else if (currentCard && !event.target.closest('.info-card')) { + // If mouseout is from the container but not over a card, clear if current card is no longer hovered + setTimeout(() => { + if (currentHoverState.card && !currentHoverState.card.matches(':hover')) { + clearHoverState(false); + } + }, 50); + } + }); + + const positionLines = debounce(() => { + if (currentHoverState.line && currentHoverState.card && typeof currentHoverState.line.position === 'function') { + try { + const startElem = currentHoverState.line.start; + const endElem = currentHoverState.line.end; + const endElemColumn = endElem ? endElem.closest('.col-lg-4.col-md-6') : null; + + if (startElem && endElem && document.body.contains(startElem) && document.body.contains(endElem) && + startElem.offsetParent !== null && endElem.offsetParent !== null && + window.getComputedStyle(startElem).display !== 'none' && window.getComputedStyle(endElem).display !== 'none' && + endElemColumn && endElemColumn.style.display !== 'none' ) { + currentHoverState.line.position(); + } else { + clearHoverState(true); + } + } catch (e) { + console.warn("LeaderLine reposition error:", e); + clearHoverState(true); + } + } + }, 100); + + window.addEventListener('resize', positionLines); + window.addEventListener('scroll', positionLines, { passive: true }); + + // --- Collapse Handling --- + const collapseElements = document.querySelectorAll('.collapse'); + collapseElements.forEach(collapseEl => { + const button = document.querySelector(`.details-toggle[data-bs-target="#${collapseEl.id}"]`); + const iconEl = button ? button.querySelector('.bi') : null; + + if (button && iconEl) { + const updateIconStateAndHighlight = () => { + const isShown = collapseEl.classList.contains('show'); + button.setAttribute('aria-expanded', isShown.toString()); + if (isShown) { + iconEl.classList.remove('bi-chevron-down'); + iconEl.classList.add('bi-chevron-up'); + if (typeof Prism !== 'undefined') { + try { + Prism.highlightAllUnder(collapseEl); + } catch (e) { + console.warn("Prism.highlightAllUnder() error on collapse show:", e); + } + } + } else { + iconEl.classList.remove('bi-chevron-up'); + iconEl.classList.add('bi-chevron-down'); + } + }; + + // Set initial state based on HTML (none are pre-expanded, so this is fine) + updateIconStateAndHighlight(); + + collapseEl.addEventListener('show.bs.collapse', () => { + updateIconStateAndHighlight(); + setTimeout(positionLines, 50); + }); + collapseEl.addEventListener('shown.bs.collapse', positionLines); + collapseEl.addEventListener('hide.bs.collapse', () => { + updateIconStateAndHighlight(); + setTimeout(positionLines, 50); + }); + collapseEl.addEventListener('hidden.bs.collapse', positionLines); + } + }); + + // --- Footer Year --- + const currentYearEl = document.getElementById('currentYear'); + if (currentYearEl) { + currentYearEl.textContent = new Date().getFullYear(); + } + + // --- Initial Setup Calls --- + initializeFiltersAndSearch(); + applyFiltersAndSearch(); +}); \ No newline at end of file