Did I just write an entire markdown blog engine using a cute lean little markdown parser I found only to realize at the very end that you can't upload .md files to Neocities?
This is what high INT low WIS looks like. ☠️
So change of plans, I write my blog posts in Obsidian.md and turns out, Obsidian folks have written a plugin that lets me export out the markdown as HTML, now nice. But that still doesn't solve the tedium of making blog posts entirely, I still have to slap that HTML snippet into a boilerplate I made and then add an entry to articles array.
So, new goal. How do I take these code snippets and make them useful. It would be a lot faster to just inject them into a static blog page as the content rather than say, wrap each post in it's own boiler plate.
Let's take this big goal and break it down into smaller parts.
Ok now lets tackle the first one.
Right now, I use a file called cards.data.js to store all my article data, inside it is an array of object variables that looks something like this:
let card = [
{
title : "Sundered Society",
date : new Date("2022-09-11"),
blurb : "Part 1 of exploring the base building blocks of the Sol Consortium post war.",
article : "blog/posts/post_society.html",
image: "blog/blogheaders/lore.png",
type: "lore",
},
];
I then pass this data file into my blog js called sudered.blog.js via standard JS Export and Import.
In this JS file, we create an article card for each article in the cards data file. This is where, currently, we generate a unique URL link for each blog post.
function generateCards(cardList){
let htmlCardList = [];
//1. iterate through cards making articles
cardList.forEach( card => {
`<div class="card">
<h2>${card.title}</h2>
<h4 class="blog-subheader">${card.blurb} <span class="blog-date">${card.date.toDateString()}</span></h4>
<img src="${card.article}" src="imgs/blog_tunein.png" />
</div>`
//2. add articles to array
htmlCardList.push(htmlCard);
}
)
return htmlCardList;
}
The href attribute on the link is getting set to ${card.article} which we can surmise from looking at the cards array that is the direct path to the blog.
This is what needs to change, we need this href attribute to always go to the same place, lets set it to something static.
<a class="blog-tunein" href="blog/blog_post.html"><img src="imgs/tunein_icon.png" /> Tune In</a>
Yes that works. We can make blog_post.html our boiler plate blog page with an empty article tag inside it that we can target with our snippet injection.
Ok onto the next step.
Hmmmm 🤔 This is trickier. We need to understand how to store some data in the JS when the html is loading a new page. There are a few ways we can do this but I think there are really only two I want to consider.
There isn't a ton different between these two options. We lose session storage data when the user closes the window but that's fine for this scenario. The real main difference is that passing the data through the URL can be seen by the user and using session storage can't.
Unfortunately, passing parameters through URLs seems to have trouble on neocities for me so we'll be going with session storage.
Ok so how do we put this data into session storage? Well, remember that link we changed? We actually need to turn it into a button instead, we need the JS to control the URL we're going to.
<a class="blog-tunein" href="blog/blog_post.html"><img src="imgs/tunein_icon.png" /> Tune In</a>
Lets take this hyperlink and remove the hyper link, we can give the image itself an onclick attribute instead.
<img src="imgs/tunein_icon.png" data-article="${card.article}" onclick="setBlogPost();"/>
There we go! ✨ But it looks like we added some extra stuff. Data attributes are usefully little tags you can put on html to pull off them in JS, this way we can store the name of the article snippet we want to grab, and we get that name from the cards.data.js array, we're going to replace the article URL with just the file name. 👍
Something like this: article : "post_right_no_markdown.html",
Ok, lets write that function up there setBlogPost().
function setBlogPost(){
//1. Get the article name from the data attribute.
let articleName = event.srcElement.dataset.article;
//2. set the value into session storage
sessionStorage.setItem("article", articleName);
//3. load the new page!
let url = '/blog/blog_post.html'
window.location.href = url;
}
Whew! 💦 ok we've made it to the blog post page while knowing what snippet we need to grab. Now we actually need to grab it!
We have the parameters in our URL, lets get them out now and use them to grab our snippet.
We'll need a second function for this and it'll run in the JS window.onload event which fires as soon as the browser loads the new page.
function getBlogPost(){
//1. lets grab the data from session storage
let article = sessionStorage.getItem("article");
let articleURL = `posts/${article}`;
//2. use fetch to get the html file and append it to the body.
let blogPost = await fetch(articleURL);
document.getElementById('blog-body').innerHTML = await blogPost.text();
}
After we grab the article's file name, we add a path and then use that with the JS base method fetch() to grab the file and get all it's text.
Then we append that text to the article tag of our blog post and blamo 💥, article! And because we pass a different parameter with each different article we click, this little method will load the same page with different html snippets every time!
Well...
Kind of. We still have to manually add the data to the cards.data.js file and upload a code snippet into the posts folder and make sure we fix any image URLs we want to include...
But! Such is the nature of serverless web development, we have no backend to hide a WYSIWG editor on or log into to use a simple GUI uploader. It means there's a little extra work in the code department.
I think, ultimately though, this is a far cry more hands off than my initial draft where I was still manually writing out the html for every. single. post. (gee wonder why I only had a few!) I'm also happy that this destroys the need for possibly hundreds of pages with the exact same footer and header code. 💪