Skip to main content
The website uses vanilla JavaScript for core functionality, including event handling, random quote rotation, and blog post loading. All JavaScript is lightweight and dependency-free except for third-party libraries (Showdown.js for Markdown conversion).

Core JavaScript Files

functions.js

Utility functions for safe event handling and page load management

quotes.js

Random quote generator with 185+ pop culture quotes and automatic rotation

list-posts.js

Blog post loader using GitHub API and Markdown-to-HTML conversion

Event Handler Utility

The addLoadEvent() function safely chains multiple window load handlers without conflicts.

Implementation

File: assets/scripts/functions.js
/* <![CDATA[ */
function addLoadEvent(func) {
    var oldonload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = func;
    } else {
        window.onload = function () {
            oldonload();
            func();
        }
    }
}
/* ]]> */

How It Works

1

Check Existing Handler

Stores the current window.onload handler in oldonload variable
2

Conditional Assignment

If no existing handler exists (typeof window.onload != 'function'), directly assigns the new function
3

Chain Handlers

If a handler already exists, creates a wrapper function that calls both the old and new handlers in sequence
This pattern was common before modern JavaScript frameworks. It ensures multiple scripts can safely add load event handlers without overwriting each other.

Usage Example

addLoadEvent(function() {
    console.log('First handler');
});

addLoadEvent(function() {
    console.log('Second handler');
});

// Both functions execute on page load
Modern applications typically use addEventListener('DOMContentLoaded', callback) or addEventListener('load', callback) instead, which natively supports multiple listeners.

Random Quote System

The quote rotator displays random quotes from a database of 185+ quotes spanning movies, games, TV shows, and pop culture.

Quote Database

File: assets/scripts/quotes.js
const quotes = [
    { quote: "If it hasn't worked out yet, it's because it's not over yet", author: "MilesONerd" },
    { quote: "Aren't you a little short for a stormtrooper?", author: "Leia Organa" },
    { quote: "I am altering the deal, pray I do not alter it further.", author: "Darth Vader" },
    { quote: "No! I don't think he likes you at all... No, I don't like you either.", author: "C-3PO" },
    // ... 181+ more quotes
];
The quote database includes references from:
  • Star Wars: 30+ quotes from the original trilogy, prequels, and sequels
  • Marvel Cinematic Universe: Avengers, Black Panther, Guardians of the Galaxy
  • Video Games: Portal, Bioshock, Metal Gear Solid, The Legend of Zelda, Halo
  • Science Fiction: Star Trek, Terminator, Alien, The Matrix
  • Fantasy: Lord of the Rings, Harry Potter, Game of Thrones
  • Action & Comedy: Various movie and TV references
Total: 185 quotes across all categories

Core Functions

function getRandomQuote() {
    const randomIndex = Math.floor(Math.random() * quotes.length);
    return quotes[randomIndex];
}

Function Breakdown

1

getRandomQuote()

Generates a random index using Math.random() multiplied by the array length, then floors it to get an integer. Returns the quote object at that index.
2

updateQuote()

Destructures the quote object to extract quote and author properties, then updates the DOM elements with proper HTML entities for quotation marks (&ldquo; and &rdquo;) and em dash (&mdash;).
3

Event Initialization

On DOM ready, displays an initial random quote and attaches a click handler to the “another quote” button for manual rotation.

HTML Integration

The quote system requires specific DOM elements:
<div>
  <p id="random-quote"></p>
  <p id="quote-author"></p>
  <button id="another-quote">Get Another Quote</button>
</div>
The quote updates both on page load and when users click the “Get Another Quote” button, providing an interactive element without page refresh.

Blog Post Loader

The legacy blog system dynamically loads Markdown posts from GitHub and renders them as HTML.

Configuration

File: old/blog/scripts/list-posts.js
const converter = new showdown.Converter();

const username = 'MilesONerd';
const repo = 'milesonerd.github.io';
const folder = 'posts';
const branch = 'update-urls';

const postList = document.getElementById('post-list');
const contentDiv = document.getElementById('content');
This code depends on Showdown.js, a JavaScript Markdown parser. The library must be loaded before this script executes.

Fetching Post List

