Keep Reddit's post back button visible while scrolling
// ==UserScript==
// @name Reddit Floating Back Button
// @namespace https://reddit.com/
// @version 1.0
// @description Keep Reddit's post back button visible while scrolling
// @match https://www.reddit.com/*
// @match https://reddit.com/*
// @grant none
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
var BUTTON_SELECTOR = 'pdp-back-button[post-id]';
var HEADER_SELECTOR = '#pdp-credit-bar';
var TITLE_SELECTOR = 'h1[slot="title"]';
var currentButton = null;
var rafId = null;
var observer = null;
function isPostPage() {
return /\/comments\//i.test(location.pathname);
}
function getBackButton() {
return document.querySelector(BUTTON_SELECTOR);
}
function getAnchorElement(button) {
if (!button) {
return null;
}
var header = document.querySelector(HEADER_SELECTOR);
if (header) {
return header;
}
var title = document.querySelector(TITLE_SELECTOR);
if (title) {
return title;
}
return button.parentElement || button;
}
function applyFloatingStyle(button) {
if (!button) {
return;
}
var anchor = getAnchorElement(button);
var left = 16;
var top = 76;
if (anchor) {
var rect = anchor.getBoundingClientRect();
left = Math.max(12, Math.round(rect.left - 52));
}
button.style.setProperty('position', 'fixed', 'important');
button.style.setProperty('top', top + 'px', 'important');
button.style.setProperty('left', left + 'px', 'important');
button.style.setProperty('right', 'auto', 'important');
button.style.setProperty('bottom', 'auto', 'important');
button.style.setProperty('z-index', '9999', 'important');
button.style.setProperty('display', 'block', 'important');
button.style.setProperty('visibility', 'visible', 'important');
button.style.setProperty('opacity', '1', 'important');
button.style.setProperty('transform', 'none', 'important');
button.style.setProperty('margin', '0', 'important');
button.style.setProperty('pointer-events', 'auto', 'important');
}
function clearFloatingStyle(button) {
if (!button) {
return;
}
button.style.removeProperty('position');
button.style.removeProperty('top');
button.style.removeProperty('left');
button.style.removeProperty('right');
button.style.removeProperty('bottom');
button.style.removeProperty('z-index');
button.style.removeProperty('display');
button.style.removeProperty('visibility');
button.style.removeProperty('opacity');
button.style.removeProperty('transform');
button.style.removeProperty('margin');
button.style.removeProperty('pointer-events');
}
function updateButton() {
var button = getBackButton();
if (!isPostPage()) {
if (currentButton) {
clearFloatingStyle(currentButton);
}
currentButton = null;
return;
}
if (!button) {
currentButton = null;
return;
}
currentButton = button;
applyFloatingStyle(button);
}
function requestUpdate() {
if (rafId !== null) {
return;
}
rafId = window.requestAnimationFrame(function () {
rafId = null;
updateButton();
});
}
function installObserver() {
if (observer) {
observer.disconnect();
}
observer = new MutationObserver(function () {
requestUpdate();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
function installHistoryHooks() {
var originalPushState = history.pushState;
var originalReplaceState = history.replaceState;
history.pushState = function () {
var result = originalPushState.apply(this, arguments);
setTimeout(requestUpdate, 0);
setTimeout(requestUpdate, 300);
setTimeout(requestUpdate, 1000);
return result;
};
history.replaceState = function () {
var result = originalReplaceState.apply(this, arguments);
setTimeout(requestUpdate, 0);
setTimeout(requestUpdate, 300);
setTimeout(requestUpdate, 1000);
return result;
};
window.addEventListener('popstate', requestUpdate, true);
}
function installListeners() {
window.addEventListener('scroll', requestUpdate, { passive: true });
window.addEventListener('resize', requestUpdate, true);
}
function init() {
installObserver();
installHistoryHooks();
installListeners();
requestUpdate();
setTimeout(requestUpdate, 500);
setTimeout(requestUpdate, 1500);
}
init();
})();