First, I know there are many questions about this phenomenon but none of the workarounds given so far would help in the situation I have here.
Environment: My code is in the Outer iFrame inside a page I don't control. It's sized to fit all my content so no scrolling happens inside my iFrame. Rather, the outer page can scroll it up and down to view its content.
Problem: I have an inner iFrame holding third party ads which I can't control. If the inner iFrame runs any of the following Javascript mands, the outer page scrolls everything down to bring the inner iFrame into view:
- blur and focus on an element inside it
window.location.href or document.location.hash to an anchor inside it.
scrollIntoView on an element inside it
Here is a demo a colleague put together (wait 5 seconds for it to start)
Solutions that don't work:
- If I controlled the outer page, I could listen to the onScroll event and prevent the page from scrolling if it wasn't from a mousewheel or key up/down event. However, due to security reasons, my outer iFrame can't access the main page or prevent the main page from scrolling it up or down.
- I could overwrite all those Javascript methods mentioned above so they didn't work - however, the ads in my inner iFrame actually create their own internal iFrames which have that functionality and causes the same issue.
- There are no events being propagated up that I can see which my iFrame could stop from reaching the main page. The browser appears to listen directly to the innermost iFrame.
First, I know there are many questions about this phenomenon but none of the workarounds given so far would help in the situation I have here.
Environment: My code is in the Outer iFrame inside a page I don't control. It's sized to fit all my content so no scrolling happens inside my iFrame. Rather, the outer page can scroll it up and down to view its content.
Problem: I have an inner iFrame holding third party ads which I can't control. If the inner iFrame runs any of the following Javascript mands, the outer page scrolls everything down to bring the inner iFrame into view:
- blur and focus on an element inside it
window.location.href or document.location.hash to an anchor inside it.
scrollIntoView on an element inside it
Here is a demo a colleague put together (wait 5 seconds for it to start)
Solutions that don't work:
- If I controlled the outer page, I could listen to the onScroll event and prevent the page from scrolling if it wasn't from a mousewheel or key up/down event. However, due to security reasons, my outer iFrame can't access the main page or prevent the main page from scrolling it up or down.
- I could overwrite all those Javascript methods mentioned above so they didn't work - however, the ads in my inner iFrame actually create their own internal iFrames which have that functionality and causes the same issue.
- There are no events being propagated up that I can see which my iFrame could stop from reaching the main page. The browser appears to listen directly to the innermost iFrame.
- Can you try sandboxing the child iframe? I realize this is probably not a solution since ads most likely need to run scripts, but perhaps worth experimenting? Perhaps add a sandbox attribute after iframe content loaded? – Yuriy Galanter Commented Jun 12, 2015 at 19:25
- Yeah I tried that and the only sandboxing setting that seems to work is totally disabling javascript which isn't an option since the ads need to run scripts – David Daudelin Commented Jun 12, 2015 at 21:29
2 Answers
Reset to default 4 +50Maybe there are some other ways to solve this issue, but one pretty straightforward solution is to take troublesome iframes out of the natural layout of the site by positioning them as fixed
.
Then the only remaining challenge may be to force them to behave as if they were part of the natural layout. But this tasks seems to be of mediocre plexity in parison to the original problem.
Moreover, in your particular case, the above approach works pretty out of the box. Since the outer iframe is not scrollable and its height matches its content height: you just need to insert -- for each iframe -- a placeholder that matches its counterpart size...
If you can drop IE10 and below support and the iframes
are same domain, you could use a bination of you solution number 2 (rewrite the methods) and mutation
events.
You'd need to first iterate through all iFrames
already in the DOM and remove unwanted functions, and maybe also clear unwanted intervals. And on each document
you add a mutation
observer
that will run the same scripts on added nodes
if they are iFrames
. So you'd have a recursive function going through all iframes at the beginning, and a function to remove methods, clear intervals and add observers on each iFrame
. And once an iFrame
is added, you run the same thing on it.Something like this:
The recursive function:
var to_cancel = ["scrollIntoView","blur","focus"];
window.onload = function(){iterate_window_object(window)};
function iterate_window_object(cur_win){
remove_methods(cur_win);
for(var i=0;i<cur_win.frames.length;i++){
remove_methods(cur_win.frames[i])
if(cur_win.frames[i].length>0){
iterate_window_object(cur_win.frames[i])
}
}
}
The cleaning function:
function remove_methods(cur_win){
for(var j=0;j<to_cancel.length;j++){
cur_win.Element.prototype[to_cancel[j]] = {}
}
for (var k = 0; k < 999;k++){
cur_win.clearInterval(k);
}
cur_win.setInterval = {};
cur_win.observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
for(var k=0;k<mutation.addedNodes.length;k++){
var added_node = mutation.addedNodes[k]
if(added_node.tagName == "IFRAME"){
remove_methods(added_node.contentWindow);
added_node.contentWindow.onload = function(){iterate_window_object(added_node.contentWindow)};
}
};
});
});
cur_win.config = { childList: true };
cur_win.observer.observe(cur_win.document.getElementsByTagName('body')[0], cur_win.config);
}
There's probably a way to make it look better, the hardest part is being able to modify the methods before they get called. And here it's a bit overkill because it iterates through new iFrames
so that if an iframe is added with already added iFrames
, they are also taken car of. It's probably not necessary.
So it could maybe be a solution, but mutation
events aren't supported by IE10 and below.