// ==UserScript== // @name 粉笔背题增强(ABCD字母盒 + 下一题 + 草稿尺寸锁定 + 自动全开草稿) // @namespace marvis.fenbi.enhance // @version 2.1.2 // @description 每题注入 ABCD 字母选择盒(D 后带「下一题」),草稿画布锁定为题干+选项高度,页面加载后自动打开全部草稿;MutationObserver 适配 SPA 增量渲染,按题目作用域去重。 // @match *://spa.fenbi.com/* // @run-at document-idle // @grant none // ==/UserScript== // // 功能清单 // ① 每道题 .title-left 内追加 .marvis-lbox,内含 A/B/C/D 四个字母按钮, // 点击即选中该题对应选项(label.click());盒子 z-index:260 高于草稿画板 130。 // ② 盒子内 D 之后追加「下一题」按钮,点击滚动到下一题并强制打开该题草稿。 // ③ 每个 .draft-container 高度锁定为「题干+选项」区域高度(app-question-choice // 的 bottom 相对 .ti 顶部),top 固定 0,解析展开时 .ti 变高不拉伸画布。 // ④ 页面加载后自动批量打开所有题的草稿(点击 li.draft.tool); // 自动打开每题只尝试一次,用户手动关闭后不会被重新打开。 // 适配:MutationObserver + rAF 节流,覆盖 SPA 增量渲染; // 注入以「题号」为 key 去重,元素复用渲染新题时会自动重建。 // (function () { 'use strict'; if (window.__MARVIS_FENBI_ENHANCE__) return; window.__MARVIS_FENBI_ENHANCE__ = true; var DLOCK_ATTR = 'data-marvis-dlock'; var AUTO_ATTR = 'data-marvis-autoopen'; var BOX_ATTR = 'data-marvis-boxkey'; /* ---------- 样式 ---------- */ var STYLE_ID = 'marvis-fenbi-enhance-style'; if (!document.getElementById(STYLE_ID)) { var st = document.createElement('style'); st.id = STYLE_ID; st.textContent = [ '.marvis-lbox{display:flex;align-items:center;gap:15px;margin-left:10px;position:relative;z-index:260;}', '.marvis-lbox .marvis-lbtn{box-sizing:border-box;width:22px;height:22px;padding:0;border-radius:4px;', 'border:1px solid #3b6ef0;background:#fff;color:#3b6ef0;font-size:12px;line-height:20px;text-align:center;cursor:pointer;}', '.marvis-lbox .marvis-lbtn:hover{background:#3b6ef0;color:#fff;}', '.marvis-lbox .marvis-lbtn:active{background:#2b55c8;color:#fff;}', '.marvis-lbox .marvis-next{box-sizing:border-box;height:22px;margin-left:6px;padding:0 8px;border-radius:4px;', 'border:1px solid #3b6ef0;background:#3b6ef0;color:#fff;font-size:12px;line-height:20px;cursor:pointer;white-space:nowrap;}', '.marvis-lbox .marvis-next:hover{background:#2b55c8;}' ].join(''); (document.head || document.documentElement).appendChild(st); } /* 页面整体左右各缩 20px */ if (!document.getElementById('marvis-page-pad')) { var pp = document.createElement('style'); pp.id = 'marvis-page-pad'; pp.textContent = '.ti{margin:0 40px!important}'; document.head.appendChild(pp); } /* ---------- 工具 ---------- */ function tis() { return [].slice.call(document.querySelectorAll('.ti')); } // 题目作用域 key:题号(SPA 复用同一 .ti 元素渲染新题时 key 会变化) function tiKey(ti) { var i = ti.querySelector('.title-index'); return i ? (i.textContent || '').trim() : ''; } // 「题干+选项」区域相对 .ti 顶部的高度 function measureTi(ti) { var qc = ti.querySelector('app-question-choice'); if (!qc) return null; var tiR = ti.getBoundingClientRect(); var r = qc.getBoundingClientRect(); var h = Math.round(r.bottom - tiR.top); return h > 0 ? h : null; } /* ---------- ③ 草稿尺寸锁定 ---------- */ function lockDraft(ti) { var dc = ti.querySelector('.draft-container'); if (!dc || dc.getAttribute(DLOCK_ATTR)) return; var h = measureTi(ti); if (!h) return; dc.style.setProperty('height', (h + 48) + 'px', 'important'); dc.style.setProperty('top', '0px', 'important'); dc.setAttribute(DLOCK_ATTR, '1'); } /* ---------- ④ 自动打开草稿 ---------- */ function clickDraftTool(ti) { if (ti.querySelector('.draft-container')) return; // 已开 var tool = ti.querySelector('li.draft.tool'); if (tool) tool.click(); } // 自动打开:每题只尝试一次(key = 题号),避免重扫时把用户手动关掉的草稿又打开 function autoOpenDraft(ti) { var key = tiKey(ti); if (ti.getAttribute(AUTO_ATTR) === key) return; ti.setAttribute(AUTO_ATTR, key); clickDraftTool(ti); } /* ---------- ①② ABCD 字母盒 + 下一题 ---------- */ function buildBox(ti) { var tl = ti.querySelector('.title-left'); if (!tl) return; var key = tiKey(ti); var old = tl.querySelector('.marvis-lbox'); if (old) { if (old.getAttribute(BOX_ATTR) === key) return; // 同一题已注入,跳过 old.remove(); // 元素复用渲染新题,重建 } var box = document.createElement('div'); box.className = 'marvis-lbox'; box.setAttribute(BOX_ATTR, key); var radios = [].slice.call(ti.querySelectorAll('li.choice-radio')); var letters = ['A', 'B', 'C', 'D']; radios.forEach(function (cr, i) { var letter = letters[i]; if (!letter) return; var b = document.createElement('button'); b.type = 'button'; b.className = 'marvis-lbtn'; b.setAttribute('data-letter', letter); b.textContent = letter; b.addEventListener('click', function (ev) { ev.preventDefault(); ev.stopPropagation(); var lab = cr.querySelector('label.choice-radio-label') || cr.querySelector('label'); if (lab) lab.click(); // 触发原生选项选择 }); box.appendChild(b); }); var nb = document.createElement('button'); nb.type = 'button'; nb.className = 'marvis-next'; nb.textContent = '下一题'; nb.addEventListener('click', function (ev) { ev.preventDefault(); ev.stopPropagation(); var all = tis(); var idx = all.indexOf(ti); var next = idx > -1 ? all[idx + 1] : null; if (!next) return; next.scrollIntoView({ behavior: 'smooth', block: 'start' }); clickDraftTool(next); // 用户主动:强制打开 setTimeout(function () { lockDraft(next); }, 450); }); box.appendChild(nb); tl.appendChild(box); // 追加到 .title-left 末尾,避免负 left 被父级 overflow 裁切 } /* ---------- 单题处理 ---------- */ function processTi(ti) { buildBox(ti); autoOpenDraft(ti); lockDraft(ti); } /* ---------- 全量扫描 ---------- */ function scan() { var list = tis(); for (var i = 0; i < list.length; i++) processTi(list[i]); } /* ---------- SPA 增量渲染适配 ---------- */ var pending = false; var mo = new MutationObserver(function () { if (pending) return; pending = true; requestAnimationFrame(function () { pending = false; scan(); }); }); function startMO() { mo.observe(document.body, { childList: true, subtree: true }); } if (document.body) { scan(); startMO(); } else { document.addEventListener('DOMContentLoaded', function () { scan(); startMO(); }); } // 草稿层动态插入后补偿锁定一次 setTimeout(function () { scan(); }, 800); window.__MARVIS_FENBI_SCAN__ = scan; })();