This commit is contained in:
2026-07-12 10:15:22 +02:00
parent 9d7a3f6660
commit 9d3d33251a
2 changed files with 54 additions and 24 deletions
+26 -23
View File
@@ -1,18 +1,16 @@
// blog.js Loads and renders blog posts with simple "Read more" toggle for long entries
// blog.js Loads blog posts and uses a gradient fade for long entries
const BLOG_DIR = 'blog/';
const MANIFEST = BLOG_DIR + 'manifest.json';
// ---------- helpers ----------
// crude HTML parser for directory listings (when manifest is missing)
function parseIndexHtmlList(html) {
const hrefs = [];
const re = /href="([^"]+)"/g;
let m;
while ((m = re.exec(html)) !== null) {
const name = m[1];
// accept typical text file extensions (and no extension)
if (name.match(/\.(txt|md|text)$/i) || !name.includes('.')) {
if (!name.endsWith('/')) hrefs.push(name);
}
@@ -20,7 +18,6 @@ function parseIndexHtmlList(html) {
return hrefs;
}
// fetch list of blog post filenames (manifest first, fallback to directory listing)
async function loadPostList() {
try {
const r = await fetch(MANIFEST, { cache: 'no-cache' });
@@ -39,7 +36,6 @@ async function loadPostList() {
return [];
}
// fetch a single post file
async function fetchPost(filename) {
const res = await fetch(BLOG_DIR + filename, { cache: 'no-cache' });
if (!res.ok) throw new Error(`Failed to fetch ${filename}`);
@@ -50,9 +46,7 @@ async function fetchPost(filename) {
function renderPost(filename, content) {
const lines = content.split('\n');
if (lines.length < 3) {
return null; // malformed
}
if (lines.length < 3) return null;
const dateTime = lines[0].trim();
const title = lines[1].trim();
const bodyLines = lines.slice(2);
@@ -75,31 +69,41 @@ function renderPost(filename, content) {
const isLong = body.length > 300;
// Body container (always created, but hidden for long posts)
// Container for the body (with optional fade)
const wrapper = document.createElement('div');
wrapper.className = 'blog-body-wrapper';
// The actual text
const bodyDiv = document.createElement('div');
bodyDiv.className = 'blog-body';
bodyDiv.style.whiteSpace = 'pre-line';
bodyDiv.textContent = body;
wrapper.appendChild(bodyDiv);
if (!isLong) {
// Short post show body directly
postDiv.appendChild(bodyDiv);
} else {
// Long post hide body initially, add toggle button
bodyDiv.style.display = 'none';
postDiv.appendChild(bodyDiv);
// If long, add collapsed class and a toggle button
if (isLong) {
wrapper.classList.add('collapsed');
// Toggle button
const toggleBtn = document.createElement('button');
toggleBtn.className = 'blog-toggle';
toggleBtn.textContent = 'Read more';
postDiv.appendChild(toggleBtn);
toggleBtn.addEventListener('click', function() {
const isHidden = bodyDiv.style.display === 'none';
bodyDiv.style.display = isHidden ? 'block' : 'none';
this.textContent = isHidden ? 'Show less' : 'Read more';
const isCollapsed = wrapper.classList.contains('collapsed');
if (isCollapsed) {
wrapper.classList.remove('collapsed');
this.textContent = 'Show less';
} else {
wrapper.classList.add('collapsed');
this.textContent = 'Read more';
}
});
postDiv.appendChild(wrapper);
postDiv.appendChild(toggleBtn);
} else {
// Short post no collapse, no button
postDiv.appendChild(wrapper);
}
return postDiv;
@@ -120,8 +124,7 @@ async function initBlog() {
return;
}
files.sort(); // alphabetical -> chronological
files.sort();
feed.innerHTML = '';
for (const file of files) {
+28 -1
View File
@@ -31,13 +31,39 @@ a:hover { color:#991b1b; text-decoration:underline; }
.blog-title { margin:0 0 8px 0; font-size:1.5rem; }
.blog-meta { margin:0 0 16px 0; color:#7f1d1d; font-size:0.95rem; }
/* ----- Blog body with fade-out ----- */
.blog-body-wrapper {
position: relative;
margin-bottom: 0.5rem;
/* No max-height by default (full) */
}
.blog-body {
white-space: pre-line;
margin: 0 0 1em 0;
margin: 0;
}
.blog-body p { margin:0 0 1em 0; }
.blog-body p:last-child { margin-bottom:0; }
/* When collapsed, limit height and show gradient fade */
.blog-body-wrapper.collapsed {
max-height: 300px; /* ~6 lines, adjust as needed */
overflow: hidden;
/* The gradient overlay is done via ::after */
}
.blog-body-wrapper.collapsed::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px; /* fade height */
background: linear-gradient(to bottom, transparent, #fff);
pointer-events: none; /* allow clicking through to the button */
/* If your blog background is not white, change #fff to match */
}
/* Toggle button */
.blog-toggle {
background: none;
@@ -53,6 +79,7 @@ a:hover { color:#991b1b; text-decoration:underline; }
text-decoration: underline;
}
/* Other existing styles (photography, modal, etc.) */
.blog-attachment-list { margin:16px 0 0 0; padding:0; list-style:none; }
.blog-attachment-list li { margin:0.5em 0; }
.blog-attachment-list a { color:#dc2626; text-decoration:none; }