如何使用Tampermonkey在悬浮模态框中渲染需JS加载的第三方页面内容
解决方案
核心问题:你通过GM_xmlhttpRequest请求仅能拿到页面静态原始HTML,目标站点依赖JS渲染的动态内容不会自动生成,以下是3种可落地的实现方案,优先推荐第一种:
方案1:使用iframe嵌入(最简便)
直接在弹窗容器内插入iframe加载目标页面,iframe会自动执行页面内置JS完成渲染,无需手动处理JS执行逻辑,你只需要控制iframe的尺寸、滚动位置来露出你需要的页面区域即可。
可直接替换原有代码的对应部分:
// 替换原有弹窗插入逻辑 $("body").append ( `<div id="gmPopupContainer" style="display:none"> <iframe id="gmPopupIframe" frameborder="0" scrolling="no"></iframe> </div>`); (function() { var bindToShowStatus = function() { jQuery("td#key").off( "mouseenter mouseleave" ); jQuery("td#key").mouseenter(function(e) { const targetUrl = "https://review.opendev.org/c/openstack/sushy/+/819309"; $("#gmPopupIframe").attr("src", targetUrl); $("#gmPopupContainer").show(); }).mouseleave(function (e) { console.log("Out"); $("#gmPopupContainer").hide(); $("#gmPopupIframe").attr("src", ""); // 离开时清空避免后台继续加载 }); }; (new MutationObserver(check)).observe(document, {childList: true, subtree: true}); function check(changes, observer) { if(document.querySelector('td#key')) { observer.disconnect(); console.log("Loaded"); bindToShowStatus(); } } })(); // 替换原有样式 GM_addStyle ( ` #gmPopupContainer { position: fixed; top: 30%; left: 20%; background: white; border: 3px double black; border-radius: 1ex; z-index: 777; overflow: hidden; width: 800px; /* 按需调整可见区域宽度 */ height: 500px; /* 按需调整可见区域高度 */ } #gmPopupIframe { width: 1200px; /* 比容器宽,用来裁掉侧边栏等不需要的部分 */ height: 1000px; /* 比容器高,调整偏移量露出目标区域 */ margin-left: -200px; /* 左移裁掉侧边栏,按需调整数值 */ margin-top: -200px; /* 上移裁掉顶部导航,按需调整数值 */ } ` );
如果目标站点和当前脚本运行站点跨域,无法通过JS操作iframe内部DOM,调整上面的margin和宽高数值即可精准露出你要的区域,不需要操作内部DOM。
方案2:手动执行页面JS渲染(适合需要提取特定DOM的场景)
如果不想用iframe,可以把请求到的HTML里的所有script标签提取出来,放到隐藏的沙箱iframe中执行,等渲染完成后再提取目标DOM插入你的弹窗:
function processDocumentResponse (rspObj) { if (rspObj.status != 200 && rspObj.status != 304) { reportAJAX_Error (rspObj); return; } 'use strict'; // 创建隐藏的沙箱iframe用来执行目标站JS,避免污染当前页面 const sandboxIframe = document.createElement('iframe'); sandboxIframe.style.display = 'none'; document.body.appendChild(sandboxIframe); // 把请求到的HTML写入沙箱iframe,会自动执行内部JS sandboxIframe.contentDocument.write(rspObj.responseText); sandboxIframe.contentDocument.close(); // 等待JS执行完成,定时查询目标元素是否渲染完成 const checkTimer = setInterval(() => { // 替换成你要提取的目标元素选择器 const targetEle = sandboxIframe.contentDocument.querySelector('.target-class'); if (targetEle) { clearInterval(checkTimer); // 把目标元素克隆到你的弹窗里 document.getElementById('gmPopupContent').innerHTML = targetEle.outerHTML; // 删掉沙箱iframe释放资源 document.body.removeChild(sandboxIframe); } }, 100); // 超时处理,10秒没渲染完就终止 setTimeout(() => { clearInterval(checkTimer); document.body.removeChild(sandboxIframe); }, 10000); }
该方案要注意目标站JS可能有跨域限制、或者检测运行环境的反爬逻辑,可能会执行失败。
方案3:调用目标站点公开接口(性能最优)
你访问的是gerrit代码评审页面,gerrit有原生的REST API可以直接拿评审数据,不需要爬页面渲染,你直接请求对应接口拿JSON数据,自己拼接成你要的HTML结构插入弹窗即可,性能比前两个方案高很多,不会有多余的资源加载。
内容的提问来源于stack exchange,提问作者douglaslps
相关产品推荐
相关产品推荐

