Scroll to Top Button

Scroll to Top button that appears after scrolling down 20%

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Scroll to Top Button
// @namespace    U2Nyb2xsIHRvIFRvcCBCdXR0b24
// @version      2.4
// @description  Scroll to Top button that appears after scrolling down 20%
// @author       smed79
// @license      MIT
// @icon         https://i25.servimg.com/u/f25/11/94/21/24/scroll10.png
// @match        *://*/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
  'use strict';

  // --- Create the host container ---
  const host = document.createElement('div');
  host.id = 'r2t-simple-btn';
  
  // Attach shadow DOM to perfectly isolate CSS from the website
  const shadow = host.attachShadow({ mode: 'closed' });

  // Add the CSS and HTML
  // Key fixes: added `contain`, `will-change`, and switched to `translate3d` for GPU isolation
  shadow.innerHTML = `
    <style>
      :host {
          all: initial;
          position: fixed;
          bottom: 25px;
          right: 25px;
          z-index: 2147483647;
          pointer-events: none;
          contain: layout paint style; /* Tells the browser this element won't affect page layout */
      }
      .circle {
          box-sizing: border-box;
          margin: 0;
          padding: 0;
          width: 40px;
          height: 40px;
          border-radius: 50%;
          background: rgba(0, 0, 0, 0.55);
          box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
          cursor: pointer;
          pointer-events: auto;
          display: flex;
          align-items: center;
          justify-content: center;
          opacity: 0;
          visibility: hidden;
          
          /* Force GPU layer and hardware acceleration */
          transform: translate3d(0, 15px, 0); 
          will-change: transform, opacity, visibility;
          backface-visibility: hidden; 
          
          transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s ease, background-color 0.2s ease;
          -webkit-tap-highlight-color: transparent;
      }
      .circle.show {
          opacity: 1;
          visibility: visible;
          transform: translate3d(0, 0, 0);
      }
      .circle:hover {
          background: rgba(0, 0, 0, 0.85);
      }
      .glyph {
          box-sizing: border-box;
          display: block;
          width: 12px;
          height: 12px;
          border-top: 4px solid #fff;
          border-left: 4px solid #fff;
          border-radius: 2px;
          transform: rotate(45deg) translate(2px, 2px);
          pointer-events: none;
      }
    </style>
    <div class="circle">
      <div class="glyph"></div>
    </div>
  `;

  const circle = shadow.querySelector('.circle');
  (document.documentElement || document.body).appendChild(host);

  // --- Scroll Logic (Throttled with requestAnimationFrame) ---
  let isTicking = false;

  window.addEventListener('scroll', () => {
      if (!isTicking) {
          window.requestAnimationFrame(() => {
              const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
              if (scrollHeight > 0) {
                  const scrollPercent = window.scrollY / scrollHeight;
                  if (scrollPercent > 0.20) {
                      circle.classList.add('show');
                  } else {
                      circle.classList.remove('show');
                  }
              }
              isTicking = false;
          });
          isTicking = true;
      }
  }, { passive: true });

  // --- Click Logic ---
  circle.addEventListener('click', () => {
      try { 
          window.scrollTo({ top: 0, behavior: 'smooth' }); 
      } catch (err) { 
          window.scrollTo(0, 0); 
      }
  });

})();