async function fetchPostList() {
    const url = `https://api.github.com/repos/${username}/${repo}/contents/old/blog/${folder}?ref=${branch}`;

    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Error loading post list.');

        const files = await response.json();
        const mdFiles = files.filter(file => file.name.endsWith('.md'));

        postList.innerHTML = '';

        mdFiles.forEach(file => {
            const listItem = document.createElement('li');
            listItem.innerHTML = `<a href="#" data-url="${file.download_url}">${file.name.replace('.md', '')}</a>`;
            postList.appendChild(listItem);
        });

        postList.addEventListener('click', (e) => {
            if (e.target.tagName.toLowerCase() === 'a') {
                e.preventDefault();
                const postUrl = e.target.getAttribute('data-url');
                loadPostContent(postUrl);
            }
        });

    } catch (error) {
        console.error(error);
        postList.innerHTML = '<li>Error loading posts 😢</li>';
    }
}

Loading Post Content

async function loadPostContent(url) {
    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Error loading post.');

        const markdown = await response.text();
        const htmlContent = converter.makeHtml(markdown);
        contentDiv.innerHTML = htmlContent;
    } catch (error) {
        console.error(error);
        contentDiv.innerHTML = '<p>Error loading post content 😢</p>';
    }
}

fetchPostList();

Workflow

1

GitHub API Request

Fetches the contents of old/blog/posts/ directory from the update-urls branch using GitHub’s REST API
2

Filter Markdown Files

Filters the response to include only files ending with .md extension
3

Render Post List

Creates a clickable list of post titles (filename without .md extension), storing each post’s download_url in a data-url attribute
4

Event Delegation

Attaches a single click handler to the post list container that intercepts clicks on links and loads the corresponding post
5

Markdown Conversion

Fetches the raw Markdown content from the post’s download_url and converts it to HTML using Showdown.js
6

DOM Update

Injects the rendered HTML into the content div, displaying the blog post without page reload

API Endpoint Structure

const url = `https://api.github.com/repos/${username}/${repo}/contents/old/blog/${folder}?ref=${branch}`;
This constructs:
https://api.github.com/repos/MilesONerd/milesonerd.github.io/contents/old/blog/posts?ref=update-urls
GitHub’s API returns an array of file objects with metadata including name, path, download_url, and more. The download_url provides direct access to raw file content.

Error Handling

Both functions implement try-catch blocks with user-friendly error messages:

fetchPostList() Errors

Displays “Error loading posts 😢” if the API request fails or returns non-200 status

loadPostContent() Errors

Shows “Error loading post content 😢” if individual post fetching or Markdown conversion fails

Dependencies

The blog system requires:
  1. Showdown.js library loaded before list-posts.js
  2. DOM elements with IDs post-list and content
  3. CORS headers allowing GitHub API requests (see Deployment)
  4. Active internet connection for API access

Performance Considerations

Quote System

  • Bundle Size: 15.6 KB for 185 quotes (~84 bytes per quote)
  • Execution: O(1) random access, instant display
  • Memory: All quotes loaded upfront (negligible impact)

Blog System

  • Initial Load: Single API request to fetch post list
  • Per Post: One fetch request + Markdown parsing
  • Optimization: No pagination (assumes small post count)
For larger blogs, consider implementing:
  • Post pagination
  • Client-side caching of loaded posts
  • Lazy loading for the post list
  • Pre-rendered HTML instead of runtime Markdown conversion

Browser Compatibility

All JavaScript uses modern ES6+ features:
  • const and let declarations
  • Arrow functions
  • Template literals
  • Async/await
  • Destructuring assignment
  • fetch() API
These features are supported in all modern browsers (Chrome 55+, Firefox 52+, Safari 11+, Edge 15+). Legacy IE11 support would require transpilation with Babel.

Security Considerations

innerHTML Usage

Both updateQuote() and loadPostContent() use innerHTML to inject content:
document.getElementById('random-quote').innerHTML = `&ldquo;${quote}&rdquo;`;
contentDiv.innerHTML = htmlContent;
Quote System: Safe because quotes come from a static, trusted arrayBlog System: Potentially vulnerable to XSS if untrusted Markdown contains malicious HTML. Consider sanitizing HTML output with a library like DOMPurify.

GitHub API

The blog system makes unauthenticated requests to GitHub’s public API:
  • Rate Limit: 60 requests/hour per IP for unauthenticated requests
  • Public Data: Only accesses publicly available repository content
  • No Tokens: Does not require or expose API tokens
For higher rate limits, implement GitHub authentication with a personal access token (stored securely, not in client-side code).

Build docs developers (and LLMs) love