如何在iframe预期DOM变更前注册MutationObserver并确保检测到变化?
父文档监听同源iframe内DOM变化的问题
我想从父文档监听iframe内的DOM变化,当目标元素加载完成后执行后续操作,但尝试使用MutationObserver始终无法检测到变化,推测问题出在时机上。
前提假设
- 父文档与iframe为同源(Same Origin)
- 我可控制iframe添加至DOM的时机
- 我可控制触发iframe内容填充的时机
- 触发iframe内容填充时,MutationObserver需已注册就绪,能观测到后续DOM变更并触发回调
补充信息
我操作的是第三方网站,点击特定链接后会加载一个层级内容,已成功将其加载到同DOM下的iframe中。该iframe完全加载需3-4秒,但目标元素加载远早于这个时间,虽然iframe.onload能正常工作,但耗时过长,我希望节省时间。
我尝试了多个方案,参考了相关旧帖,但截至2024年中仍未成功,不确定该需求是否可行。以下是简化测试代码(在Chrome/ Firefox的example.com控制台运行),但Observer未检测到任何变化:
const setObserver = (iframe, sel) => { // Create a MutationObserver instance that observes the // documentElement of the passed <iframe> element. // Return the observer. const observer = new MutationObserver( (mutations, obsvr) => { // observer.disconnect(); // TODO when it works console.log(mutations); document.body.prepend("Mutation triggered!"); } ); observer.observe( iframe.contentWindow.document.documentElement, { childList: true, subtree: true } ); return observer; }; const creaIframeInDom = () => { // Create an <iframe> and prepend it to the parent DOM. // Return the <iframe> element. const ifr = document.createElement('iframe'); ifr.id = 'myIframe'; ifr.classList.add('my-class'); ifr.name = 'myIframe'; document.body.prepend(ifr); return ifr; }; const creaAElemInDom = () => { // Create an <a> element linking to example.com, and prepend it to DOM. // Return the <a> element. const a = document.createElement('a'); a.href = 'https://example.com'; a.textContent = 'This is the trigger link'; a.classList.add('trigger'); document.body.prepend(a); return a; }; const creaAElemInIframe = () => { // Create an <a> element with class="Link" and prepend it to the // iframe's body. // Return the <a> element. const a = document.createElement('a'); a.href = '#'; a.textContent = 'This is the EXPECTED element'; a.classList.add('Link'); if (iframe.contentDocument?.body.prepend(a)) { return a; } return null; }; // This selects the element that we'll be waiting to observe when it // loads inside the iframe, later: const selector = 'a.Link'; // Create a test iframe in the DOM const iframe = creaIframeInDom(); // Set a load event listener: iframe.onload = function() { console.log('IFRAME HAS FIRED THE LOAD EVENT!!!!'); console.log('readyState: ' + iframe.contentDocument.readyState); console.log( 'iframe body\'s child nodes: ', iframe.contentDocument.body.childNodes ); }; // Set the observer const myObserver = setObserver(iframe, selector); // Set our test link in the parent DOM: const aElem = creaAElemInDom(); // ...and give it the iframe as target: aElem.target = 'myIframe'; // Click the trigger link, to load the page in the iframe and // hopefully see the MutationObserver reaction as it loads. // Make it a macro-task, and delay it a bit in the hope that it executes // after above: (NOTE: Times are completely arbitrary here) setTimeout(() => { aElem.dispatchEvent( new MouseEvent( 'click', {bubbles: false} ) ); // A bit later, append an <a> element to iframe's <body> setTimeout(() => creaAElemInIframe(), 500); }, 1000 );
后续更新
感谢各位的评论与回答。针对@traktor的疑问,我在目标第三方网站测试也得到同样结果,且加载完成后可正常从控制台查看iframe内容;@woxxom的思路很有趣,但我不确定如何提前在iframe内注册Observer;@David Bradshaw的经验让我备受鼓舞,期待测试其方案。
内容的提问来源于stack exchange,提问作者Ripeadori
相关产品推荐
相关产品推荐

