RedditorsInFeed

Adds post authors to items in Reddit feed (on new Reddit)

// ==UserScript==
// @name         RedditorsInFeed
// @namespace    github.com/JasonAMelancon
// @version      2025-08-18
// @description  Adds post authors to items in Reddit feed (on new Reddit)
// @author       Jason Melancon
// @license      GNU AGPLv3
// @match        http*://www.reddit.com/
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

const DEBUG = false;
const CREDITED = "is-credited"; // my custom attribute for the post element
const POST = "shreddit-post"; // Reddit's custom element name
const FEED = "shreddit-feed"; // Reddit's custom element name
const scriptName = GM_info.script.name;

const feeds = document.getElementsByTagName(FEED);
if (feeds.length > 1) { // I have no idea whether this is or will ever be necessary
    console.log(`[${scriptName}] Multiple Reddit feed nodes present`);
}
const feed = feeds[0];

debugLog(`[${scriptName}] ${feed.querySelectorAll("article").length} initial articles`);

// Get the first few articles in the feed when the page loads, and add the author.
feed.querySelectorAll("article").forEach(article => {
    if (!isCredited(article)) {
        creditAuthor(article);
        markCredited(article);
    }
});

// Watch the page for new articles that appear when scrolling.
var dynamicScroll = new MutationObserver((mutations) => {
    debugLog(`[${scriptName}] ${mutations.length} new mutation objects`);
    for (var mutation of mutations) {
        let newArticleArray = Array.from(mutation.addedNodes).filter(node => node.nodeName === "ARTICLE");
        debugLog(`[${scriptName}] ${newArticleArray.length} new articles`);
        if (newArticleArray.length == 0) continue;
        // Add the author to the new articles as they appear.
        newArticleArray.forEach(article => {
            if (!isCredited(article)) {
                creditAuthor(article);
                markCredited(article);
            }
        });
    }
});
dynamicScroll.observe(feed, { childList: true, subtree: false, attributes: false, characterData: false });

// Put the author of a single article on the top line, next to the subreddit and post age.
function creditAuthor(article) {
    const post = article.querySelector(POST);
    const author = post.getAttribute("author");
    const creditBar = post.querySelector("span > span");
    const separator = creditBar.querySelector(".created-separator").cloneNode(/*deep = */true);
    creditBar.appendChild(separator);
    if (author === "[deleted]") {
        creditBar.insertAdjacentHTML('beforeend', `<span class="whitespace-nowrap text-neutral-content-weak">by ${author}</span>`);
    } else {
        creditBar.insertAdjacentHTML('beforeend', `<span class="whitespace-nowrap text-neutral-content-weak">by <a href="/u/${author}">${author}</a></span>`);
    }
}

// When scrolling down far enough, Reddit unloads posts from the top of the page,
// presumably to save memory. In general, Reddit unloads posts you scroll away from
// and loads or reloads posts you scroll toward. Without checking to make sure the
// post hasn't already been credited, this can cause this script to credit the post
// multiple times when the MutationObserver notices a credited post reappear in the
// feed.
//
// Therefore, check first.
function isCredited(article) {
    const post = article.querySelector(POST);
    debugLog(`[${scriptName}] credited check: ${post.hasAttribute(CREDITED)}`);
    return post.hasAttribute(CREDITED);
}

// Marks a post as already credited.
function markCredited (article) {
    const post = article.querySelector(POST);
    post.setAttribute(CREDITED, "");
}

function debugLog(msg) {
    if (DEBUG) console.log(msg);
}

})();