docs: Update skeleton loader implementation from hyperscript to JavaScript

MIGRATION SUMMARY:
- Moved skeleton loader logic from hyperscript to JavaScript (main.js)
- Changed from htmx:oobAfterSwap to htmx:afterSettle event
- Changed OOB swap from innerHTML to outerHTML for proper element replacement
- Added languageSwitching flag for state tracking
- Added 100ms delay after afterSettle for final render completion

DOCUMENTATION UPDATES:
- 2-MODERN-WEB-TECHNIQUES.md: Updated skeleton loader section with
This commit is contained in:
juanatsap
2025-11-18 19:32:28 +00:00
parent 65eb91b00b
commit 1f6f8e417e
13 changed files with 539 additions and 150 deletions
@@ -86,45 +86,27 @@ async function testSkeletonLoaders() {
// ========================================================================
console.log("\n4️⃣ Testing First Language Switch (EN → ES)...");
// Set up monitoring
await page.evaluate(() => {
window.loadingEvents = [];
const containers = [
document.querySelector('#cv-inner-content-page-1'),
document.querySelector('#cv-inner-content-page-2')
];
containers.forEach((container, index) => {
if (container) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
window.loadingEvents.push({
time: Date.now(),
container: index + 1,
hasLoading: mutation.target.classList.contains('loading')
});
}
});
});
observer.observe(container, { attributes: true });
}
});
// Set up console log monitoring to track our JavaScript skeleton loader messages
const consoleMessages = [];
page.on('console', msg => {
const text = msg.text();
if (text.includes('Skeleton loader:')) {
consoleMessages.push(text);
}
});
// Click Spanish button
await page.click('.selector-btn[aria-label="Español"]');
await page.waitForTimeout(800);
const switch1 = await page.evaluate(() => window.loadingEvents || []);
const loadingAdded1 = switch1.filter(e => e.hasLoading).length;
const loadingRemoved1 = switch1.filter(e => !e.hasLoading).length;
// Check the console messages
const addedMessages1 = consoleMessages.filter(m => m.includes('Added .loading')).length;
const removedMessages1 = consoleMessages.filter(m => m.includes('Removed .loading')).length;
console.log(` Parent containers got .loading: ${loadingAdded1 > 0 ? '✅' : '❌'} (${loadingAdded1} events)`);
console.log(` Parent containers lost .loading: ${loadingRemoved1 > 0 ? '✅' : '❌'} (${loadingRemoved1} events)`);
console.log(` Skeleton loader added .loading: ${addedMessages1 > 0 ? '✅' : '❌'} (${addedMessages1} events)`);
console.log(` Skeleton loader removed .loading: ${removedMessages1 > 0 ? '✅' : '❌'} (${removedMessages1} events)`);
const switch1Passed = loadingAdded1 > 0 && loadingRemoved1 > 0;
const switch1Passed = addedMessages1 > 0 && removedMessages1 > 0;
console.log(` ${switch1Passed ? '✅ PASS' : '❌ FAIL'} - Skeleton displayed during transition`);
testResults.push({ test: 'First Language Switch', passed: switch1Passed });
@@ -133,21 +115,22 @@ async function testSkeletonLoaders() {
// ========================================================================
console.log("\n5️⃣ Testing Second Language Switch (ES → EN)...");
// Clear events
await page.evaluate(() => { window.loadingEvents = []; });
// Clear console messages
const beforeSwitch2 = consoleMessages.length;
// Click English button
await page.click('.selector-btn[aria-label="English"]');
await page.waitForTimeout(800);
const switch2 = await page.evaluate(() => window.loadingEvents || []);
const loadingAdded2 = switch2.filter(e => e.hasLoading).length;
const loadingRemoved2 = switch2.filter(e => !e.hasLoading).length;
// Check new console messages since last switch
const newMessages2 = consoleMessages.slice(beforeSwitch2);
const addedMessages2 = newMessages2.filter(m => m.includes('Added .loading')).length;
const removedMessages2 = newMessages2.filter(m => m.includes('Removed .loading')).length;
console.log(` Parent containers got .loading: ${loadingAdded2 > 0 ? '✅' : '❌'} (${loadingAdded2} events)`);
console.log(` Parent containers lost .loading: ${loadingRemoved2 > 0 ? '✅' : '❌'} (${loadingRemoved2} events)`);
console.log(` Skeleton loader added .loading: ${addedMessages2 > 0 ? '✅' : '❌'} (${addedMessages2} events)`);
console.log(` Skeleton loader removed .loading: ${removedMessages2 > 0 ? '✅' : '❌'} (${removedMessages2} events)`);
const switch2Passed = loadingAdded2 > 0 && loadingRemoved2 > 0;
const switch2Passed = addedMessages2 > 0 && removedMessages2 > 0;
console.log(` ${switch2Passed ? '✅ PASS' : '❌ FAIL'} - Skeleton still works on second switch`);
testResults.push({ test: 'Second Language Switch', passed: switch2Passed });
@@ -156,18 +139,22 @@ async function testSkeletonLoaders() {
// ========================================================================
console.log("\n6️⃣ Testing Third Language Switch (EN → ES)...");
await page.evaluate(() => { window.loadingEvents = []; });
// Clear console messages
const beforeSwitch3 = consoleMessages.length;
// Click Spanish button
await page.click('.selector-btn[aria-label="Español"]');
await page.waitForTimeout(800);
const switch3 = await page.evaluate(() => window.loadingEvents || []);
const loadingAdded3 = switch3.filter(e => e.hasLoading).length;
const loadingRemoved3 = switch3.filter(e => !e.hasLoading).length;
// Check new console messages since last switch
const newMessages3 = consoleMessages.slice(beforeSwitch3);
const addedMessages3 = newMessages3.filter(m => m.includes('Added .loading')).length;
const removedMessages3 = newMessages3.filter(m => m.includes('Removed .loading')).length;
console.log(` Parent containers got .loading: ${loadingAdded3 > 0 ? '✅' : '❌'} (${loadingAdded3} events)`);
console.log(` Parent containers lost .loading: ${loadingRemoved3 > 0 ? '✅' : '❌'} (${loadingRemoved3} events)`);
console.log(` Skeleton loader added .loading: ${addedMessages3 > 0 ? '✅' : '❌'} (${addedMessages3} events)`);
console.log(` Skeleton loader removed .loading: ${removedMessages3 > 0 ? '✅' : '❌'} (${removedMessages3} events)`);
const switch3Passed = loadingAdded3 > 0 && loadingRemoved3 > 0;
const switch3Passed = addedMessages3 > 0 && removedMessages3 > 0;
console.log(` ${switch3Passed ? '✅ PASS' : '❌ FAIL'} - Consistent behavior on third switch`);
testResults.push({ test: 'Third Language Switch', passed: switch3Passed });
@@ -197,31 +184,29 @@ async function testSkeletonLoaders() {
testResults.push({ test: 'No Stuck Loading States', passed: noStuckStates });
// ========================================================================
// TEST 7: Hyperscript event delegation works
// TEST 7: JavaScript event handlers work
// ========================================================================
console.log("\n8️⃣ Testing Hyperscript Event Delegation...");
console.log("\n8️⃣ Testing JavaScript Event Handlers...");
const hyperscriptCheck = await page.evaluate(() => {
const body = document.body;
const hasHyperscript = body.hasAttribute('_');
const hyperscriptContent = body.getAttribute('_') || '';
const hasBeforeRequest = hyperscriptContent.includes('htmx:beforeRequest');
const hasOobAfterSwap = hyperscriptContent.includes('htmx:oobAfterSwap');
const jsCheck = await page.evaluate(() => {
// Check if main.js loaded and languageSwitching variable exists
const hasLanguageSwitchingFlag = typeof window.languageSwitching !== 'undefined';
// Verify the flag is in clean state (false)
const flagIsClean = window.languageSwitching === false;
return {
hasHyperscript,
hasBeforeRequest,
hasOobAfterSwap
hasLanguageSwitchingFlag,
flagIsClean
};
});
console.log(` Body has _hyperscript: ${hyperscriptCheck.hasHyperscript ? '✅' : '❌'}`);
console.log(` Listens for htmx:beforeRequest: ${hyperscriptCheck.hasBeforeRequest ? '✅' : '❌'}`);
console.log(` Listens for htmx:oobAfterSwap: ${hyperscriptCheck.hasOobAfterSwap ? '✅' : '❌'}`);
console.log(` JavaScript languageSwitching flag exists: ${jsCheck.hasLanguageSwitchingFlag ? '✅' : '❌'}`);
console.log(` Flag is in clean state (false): ${jsCheck.flagIsClean ? '✅' : '❌'}`);
const hyperscriptPassed = hyperscriptCheck.hasHyperscript && hyperscriptCheck.hasBeforeRequest && hyperscriptCheck.hasOobAfterSwap;
console.log(` ${hyperscriptPassed ? '✅ PASS' : '❌ FAIL'} - Global event delegation configured`);
testResults.push({ test: 'Hyperscript Event Delegation', passed: hyperscriptPassed });
const jsPassed = jsCheck.hasLanguageSwitchingFlag && jsCheck.flagIsClean;
console.log(` ${jsPassed ? '✅ PASS' : '❌ FAIL'} - JavaScript event handlers configured`);
testResults.push({ test: 'JavaScript Event Handlers', passed: jsPassed });
// ========================================================================
// FINAL SUMMARY