Greasy Fork is available in English.
强制绕过网站防嵌套限制,在任意网页小窗口看小说。带老板键(Alt+M)
// ==UserScript==
// @name 小说阅读器
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 强制绕过网站防嵌套限制,在任意网页小窗口看小说。带老板键(Alt+M)
// @author Gemini
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect *
// ==/UserScript==
(function() {
'use strict';
if (window.top !== window.self) return; // 防止嵌套套娃
// 创建主容器
const container = document.createElement('div');
container.id = 'moyu-container';
container.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
width: 350px;
height: 500px;
background: white;
border: 1px solid #ccc;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 2147483647;
display: none;
flex-direction: column;
resize: both;
overflow: hidden;
opacity: 0.85;
font-family: sans-serif;
border-radius: 6px;
`;
// 顶部控制栏
const header = document.createElement('div');
header.style.cssText = `
padding: 6px;
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
display: flex;
gap: 5px;
align-items: center;
`;
const urlInput = document.createElement('input');
urlInput.type = 'text';
urlInput.placeholder = '输入小说网址...';
urlInput.style.cssText = 'flex-grow: 1; padding: 4px; font-size: 12px; border: 1px solid #ccc; border-radius: 3px;';
urlInput.value = GM_getValue('moyu_last_url', '');
const loadBtn = document.createElement('button');
loadBtn.textContent = '加载';
loadBtn.style.cssText = 'font-size: 12px; padding: 4px 8px; cursor: pointer; border: 1px solid #ccc; background: white; border-radius: 3px;';
const opacityInput = document.createElement('input');
opacityInput.type = 'range';
opacityInput.min = '0.1';
opacityInput.max = '1';
opacityInput.step = '0.05';
opacityInput.value = '0.85';
opacityInput.style.cssText = 'width: 60px; cursor: pointer;';
header.appendChild(urlInput);
header.appendChild(loadBtn);
header.appendChild(opacityInput);
// 内容展示区 (使用 srcdoc 强行渲染)
const iframe = document.createElement('iframe');
iframe.style.cssText = `
width: 100%;
flex-grow: 1;
border: none;
background: #fff;
`;
container.appendChild(header);
container.appendChild(iframe);
document.body.appendChild(container);
// 核心抓取逻辑 (绕过限制)
const loadUrl = () => {
let url = urlInput.value.trim();
if (!url) return;
if (!url.startsWith('http')) url = 'https://' + url;
loadBtn.textContent = '加载中...';
// 使用油猴特权跨域请求
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(response) {
loadBtn.textContent = '加载';
if(response.status !== 200) {
iframe.srcdoc = `<div style="padding:20px;color:red;">加载失败,对方服务器返回: ${response.status}</div>`;
return;
}
let html = response.responseText;
// 1. 注入 base 标签,修复网页里的 CSS 和图片路径
const baseTag = `<base href="${url}">`;
if (html.includes('<head>')) {
html = html.replace('<head>', `<head>${baseTag}`);
} else {
html = baseTag + html;
}
// 2. 拦截网页里的所有点击事件,点击下一章时由我们的脚本代为拉取,防止跳出
const injectScript = `
<script>
document.addEventListener('click', function(e) {
const a = e.target.closest('a');
if (a && a.href && !a.href.startsWith('javascript:')) {
e.preventDefault(); // 阻止默认跳转
window.parent.postMessage({ type: 'MOYU_NAVIGATE', url: a.href }, '*');
}
});
</script>
`;
html += injectScript;
// 强行把抓回来的代码塞进窗口
iframe.srcdoc = html;
GM_setValue('moyu_last_url', url);
},
onerror: function() {
loadBtn.textContent = '加载';
iframe.srcdoc = `<div style="padding:20px;color:red;">请求出错:可能是网络问题或跨域极度严格。</div>`;
}
});
};
loadBtn.addEventListener('click', loadUrl);
urlInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') loadUrl(); });
opacityInput.addEventListener('input', (e) => { container.style.opacity = e.target.value; });
// 接收内部跳转消息
window.addEventListener('message', (e) => {
if (e.data && e.data.type === 'MOYU_NAVIGATE') {
urlInput.value = e.data.url;
loadUrl();
}
});
// 老板键
document.addEventListener('keydown', (e) => {
if (e.altKey && e.key.toLowerCase() === 'm') {
container.style.display = container.style.display === 'none' ? 'flex' : 'none';
if (container.style.display === 'flex' && urlInput.value && !iframe.srcdoc) {
loadUrl();
}
}
});
})();