Reddit - Old School

Allows for easy navigation through a sub by pressing the left/right arrow keys, amongst other useful experience improvements. (Only works for old.reddit.com, so this script automatically redirects you there.)

当前为 2020-04-26 提交的版本,查看 最新版本

// ==UserScript==
// @name        Reddit - Old School
// @version     1.4
// @grant       none
// @include     https://*.reddit.com/*
// @namespace   selbi
// @description Allows for easy navigation through a sub by pressing the left/right arrow keys, amongst other useful experience improvements. (Only works for old.reddit.com, so this script automatically redirects you there.)
// ==/UserScript==

////////////////////////

// Entry Point with redirect
let url = window.location.href;

const subdomainRegex = /^https?\:\/\/((?!old)\w+)/i;
let matches = url.match(subdomainRegex);
if (matches != null) {
  let subdomain = matches[1];
  url = url.replace(subdomain, "old");
  window.location.href = url;
} else {
  let isComments = url.includes("/comments/");
  redditScrollSetup(isComments); 
}

////////////////////////

// Global variable to keep track of the currently selected post
let currentPost = null;

////////////////////////

// Setup
function redditScrollSetup(isComments) {
  if (isComments) {  
    redditScrollSetupClickComments();
  } else {
    redditScrollSetupClickPost();
    redditScrollSetupClickableArrows();
    redditScrollSetupArrowKeys();  
  }
}

// Open/close comment by clicking it
function redditScrollSetupClickComments() {
  document.querySelector(".commentarea .sitetable").addEventListener('click', function(event) {
    let targetElem = event.target;
    if (isIgnoredElem(targetElem) || window.getSelection().toString() !== "") {
      return;
    }
    let entry = findParentElemByClass(targetElem, "entry", 5);
    if (entry != null) {
      entry.querySelector(".expand").click();
      scrollToY(entry);
    }
  });
}

// Open/close post by clicking it
function redditScrollSetupClickPost() {
  document.getElementById("siteTable").addEventListener('click', function(event) {
    let targetElem = event.target;
    scrollToTarget(targetElem);
  });
}


// Directions
const SCROLL_RIGHT = 1;
const SCROLL_LEFT = -1;

// Clickable arrows to the right to enable scrolling with the mouse
function redditScrollSetupClickableArrows() {
  let rightClickArrow = document.createElement("div");
  rightClickArrow.classList.add("clickableNavigationArrow")
  rightClickArrow.innerHTML = "►";
  rightClickArrow.onclick = function(){leftRightScroll(SCROLL_RIGHT)};
  document.getElementById("siteTable").appendChild(rightClickArrow);

  let leftClickArrow = document.createElement("div");
  leftClickArrow.classList.add("clickableNavigationArrow", "clickableNavigationArrowLeft")
  leftClickArrow.innerHTML = "◄";
  leftClickArrow.onclick = function(){leftRightScroll(SCROLL_LEFT)};
  document.getElementById("siteTable").appendChild(leftClickArrow);
}


// Arrow Keys Navigation
const LCTRL     = '17';
const LSHIFT    = '16';
const ARR_LEFT  = '37';
const ARR_RIGHT = '39';

// Scroll by pressing left/right arrow keys
function redditScrollSetupArrowKeys() {
  document.onkeydown = function browseContent(e) {
    // Fetch the key code and only allow left/right
    let key = e.keyCode;
    if (key == ARR_LEFT) {
      leftRightScroll(SCROLL_LEFT);
    } else if (key == ARR_RIGHT) {
      leftRightScroll(SCROLL_RIGHT);
    }
  }
}

function leftRightScroll(direction) {
  // Don't scroll the page if we're currently in a textbox
  if (isIgnoredElem(document.activeElement)) {
    return;
  }
  
  // If no post is set yet, jump to the very top one
  if (currentPost == null) {
    scrollToTarget(document.querySelector(".sitetable .entry"));
    return;
  }
  
  // Find the parent container for the post
  let post = findParentElemByClass(currentPost, "thing", 2);
  if (post == null) {
    return;
  }
  
  // Set the relative browsing methods depending on whether left or right was pressed
  let sibling, child;
  if (direction == SCROLL_LEFT) {
    sibling = function(post){return post.previousElementSibling;}
    child = function(post){return post.lastChild;}
  } else if (direction == SCROLL_RIGHT) {
    sibling = function(post){return post.nextElementSibling;}
    child = function(post){return post.firstChild;}
  }
  
  // Find the new sibling post relative to the currently opened one
  // (Plus some fluff to make page transitions seamless and skipping over non-expandable posts)
  do {
    let siblingPost = sibling(post);
    if (siblingPost == null) {
      post = post.parentElement;
    } else if (siblingPost.classList.contains("sitetable")) {
      post = child(siblingPost);
    } else {
      post = siblingPost; 
    }
    if (post == null) {
      return;
    }
  } while (!post.classList.contains("thing") || !post.querySelector(".expando-button") || post.classList.contains("promoted"));

  // Close the previous post, if it was still open
  let expando = currentPost.querySelector(".expando-button");
  if (expando.classList.contains("expanded")) {
    expando.click();
  }
  // Open the new post and scorll to it
  let scrollTarget = post.querySelector(".entry");
  scrollToTarget(scrollTarget);
}

////////////////////////
// All kinds of helper functions

const MAX_PARENT_DEPTH = 7;
function scrollToTarget(targetElem) {
  if (targetElem.classList.contains("expando-button")) {
    scrollToY(targetElem.parentElement);
  } else {
    let entry = findParentElemByClass(targetElem, "entry", MAX_PARENT_DEPTH);
    if (entry != null) {
      entry.querySelector(".expando-button").click();
      currentPost = entry;
    }
  }
}

function findParentElemByClass(elem, className, maxSearchDepth) {
  if (elem == null || maxSearchDepth <= 0) {
    return null; 
  } else if (elem.classList.contains(className)) {
    return elem; 
  }
  return findParentElemByClass(elem.parentElement, className, maxSearchDepth - 1);
}

function scrollToY(elem) {
  let scroll = elem.getBoundingClientRect().top + window.scrollY;
  window.scroll({
    top: scroll,
    left: 0,
    behavior: 'smooth'
  });
}

const IGNORED_TAG_TYPES = ["a", "textarea", "input"];
function isIgnoredElem(elem) {
  let tag = elem.tagName.toLowerCase();
  return IGNORED_TAG_TYPES.includes(tag);
}

////////////////////////

function addGlobalStyle(css) {
  let style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = css;
  
  let head = document.getElementsByTagName('head')[0];
  if (head != null) {
    head.appendChild(style); 
  }
}

addGlobalStyle(`
  body {
    overflow-x: hidden;
  }

  .entry {
    transition: 0.06s ease;
  }
  .entry:hover, .res-nightmode .entry.res-selected:hover {
    background-color: rgba(128,128,128, 0.2) !important;
    cursor: pointer;
  }

	.NERPageMarker {
	  display: none;
  }

  :root {
    --scroll-arrow-width: 6vw;
  }

 .clickableNavigationArrow {
    position: fixed;
    bottom: 0;
    right: 0;
    width: var(--scroll-arrow-width);
    font-size: var(--scroll-arrow-width);
    text-align: center;
    opacity: 0.2;
    transition: 0.1s ease;
    user-select: none;
    color: gray;
  }

  .clickableNavigationArrow:hover {
    opacity: 0.8;
    cursor: pointer;
  }

  .clickableNavigationArrowLeft {
    right: var(--scroll-arrow-width);
  }
`);

////////////////////////

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址