hashbloge - single-page bloge for static webbed sites
This is the code used for the blog of breadavota.cafe.
hashbloge is a single-page blog where all content is pulled from different HTML files and injected into the singular /blog page. Separate blog entries can still be linked through anchor links.
This was designed as an expansion of BreadAVOTA's old blog where all entries where put in one page and separated in sections that used ID's to assign anchor links to each. As the number of blog entries increased, this became inconvenient, but I didn't want to break the original linking scheme, so simply transporting all the entries to their own HTML files would be insufficient.
view sample bloge here
Why use hashbloge?
I mean you probably shouldn't but here's some things I find nifty:
- No backend needed: works for static sites like Neocities, Netlify, etc.
- Scrolling and fading animations (can be turned off: see end of page)
- HTML files for entries only need to include content: any CSS/styling information only needs to be on the index page, and if you want to change your template you only need to do it once
- Entries can be linked using anchor links, so URL's are short and sweet (eg mysite.com/blog#10 goes to poste 10)
- The only time you will ever need to change the JS code is to replace one (1) number every time your bloge updates
- This only provides the functionality of the bloge: you can design the CSS however you want
Why not use hashbloge?
- Technically I am a 'webcomic maker' and I only study how to make webbed site to make my comic website so I'm not really a programmer and am unlikely to know how to maintain this thing in the long-term
- If you do not want to have Javascript on your website or want your website to be easily read by people who don't use Javascript
- If you need a tagging system or search function
- If your bloge is massive and has seven million postes I guess idk
- If you don't know or don't want to write HTML/CSS yourself (hashbloge doesn't help in that regard)
- If you want more descriptive URL's
- If you want to scrub a specific poste off the face of the earth (see end of page)
- If you want a well-maintained blogging template, especially for static sites, nine out of ten dentists recommend Zonelets
How to use hashbloge
0 Download and extract this ZIP, plop it into your website and ignore everything else below.
1 If you want to do things manually, create a directory (folder) in your website called blog
. You can name it something else if you want to, but for this guide, I will assume it is named blog
.
2 Create an index.html
file in blog
. This will be where you put the hashbloge code, as well as any styling information using CSS. Use the following as a template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hashbloge</title>
<!-- Replace with your stylesheet, if any -->
<link rel="stylesheet" href="style.css">
<style>
/* You can put your CSS here or have it linked externally like above.
Any injected content will have this CSS apply to it, so you do not need to
put or link the CSS into the individual blog entries. Make sure the CSS
below is retained somewhere for the scrolling animation to work. */
#blog-content {opacity:1;transition:opacity 0.4s ease;}
#blog-content.fade-out {opacity: 0;}
#blog-nav {text-align:center;} /* You can change this if you want. */
.greyedout {opacity:0.5;pointer-events:none;} /* This greys out the navigation buttons if
you are already at the first or last poste. */
/* The following can be deleted. I am putting them here as a sample for later. */
body{background:#e7e4db;color:black;padding:2% 5%;}
.sample-class {color:green; font-size:1.5em;}
.sample-title {color:red; font-size:2em;}
.nav-link {border-radius:5px;background-color:#acbfa0;color:#212121;font-family:"Courier";}
img {max-width:100%;}
</style>
</head>
<body>
<!-- You can modify the HTML outside of #blog-content, for example if you want to add a page title above it, to add a footer below, etc. -->
<h1>hashbloge</h1>
<div id="blog-content">
<!-- Your injected content will go in here. -->
</div>
<hr>
<!--These are the navigation buttons to go to older or newer entries. -->
<div id="blog-nav">
<button title="Older" id="older" class="nav-link">Older</button>
<button title="Newer" id="newer" class="nav-link">Newer</button>
</div>
<!--This is the bloge code. Put it in the same directory as the blog; if you put it somewhere else, make sure you update the src below. -->
<script src="hashbloge.js"></script>
</body>
</html>
3 Place hashbloge.js
inside the blog
directory. If you place it elsewhere, you need to make sure the src in index.html
is updated. Below are comments on how the code works.
Note: You should probably use the script in the downloadable ZIP because I only added these explanation comments later, like, right here in the HTML file and realised that the spacing I put for readability apparently FUCKS with everything and I had to re-reformat it when I copy-pasted the commented out version for the script. Let this be a lesson that I should have put the comments inside the script the first time around.
const lastPoste = 3; // You need to MANUALLY change this to the number of your last/newest poste each time.
const firstPoste = 1; // This is the first poste. Don't change this unless your first poste is not 1 for some reason.
let currentPosteId = null;
let init = false;
let loading = false;
async function loadContent(posteId) {
const blogContent = document.getElementById('blog-content');
if (!blogContent || loading || posteId === currentPosteId) return;
loading = true;
currentPosteId = posteId;
//This adds the 'fade-out' class defined in the CSS above, used for the fading out animation when changing bloge postes. The timeout here sets a slight delay to make sure that the animation plays smoothly even if the user clicks multiple times, the content hasn't fully loaded, etc.
blogContent.classList.add('fade-out');
await new Promise(resolve => setTimeout(resolve, 400));
// If you do not want a fade-out animation and want the postes to appear instantly, delete the code above AND the other code slightly below this.
try {
const response = await fetch(`${posteId}`); // Depending on your website's redirect rules, you might also need to replace ${posteId} with ${posteId}.html
if (!response.ok) throw new Error('The file is missing!'); // Message if the poste is not found eg you go to blog#10 but your bloge only has 9 postes. You can change this error message to something else if you want to be quirky widdit.
const content = await response.text();
blogContent.innerHTML = content;
} catch (error) {
blogContent.innerHTML = `<p>ERROR: ${error.message}</p>`; // Appears before the error message above. Again, you can replace 'ERROR' here with a different word if you want.
}
//Delete the code below AND the code slightly above this (read the comments) if you want to turn off the fade-out animations.
blogContent.classList.remove('fade-out');
// This is the 'scroll to top' animation where the page automatically scrolls back to top when changing entries.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTimeout(() => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}, 10); // minor delay because that fuck-ass scroll won't work otherwise
});
});
// If you do not want a scrolling animation and want the scrolling to move to the top, instantly, delete the code above and uncomment the code below.
// window.scrollTo(0, 0);
greyNav(posteId);
loading = false;
}
function greyNav(posteId) {
const olderNav = document.getElementById('older');
const newerNav = document.getElementById('newer');
olderNav.classList.toggle('greyedout', posteId <= firstPoste);
newerNav.classList.toggle('greyedout', posteId >= lastPoste);
}
function getPosteId() {
const hash = window.location.hash.slice(1);
const posteId = parseInt(hash, 10);
return isNaN(posteId) ? lastPoste : posteId;
}
function setPoste(posteId) {
if (`#${posteId}` !== window.location.hash) {
window.location.hash = `#${posteId}`;
} else {
loadContent(posteId);
}
}
window.addEventListener('DOMContentLoaded', () => {
const olderNav = document.getElementById('older');
const newerNav = document.getElementById('newer');
if (olderNav) {
olderNav.addEventListener('click', () => {
const posteId = getPosteId();
if (posteId > firstPoste) setPoste(posteId - 1);
});
}
if (newerNav) {
newerNav.addEventListener('click', () => {
const posteId = getPosteId();
if (posteId < lastPoste) setPoste(posteId + 1);
});
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', event => {
event.preventDefault();
const target = event.currentTarget.getAttribute('href').slice(1);
const posteId = parseInt(target, 10);
if (!isNaN(posteId)) setPoste(posteId);
});
});
// it's chewsday init
if (!init) {
const posteId = getPosteId();
setPoste(posteId);
init = true;
}
});
window.addEventListener('hashchange', () => {
const posteId = getPosteId();
if (!init || posteId !== currentPosteId) {
loadContent(posteId);
}
});
4 You can now write your bloge postes. Create a new HTML file and number it eg 1.html
. Make sure that all your postes are in the same directory as index.html
. You only need to put the actual content that you want injected into index.html
inside 1.html
: you do not need to put the <html>, <meta> <head> or <body> tags again. Your postes will inherit the CSS in index.html
and you do not need to put in any styling or template information inside. Here is an example of a poste:
Example of 1.html
<span class="sample-title">First poste</span> | <a href="#1">Permalink</a>
<hr>
<p>Hi, this is an example poste.</p>
<p class="sample-class">If I have .sample-class defined with styling information in a style sheet that index.html is linked, then it will also apply here without needing to put or link that CSS in this exact poste. Waouh!</p>
<p>Notice how I contain only the poste's content, and have no <html>, <meta> <head> or <body> tags, and no CSS.</p>
Which renders to the following:
Example of 1.html
First poste | Permalink
Hi, this is an example poste.
If I have .sample-class defined with styling information in a style sheet that index.html is linked, then it will also apply here without needing to put or link that CSS in this exact poste. Waouh!
Notice how I contain only the poste's content, and have no <html>, <meta> <head> or <body> tags, and no CSS.
Every time you want to create a new poste, just number it accordingly and make sure to change const lastPoste in hashbloge.js
to the latest poste.
Remember:
- All references I make to
index.html
refer to the index inside your blog directory, not the index file of your entire website. - All your postes and
hashbloge.js
must be saved in the same directory asindex.html
- The file names must be numbers. They cannot be letters, words, symbols, etc.
- Only
index.html
needs to be updated when making changes to the CSS/styling. The only change you need to make inhashbloge.js
is to update const lastPoste with the number of your last poste each time you make a new one.
Can I use hashbloge?
Sure, but keep in mind my disclaimer above. I'm not entirely sure how scalable hashbloge is.
Can I implement a tagging system or search function to find specific postes?
I did not implement this functionality because I don't really know how to do that. Sorry. If you know how to add it yourself then by all means.
How can I remove the scrolling animation so that it instantly jumps back to top?
Look for this part:
// This is the 'scroll to top' animation where the page automatically scrolls back to top when changing entries.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTimeout(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, 10);
});
});
Replace that part with this:
window.scrollTo(0, 0);
How can I remove the fading animation so that postes instantly appear?
Look for and delete these parts:
blogContent.classList.add('fade-out');
await new Promise(resolve > setTimeout(resolve, 400));
blogContent.classList.remove('fade-out');
Remove these from the CSS as well:
#blog-content {opacity:1;transition:opacity 0.4s ease;}
#blog-content.fade-out {opacity: 0;}
What if I want to delete a specific poste?
Because hashbloge works by having the postes numbered chronologically, deleting a specific poste will be troublesome. You can do it, but the poste number and anchor link that it represented will always display as a 'file missing' error. There isn't a clean way to delete it totally to the point where there is no evidence that it existed: you can rename all the postes after that to make up for the numbering, but not only is this cumbersome, this will also break any external links to these postes.
Your options then are to leave the 'file missing' error alone, or to simply replace all the contents of the redacted poste with something else instead of deleting it.
How do I put titles/dates/permalinks/etc. in a poste?
You will have to manually include them in your individual HTML files.
What if I want to name a poste with something besides a number?
The script does not let you do that, and I have it this way because I want postes on my bloge to be numbered, so I have no plans on changing it. However, it should be relatively easy to modify the script to allow for posteId to be a string, although those postes would only be accessible from direct links and not through the Older/Newer buttons.
Do note that if you want to give your entries titles, you will have to write the titles inside their HTML pages (see the samples). The file name is only used for poste order and anchor linking, and it isn't inherently displayed anywhere else besides the URL.
How do I change the appearance of my bloge?
You can do this with CSS, either by linking an external stylesheet or by putting the CSS in <style> tags in your bloge's index.html
. There are many better tutorials out there for how to learn CSS.
How do I make my bloge look good on mobile?
hashbloge only determines the functionality of the bloge but not the appearance. It should work fine on mobile devices, and if your issue involves the look of your site on mobile, this is also a CSS matter.
How much HTML/CSS/JS do I need to know?
JS: functionally none. I barely know how to use JS and am attached to the hip of MDN Web Docs and w3schools.
HTML/CSS: You will need to know at least a moderate amount, as you will need to write your actual postes in HTML and need to write your own CSS to change the appearance.
Can I use Markdown? Are there preset themes?
hashbloge does not support Markdown. I don't use Markdown myself so it's kind of tedious for me to add.
There are no preset themes, though if somebody actually wanted to use hashbloge and needed help with the CSS, feel free to message me. I don't actually feel like anybody's even going to see this. Hi, Hypothetical Audience!!!
What is 'anchor linking'?
Let's say you have yourwebsite.com/blog. You have a section on your bloge labelled id="1". ID's are denoted with the # symbol, so id="1" is the same as #1. The ID is what serves as the 'anchor'.
If you go to yourwebsite.com/blog#1, the page automatically jumps to the section that has #1.
In hashbloge, instead of the default behaviour (the page scrolling down/jumping to #1), what it instead does is look for the HTML file that has that as the file name, and inject it into the main bloge page. If you want your bloge to show the contents of 1.html then, you can go to yourwebsite.com/blog#1. If you want to link that specific entry to others, you can also use yourwebsite.com/blog#1. If you want to link to a specific poste inside your bloge (for example: on post 5 you write 'Check out my last update on post 1'), you only need to link the anchor (eg 'Check out my last update on <a href="#1">post 1</a>).
Shit, I should have called hashbloge id="bloge".
What if I want to make a separate page for an archive/table of contents/etc.?
If you want it as a separate page, for example a page called archive.html
instead of putting it somewhere in the index file like my sample, you can do this, but the script as it is won't allow for injecting it into the main page because the file name isn't a number. However, you can easily link to it yourself at yourwebsitehere.com/blog/archive (notice how there is no #).
Becase these pages are not injected into the main index file, they will also not inherit the CSS of the index file and will need to be styled separately.
My HTML files are all in one folder, but I keep getting the 'file missing' error!
Try clearing your cache first. If that doesn't work, look for the following in the code:
const response = await fetch(`${posteId}`);
You may need to change `${posteId}` to `${posteId}.html` if your website doesn't use 'pretty links'. Worst case scenario, you can try changing it to your absolute URL (eg `https://yourwebsitehere.com/blog/${posteId}`) but if you resort to needing to do that you might as well find an easier blogging template to work with lmfao I'm sorry.
If that doesn't work, please open your browser's developer tools and check the Console. Do you get a 'mixed content' error? That means that your website is trying to access your individual entries through HTTP instead of HTTPS.
Check the Networks tab in the developer tools as well. Refresh the page: do you keep getting requests for the same page over and over again? This is a redirect loop where the website keeps sending requests for your entries but gets stuck in a loop.
Both of these are problems with your website's .htaccess, and changing the hashbloge code is unlikely to fix it: you will need to fix your .htaccess yourself.
If you received neither of these errors, then uhhh I don't know lol. But try sending me an e-mail and I will maybe cry about it alongside you.
There are atrocious mistakes here that no serious programmer would ever make. How do I litigate you for them?
I'm not a programmer, I'm just a Homestuck fanventure writer (˃̣̣̥ヘ˂̣̣̥). Please e-mail me at [email protected]
for any corrections or suggestions.
Your indenting fucking sucks
Yeah I don't like indenting because it's tedious and I run everything through this shit
Why hashbloge? Why blogE and postE?
Since the whole thing relies on anchor links through the good ol' hash (#).
That's just how I spell it. I have quirky disease.
Do I need to give credit if I use hashbloge?
No, I don't really care. You could if you wanted to but I'm not going to hound your ass for it. If you want to do something nice, you can leave a comment on the guestbook. That would be very cool.
Can I modify the script or take only parts of it for something else?
Yeah whatevs.
Can I claim hashbloge as my own mistake of innature?
Yeah whatevs.