Back to top

scroll to top

目前为 2020-06-20 提交的版本。查看 最新版本

// ==UserScript==
// @name         Back to top
// @namespace    cyyyu
// @version      0.1
// @description  scroll to top
// @author       Chuang Yu
// @match        https://*/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    /* written in es5 */

    var rootNode = document.querySelector("html")
    var skipNodes = new Set("HEAD", "SCRIPT", "STYLE")
    var prevLocations = new Map() // node: number
    var timers = new Map() // node: number
    var backToPrevLocationDelay = 10000 // 10s
    var scrollableNodes = getScrollableNodes(rootNode, [])

    scrollableNodes.forEach(function (node) {
        node.addEventListener("dblclick", function (evt) {
            evt.stopPropagation()

            if (!evt.altKey) return

            if (!isAtTop(node)) {
                clearTimeout(timers.get(node))
                scrollTo(node, 0)
            } else if (prevLocations.has(node)) {
                scrollTo(node, prevLocations.get(node))
            }
        })
    })

    function isAtTop(node) {
        return node.scrollTop === 0
    }

    function scrollTo(node, top) {
        var prevScrollBehavior = node.style.scrollBehavior
        var prevLocation = node.scrollTop

        node.style.scrollBehavior = "smooth"
        node.scrollTop = top
        node.style.scrollBehavior = prevScrollBehavior

        if (top === 0) {
            prevLocations.set(node, prevLocation)
            var timer = setTimeout(
                function () { prevLocations.delete(node) },
                backToPrevLocationDelay
            )
            timers.set(node, timer)
        }
    }

    function isScrollable(node) {
        return (
            (node.scrollHeight - node.clientHeight > 100) &&
            (node.clientHeight > 50)
        )
    }

    function getScrollableNodes(node, blocks) {
        if (isScrollable(node)) {
            blocks.push(node)
        }

        if (node.children && node.children.length > 0) {
            for (var i = 0; i < node.children.length; i++) {
                var childNode = node.children.item(i)
                if (skipNodes.has(childNode.tagName)) continue;
                getScrollableNodes(node.children.item(i), blocks)
            }
        }

        return blocks
    }
})();