Greasy Fork is available in English.
Highlights the correct answer in Blooket games. Toggle with Alt+X (or Cmd+X on Mac).
// ==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(); }
});
})();