mike's lib

快速處理畫面操作

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/570074/1777131/mike%27s%20lib.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         mike's lib
// @namespace    小說
// @version      2026-03-18
// @description  工具函式庫
// @author       mike
// @match        http*://*/*
// @grant        none
// ==/UserScript==

(function (global) {
    'use strict';

    // 等待物件 func
    const waitElement = (selector, callback) => {
        const interval = setInterval(() => {
            const element = document.querySelector(selector);
            if (element) {
                clearInterval(interval);
                callback(element);
            }
        }, 100);
    };

    // 移除物件
    const removeElement = (baseObj, removeTarget) => {
        let beRemoveObj = baseObj.querySelector(removeTarget);
        if (beRemoveObj) {
            beRemoveObj.remove();
        }
    };
    
    // 等並刪除
    const waitAndKillElement = (selector) => {
        waitElement(selector, () => {
            removeElement(document, selector);
        });
    };

    // 重置寬高
    const resetMarginPadding = (element) => {
        element.style.margin = '0px';
        element.style.padding = '0px';
    };
    
    // 取得 平台名稱
    const getPlatform = (platformList) => {
        const pathname = window.location.pathname // 用網址查
        const findPlatform = platformList.find(item => {
            const [key, value] = Object.entries(item)[0];
            return pathname.includes(key);
        });
        return Object.values(findPlatform)[0]
    }

    // 製作快速跳轉浮窗
    // initChapterNavigator({selectors: ['.entry-content', 'p'], chapterStartsWith: ['[', '【']});
    const initChapterNavigator = ({
        selectors,
        chapterStartsWith
    }) => {

        if (
            !Array.isArray(selectors) ||
            selectors.length === 0 ||
            selectors.some(s => typeof s !== 'string' || !s.trim())
        ) {
            console.log('[ChapterNavigator] selectors 必須為非空字串陣列');
            return;
        }

        if (
            !Array.isArray(chapterStartsWith) ||
            chapterStartsWith.length === 0 ||
            chapterStartsWith.some(s => typeof s !== 'string' || !s)
        ) {
            console.log('[ChapterNavigator] chapterStartsWith 必須為非空字串陣列');
            return;
        }

        const queryBySelectors = () => {
            let current = document;

            for (let i = 0; i < selectors.length; i++) {
                if (!current) return [];

                if (i === selectors.length - 1) {
                    return Array.from(current.querySelectorAll(selectors[i]));
                }

                current = current.querySelector(selectors[i]);
            }

            return [];
        };

        const areaKeyList = () => {
            return queryBySelectors().filter(el => {
                const text = el.textContent.trim();
                return chapterStartsWith.some(prefix =>
                    text.startsWith(prefix)
                );
            });
        };

        const gotoText = (findText) => {
            areaKeyList().forEach(el => {
                if (el.textContent.includes(findText)) {
                    el.scrollIntoView({
                        behavior: 'smooth',
                        block: 'start'
                    });
                }
            });
        };

        const getChapterList = () =>
            areaKeyList().map(el => el.textContent);

        const createFloatingBox = (chapterList) => {
            if (document.getElementById('floatingBox')) {
                return;
            }

            const floatingBox = document.createElement('div');
            floatingBox.id = 'floatingBox';

            Object.assign(floatingBox.style, {
                position: 'fixed',
                right: '10px',
                bottom: '10px',
                width: '150px',
                height: '450px',
                overflow: 'auto',
                padding: '10px',
                backgroundColor: '#333',
                color: 'white',
                borderRadius: '8px',
                zIndex: '1000'
            });

            chapterList.forEach(text => {
                const btn = document.createElement('button');

                Object.assign(btn.style, {
                    width: '100%',
                    margin: '2px 0',
                    padding: '10px 5px',
                    backgroundColor: '#f44336',
                    color: 'white',
                    border: 'none',
                    borderRadius: '4px',
                    cursor: 'pointer'
                });

                btn.textContent = text;
                btn.onclick = () => gotoText(text);

                floatingBox.appendChild(btn);
            });

            document.body.appendChild(floatingBox);
        };

        const chapterList = getChapterList();

        if (!chapterList.length) {
            console.log('[ChapterNavigator] 未找到任何符合規則的章節');
            return;
        }

        createFloatingBox(chapterList);
    };

    // ⭐ 關鍵:對外輸出
    global.mikeLib = {
        waitElement,
        removeElement,
        waitAndKillElement,
        resetMarginPadding,
        getPlatform,
        initChapterNavigator
    };

})(window);