Blooket Answer Highlighter

Highlights the correct answer in Blooket games. Toggle with Alt+X (or Cmd+X on Mac).

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

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

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

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

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

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

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

Advertisement:

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

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

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

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

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

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

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

Advertisement:

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Blooket Answer Highlighter
// @namespace    https://greasyfork.org/
// @version      1.1
// @description  Highlights the correct answer in Blooket games. Toggle with Alt+X (or Cmd+X on Mac).
// @author       
// @match        https://www.blooket.com/*
// @match        https://play.blooket.com/*
// @grant        none
// @run-at       document-idle
// @license MIT
// ==/UserScript==

(function () {
    let enabled = false;
    let highlightEl = null;

    const ANSWER_SELECTORS = [
        '[class*="answerText"]',
        '[class*="AnswerText"]',
        '[class*="answer"]',
        '[class*="Answer"]',
        '[class*="choice"]',
        '[class*="Choice"]',
        '[class*="option"]',
        '[class*="Option"]',
    ];

    const CORRECT_HINTS = [
        'correct',
        'Correct',
        'right',
        'Right',
        'isCorrect',
        'isRight',
        'true',
    ];

    function getReactFiber(el) {
        if (!el) return null;
        const key = Object.keys(el).find(k =>
            k.startsWith('__reactFiber') ||
            k.startsWith('__reactInternalInstance')
        );
        return key ? el[key] : null;
    }

    function getFiberProps(fiber) {
        if (!fiber) return null;
        let node = fiber;
        while (node) {
            if (node.memoizedProps) return node.memoizedProps;
            node = node.return;
        }
        return null;
    }

    function searchPropsForCorrect(props, depth) {
        if (!props || depth > 6) return false;
        for (const key of Object.keys(props)) {
            const val = props[key];
            if (typeof val === 'boolean' && val && CORRECT_HINTS.some(h => key.toLowerCase().includes(h.toLowerCase()))) return true;
            if (typeof val === 'string' && CORRECT_HINTS.some(h => val === h)) return true;
            if (val && typeof val === 'object' && !Array.isArray(val)) {
                if (searchPropsForCorrect(val, depth + 1)) return true;
            }
        }
        return false;
    }

    function walkFiberForCorrect(fiber) {
        if (!fiber) return false;
        let node = fiber;
        let steps = 0;
        while (node && steps < 20) {
            const props = node.memoizedProps || node.pendingProps;
            if (props && searchPropsForCorrect(props, 0)) return true;
            const stateNode = node.memoizedState;
            if (stateNode && typeof stateNode === 'object') {
                const queue = stateNode.queue;
                if (queue && queue.lastRenderedState !== undefined) {
                    if (searchPropsForCorrect({ state: queue.lastRenderedState }, 0)) return true;
                }
            }
            node = node.return;
            steps++;
        }
        return false;
    }

    function findCorrectElement() {
        const candidates = [];
        for (const sel of ANSWER_SELECTORS) {
            document.querySelectorAll(sel).forEach(el => candidates.push(el));
        }

        const seen = new Set();
        const unique = candidates.filter(el => {
            if (seen.has(el)) return false;
            seen.add(el);
            return true;
        });

        for (const el of unique) {
            const fiber = getReactFiber(el);
            if (walkFiberForCorrect(fiber)) return el;
        }

        for (const el of unique) {
            const classStr = el.className || '';
            if (CORRECT_HINTS.some(h => classStr.toLowerCase().includes(h.toLowerCase()))) return el;
        }

        return null;
    }

    function removeHighlight() {
        if (highlightEl && highlightEl.parentNode) {
            highlightEl.parentNode.removeChild(highlightEl);
        }
        highlightEl = null;
    }

    function placeHighlight(el) {
        removeHighlight();
        const rect = el.getBoundingClientRect();
        const marker = document.createElement('div');
        marker.id = '__blooket_highlight__';
        marker.style.cssText = `
            position: fixed;
            left: ${rect.left + rect.width / 2 - 14}px;
            top: ${rect.top - 22}px;
            width: 28px;
            height: 18px;
            background: #FFD700;
            border-radius: 4px;
            z-index: 999999;
            pointer-events: none;
            box-shadow: 0 2px 8px rgba(0,0,0,0.35);
            transition: opacity 0.2s;
        `;
        document.body.appendChild(marker);
        highlightEl = marker;
    }

    function run() {
        if (!enabled) { removeHighlight(); return; }
        const correct = findCorrectElement();
        if (correct) {
            placeHighlight(correct);
        } else {
            removeHighlight();
        }
    }

    let observer = null;

    function startObserver() {
        if (observer) return;
        observer = new MutationObserver(() => { if (enabled) run(); });
        observer.observe(document.body, { childList: true, subtree: true, attributes: true });
    }

    function stopObserver() {
        if (observer) { observer.disconnect(); observer = null; }
    }

    document.addEventListener('keydown', function (e) {
        const isMac = navigator.platform.toUpperCase().includes('MAC');
        const trigger = isMac ? (e.metaKey && e.key === 'x') : (e.altKey && e.key === 'x');
        if (!trigger) return;
        e.preventDefault();
        enabled = !enabled;
        if (enabled) { startObserver(); run(); }
        else { stopObserver(); removeHighlight(); }
    });
})();