?

js page index

This site is built with my modified version of Zonelots, which is a structure for sites built around a JavaScript code that deals with an array of posts, complete with tags and dates and all.

I enjoy this immensely. However, I also have some things that don't quite fit into the blog post style. In particular, I really didn't want my shrines to be dated, since I go back and add things as needed, so any date would quickly become inaccurate.

So I took the indexing capabilities of the foundational Zonelots script and modified it to make a mini-index. Or, rather, mini-indicies: a very, very bare-bones script for my shrines and a slightly more complex one for my recipes, which maintains tagging abilities. The code below is for the latter; if you want the simpler version, just delete the marked sections.

instructions

A few notes:

  1. This is dependent on some of the CSS classes established in Zonelets. I've included the code below for the post-title, index, and tag-list classes.[1]
  2. If you are running this script on a page that already uses Zonelets or a script based on its script, you will need to ensure that your variables have different names, or your browser will throw all sorts of fun errors. In the example below, which is taken from my recipes page, I have left in all my variable names, which tend to be the names from Zonelots with "recipe" in place of "post" or otherwise appended to the name. If you're doing something other than recipes (and also care), it should be easy enough to do a find/replace for "recipe"; likewise, you should replace "recipe" in the subsequent instructions.
  3. You'll probably need to edit some of the links, depending on where you're placing the script and the files it's meant to organize. If you plan to use a similar structure to mine (a directory, in this case /pages/recipes/, containing the script _recipes.js and all relevant HTML files), then the only thing you should need to change is the const recipePath.
  4. And speaking of files, you'll need an index page featuring the line <ol id="recipe-index" class="index"></ol> and if you're using tags, you'll also need a tags page featuring the line <ol id="recipe-tags" class="index"></ol>. Both will need to call the script[2] .

code

JavaScript

/* 
CONTENTS
1. Basics
	1a. Variables
	1b. Adjustments
2. Functions
3. HTML generation
4. HTML insertion
*/



/* ==================
	1. BASICS
================== */

/* ------------------
	1A. VARIABLES
------------------ */

const recipePath = "/pages/recipes/";

const recipeArray = [ // remove the "tags" line if not using tags
	// {
	// 	path: "",
	// 	title: "",
	//  description: "",
	// 	tags: [""]
	// },
	{
		path: "sample",
		title: "sample",
		description: "an example page",
		tags: ["test"]
	},
]

const recipePages = {
	recipeCode: { id: "recipe-index", HTML: "" },
	recipeTagsCode: { id: "recipe-tags", HTML: "" } // remove if not using tags
}


/* ------------------
	1B. ADJUSTMENTS
------------------ */

for (let i in recipeArray) recipeArray[i].path = recipePath + recipeArray[i].path + ".html";

const recipesSorted = recipeArray.sort(
	(a, b) => {
		const recipeTitleA = a.title.toUpperCase(); // ignore upper and lowercase
		const recipeTitleB = b.title.toUpperCase(); // ignore upper and lowercase
		if (recipeTitleA < recipeTitleB) {
			return -1;
		}
		if (recipeTitleA > recipeTitleB) {
			return 1;
		}

		// titles must be equal
		return 0;
	}
);



/* ==================
	2. FUNCTIONS
================== */

function formatRecipeLink(i, recipeArray) {
	let recipeLinkText = "";
	const recipeTitle = recipeArray[i].title;
	const recipeTags = recipeArray[i].tags; //remove if not using tags
	const recipeDescr = recipeArray[i].description;

	recipeLinkText += '<li><a href="/' + recipeArray[i].path + '">';
	recipeLinkText += '<span class="post-title">' + recipeTitle + "</span></a>";
	if (recipeDescr && recipeDescr.length) linkText += ' | ' + recipeDescr;

	// remove if not using tags
	if (recipeTags && recipeTags.length) {
		recipeTags.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
		for (let i = 0; i < recipeTags.length; i++) {
			let recipeTagName = recipeTags[i];
			recipeTags[i] = '<li><a href="' + recipePath + 'tags.html#--' + recipeTagName.replace(/\s/g, "-") + '" rel="tag">#' + recipeTagName + '</a></li>';
		}
		recipeLinkText += '<dl><dt>Tags</dt><dd class="tag-list">' + recipeTags.join("") + '</dd></dl>';
	}

	recipeLinkText += '</li><'

	return recipeLinkText;
}

// remove if not using tags
function buildRecipeIndex(recipeTagType, recipeEmptyMessage) {
	let recipeListText = "";

	for (let i = 0; i < recipeArray.length; i++) {
		for (let j = 0; j < recipeArray[i][recipeTagType].length; j++) {
			if (typeof allRecipeTags[recipeArray[i][recipeTagType][j]] == 'undefined') allRecipeTags[recipeArray[i][recipeTagType][j]] = [];

			allRecipeTags[recipeArray[i][recipeTagType][j]].push({ path: recipeArray[i].path, title: recipeArray[i].title });
		}
	}
	const allrecipeTagNames = Object.keys(allRecipeTags).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));

	if (allrecipeTagNames.length > 0) {
		for (let i = 0; i < allrecipeTagNames.length; i++) {
			let recipeTagName = allrecipeTagNames[i];
			
			recipeListText += '<li><details id="--' + recipeTagName.replace(/ /g, "-") + '"><summary>' + recipeTagName + '</summary><ol reversed class="index">';
			
			for (let j = 0; j < allRecipeTags[recipeTagName].length; j++) recipeListText += formatRecipeLink(j, allRecipeTags[recipeTagName]);

			recipeListText += '</ol></details></li>';
		}
	} else recipeListText += '<li>' + recipeEmptyMessage + '</li>';

	return recipeListText;
}



/* ==================
	3. HTML GENERATION
================== */

for (let i = 0; i < recipeArray.length; i++) {
	recipePages.recipeCode.HTML += formatRecipeLink(i, recipesSorted);
}

// remove if not using tags
const allRecipeTags = {};
if (document.getElementById(recipePages.recipeTagsCode.id)) recipePages.recipeTagsCode.HTML = buildRecipeIndex("tags", "This blog has no tags!");

/* ==================
	4. HTML INSERTION
================== */

for (let i in recipePages) {
	const recipeElement = document.getElementById(recipePages[i].id);
	if (recipeElement) recipeElement.innerHTML = recipePages[i].HTML;
	else {
		const recipeElements = document.getElementsByClassName(recipePages[i].id);
		if (recipeElements.length > 0) {
			for (let j in recipeElements) {
				recipeElements[j].innerHTML = recipePages[i].HTML;
			}
		}
	}
}

// remove if not using tags
if (document.querySelector(".index") && window.location.hash) document.getElementById(window.location.hash.slice(1))?.setAttribute("open", "");
JavaScript

CSS

.index {
	list-style-type: none;
	margin-inline-start: 1em;
	padding: 0;
	margin-left: 0;

	li {
		margin: .5em 0;

		>p {
			margin: 0;
		}
	}
}

.post-title {
	text-transform: uppercase;
	font-family: var(--head-font);
	font-weight: var(--bold-weight);
}

// remove if not using tags
.tag-list {
	hyphens: none;

	li {
		display: inline;
		margin-inline-start: 0;
		list-style-type: none;

		&:not(:last-child)::after {
			content: ", ";
		}
	}
}
JavaScript

  1. I also use my "box" class, which is just a transparent background with rounded corners, but I've removed that from the code below.^
  2. Because of the way I've set this up (with no inherent order and thus no navigation between pages), you don't need to call this script on every page it organizes, unlike in the case of Zonelets et al., only on the index page and, if applicable, the tags page.^
tags
projects