MutationObserver回调不是在Firefox中调用的,而是在Chrome中调用的



我有一个问题:

我查找的每一个资源都告诉我这应该有效,它在Chrome、Safari中也有效。。但在Firefox中没有。

系统:macOS Catalina 10.15.7,Chrome 90.0.4430.212,Firefox 88.0.1,Edge 90.0.818.66

我试图在Chrome上实现的:找到一个托管在同一域上的iFrame,并瞄准那里的一些子节点,更改属性,删除文本输入等。

我的JS代码的简化版本:

// set as null first because the iFrame takes some time to load
let iframeBody = null;
// this function is only run once, while it should be run on every MutationObserver change
function purge (iframe) {
var timer = null;
// only by querying that iFrame again Firefox picked up the purge function, I don't need that for Chrome 
let iF = document.querySelector('#chatbot iframe').contentWindow.document.body;
var inputField = iF.querySelector('div > div.frame-content > div.widget-position-right > div.chat > div.input-group');
if(!inputField || typeof inputField === 'undefined') {
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(purge, 50);
return;
}
// remove it
inputField.remove();
}
// this one is never called in FF
function cb (mutationList) {
let chatDiv = iframeBody.querySelector('div > div.frame-content > div.widget-position-right > div.chat')
if (chatDiv) {
mutationList.some(item => {
if (item.type === 'childList' && item.addedNodes && item.addedNodes[0] === chatDiv) {
purge();
return true;
}
})
}
let button = iframeBody.querySelector("div > div.frame-content > div.widget-position-right > #button")
// make button invisible if found, restore if chat is closed
if (button) {
if (button.classList.contains('chat-open')) {
button.style.opacity = 0;
button.style.pointerEvents = "none";
button.style.width = 0;
}
else {
button.style.opacity = 1;
button.style.pointerEvents = "auto";
button.style.width = "auto";
}
}
}
function bind () {
try {
iframeBody = document.querySelector('#chatbot iframe').contentWindow.document.body;

if (!iframeBody) {
return setTimeout(bind, 500) // wait and retry because iFrame is not loaded immediately
}
if(iframeBody){
console.log("iframeBody found");
}
const mutationObservable = new MutationObserver(cb)
// actually don't need characterData, just an attempt to pick up the observed event
const config = { characterData: true, attributes: true, childList: true, subtree: true};
setTimeout(() => {
// this is the initial call that works in FF (and Chrome/Safari naturally)
purge();
mutationObservable.observe(iframeBody, config);
}, 500);

} catch (err) {
// console.log(err);
// try to bind again, can take some time to load
setTimeout(bind, 100)
}
}
// start here
bind();
})();

我验证了我可以在Developer控制台中使用querySelector访问HTML节点,并在所有浏览器中对其进行良好的编辑,只是MutationObserver回调没有被选中。

我试着把它写成这个

new MutationObserver((mutation) => {
// cb function goes here
})

但无济于事。我已经坐了几个小时了,只是为了让它发挥作用,我很想为Firefox停用它。。

如有任何提示,我们将不胜感激。如果需要任何额外的资源,请告诉我。

更新:尝试使用new frameElem.contentWindow.MutationObserver的评论建议,调用MutationObserver,没有任何更改:

const iframeElmWindow = document.querySelector('#chatbot iframe').contentWindow;
const mutationObservable = new iframeElmWindow.MutationObserver(cb);

问题当然是您在iframeBody变量中捕获了初始about:blank的Document的<body>

下面是一个外包的1最小示例,它表明您的代码可能会捕获初始文档,而不是稍后加载的文档

// removed a lot of unrelated stuff from OP,
// to keep only the core of the issue
function bind() {
const iframeBody = frame.contentWindow.document.body;
if( !iframeBody ) {
return setTimeout( bind, 500 );
}
if( iframeBody ) {
console.log( "iframeBody found" );
console.log( "location:", iframeBody.ownerDocument.location.href );
// logs "about:blank"
}
bind();

因此,您观察到的<body>临时初始about:blank文档中的一个。由于该文档将在几毫秒内被加载的文档替换,因此不会发生新的突变。

最简单的解决方法是使用<iframe>load事件,该事件只有在实际加载了加载的文档时才会触发
通过这种方式,您可以确保不会捕获初始的about:blank文档,并且不再需要setTimeout( bind )破解:如果在此事件中无法访问iframe的contentDocument,那是因为两个上下文的来源不相同,重试不会修复它。

总之,要启动一个针对<iframe>身体的突变观察者,你需要做的是:

frame.onload = function( evt ) {
const iframeBody = frame.contentDocument?.body;
if( !iframeBody ) {
// this means we are in a cross-origin document...
return;
}
const observer = new MutationObserver( cb );
observer.observe( iframeBody, { childList: true, subtree: true } );
};

再次作为外包示例1

1.我们需要外包这些例子,因为StackSnippet的空源iframe不允许我们的脚本访问iframe的内容,无论它是如何提供的

最新更新