scrollIntoView() ancestor frame scrolling in Firefox but not Chrome - javascript

It seems Chrome and Firefox have two different interpretations of scrollIntoView() when it comes to iframes. In Chrome, the scrolling only applies to elements until a position:fixed ancestor element is found, whether it is in the iframe or parent frame. But in Firefox, it is not just any ancestor that has position:fixed; there seems to need to be an unscrollable ancestor element in the parent frame also, otherwise the parent frame will still scroll.
I want to prevent Firefox from scrolling the parent frame in this case. So far the only solution I have found is making an element in the parent frame be position:fixed, but as you can imagine that will ruin the parent frame's layout if you don't want a fixed element and want the page to flow (eg, if the iframe is embedded within a long blog post).
Note that I do NOT have control over the iframe (it is cross domain also), otherwise I could just switch scrollIntoView() with scrollTo().
Here is a concrete example you can test:
<html>
<head>
<title>Parent frame</title>
<style>
html { scroll-behavior:smooth; }
</style>
</head>
<body>
<iframe src="iframe.html" style="height:200px;"></iframe>
<div style="height:10000px;">parent</div>
</body>
</html>
<html>
<head>
<title>Child frame</title>
<style>
</style>
<script>
setTimeout(function() {
var bottom = document.getElementById("bottom");
bottom.scrollIntoView();
}, 2000);
</script>
</head>
<body>
<div id="someParent" style="height:200px;position:fixed;overflow:scroll;">
<div style="height:5000px;">iframe</div>
<div id="bottom" style="background-color:red;">bottom</div>
</div>
</body>
</html>

Related

Chrome 75 - setting iFrame src attribute causes iFrame parent to load the iFrame content

Chrome v75 appears to have introduced a bug whereby if you replace an iFrame's src programatically, it will replace the entire page instead of the iFrame.
This didn't happen on v74 and I can't get a test case to work (yet), it just fails in our site. (The site hasn't changed since going from v74 to v75, only Chrome has changed)
It appears to work fine the first time but then when you change it again (in our case viewing report drill downs) it causes the entire page (i.e. the iFrame's Parent) to load the src you were trying to load into the iFrame.
It also doesn't matter if you use pure Javascript or (in our case) JQuery, both cause the same issue.
EDIT: After a few hours detective work, I've found the bug. Setting the tag in the iFrame's content causes Chrome to load the iFrame's content into it's parent rather than the iFrame itself.
I've setup a Plunker account with a demo: https://plnkr.co/edit/UQ0gBY?plnkr=legacy&p=info
Just so I can post the link to Plunker, here is the code for the main file & the iframe content
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function onLoaded() {
// find element
let button = document.getElementById("button");
button.addEventListener("click",function(e){
// Add a random number on the end as a cache buster
document.getElementById('frame-finance-custom').src = 'test2.html?rnd=' + Math.random();
},false);
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
</head>
<body>
<div>IFrame Src Changing Test</div>
<div>
<div id="div-frame-finance-custom" style="float:left;width:33%">
<iframe id="frame-finance-custom" name="frame-finance-custom" class="iframe"
style="border:1px solid black; width: 100%; height: 350px; overflow-y: scroll; vertical-align: top;">
no data
</iframe>
</div>
<div style="float:left;margin-left:1em;">
Detail: Loading an iframe page with a <Base> tag in it with target set to "_parent" will cause any refresh of that frame to replace the parent document<BR>
<BR>Instruction: <UL><LI>Click the 'Update Frame' Button, this will load test2.html into the frame. <LI>Click it again & it will replace the iframe's parent with the content of the iFrame.</UL>
<BR>Confirmation: Remove the <Base> tag from the header of test2.html & reload, it will work as expected.
</div>
</div>
<br clear=both>
<div>
<button id="button">
Update Frame
</button>
</div>
</body>
</html>
IFrame Content (test2.html):
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_parent"/>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>This is the frame content</div>
</body>
</html>
Note, using their new layout it doesn't work, but using their legacy layout it does. Feel free to save the files locally and use chrome directly too.
Ok, so this turned out to be a bug in Chrome rather than anything else, so yes, strictly not a SO question, but seeing as SO ranks so well in Google (other search engines are available), I thought it better to leave it here as a solution rather than simply delete it, just incase anyone else has a similar problem.
The reason is outlined as an edit in my question, the solution is to remove the <base target="_parent"> tag from the iFrame and programatically add the 'target="_parent"' attribute to any links in the iFrame.
We do this via jQuery, I'm sure its just as easy via vanilla Javascript.
$('a').attr('target','_parent');
Add that to the javascript that runs when a page has loaded and it'll replace add target="_parent" to any links on the page.
e.g.
<script>
function onLoaded() {
// find all links and add the target attribute
$('a').attr('target','_parent');
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
As #Kaiido says in his comment, its apparently fixed in Chrome v77, but this isn't the current (as of June 2019) stable release, so we've had to add the workaround into production so that our CRM works with Chrome v75. Thanks to #Kaiido for confirming that.

stop parent page from scrolling down to iframe

I have embedded a website using iframe.
Whenever the parent page loads, the embedded page makes the parent page scroll down to the iframe. I cannot change any code in the embedded page, only the parent page.
Here's the [fiddle of the issue][1]:
HTML:
<iframe src="http://store.ecwid.com/#!/~/cart" width="100%" height="100%" id="Container"></iframe>
CSS:
body { margin-top: 100px; height: 1000px; }
How can I prevent the parent page from scrolling down to the iframe?
IMPORTANT UPDATE: ALMOST THERE
So we've added the following javascript to force the page to scroll bacl to the top:
window.addEventListener("scroll", runOnScroll);
function runOnScroll(){
window.scrollTo(0, 0);
window.removeEventListener("scroll", runOnScroll);
}
It does work as you can see [in this fiddle][2]. However, on the iPad and iPhone, you can clearly see the page scolling back then up again. On the PC, you can't see the transition.
Please visit [this website][3] so you can check both transitions (pc and mobile).
I'd like to know if there is anything we can add to the code so:
the transition in mobile is not noticed like in the pc (preferred choice)
OR
the transition is smoother (slower scrolling or something like that)
Ok, I added a bit of JavaScript that listens to the first time the document is scrolled down. When the document is scrolled down for the first time, it'll force itself back to the top, then it'll remove the listener so that the user may scroll as desired afterward.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css"></link>
<script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
</head>
<body>
<iframe src="http://store4549118.ecwid.com/#!/~/cart" width="100%" height="100%" id="Container"></iframe>
<script src="scripts.js"></script>
</body>
</html>
JAVASCRIPT (In a file named scripts.js)
window.addEventListener("scroll", runOnScroll);
function runOnScroll(){
$('html,body').animate({scrollTop: 0},1000);
window.removeEventListener("scroll", runOnScroll);
}
Give it a shot, and let me know if it works!

How can I prevent JavaScript in an iFrame to access properties of the outer site, even if the iFrame's content comes from the same origin?

Basically I want to have an iFrame which always restricts it's content as if it comes from a different domain, even if the content comes from the same origin.
Is there any way to do this?
The best solution is probably to use the HTML5 sandbox attribute on the iframe, which (by default) explicitly disables both scripting and same-origin access to the parent DOM.
Good introduction at http://msdn.microsoft.com/en-us/hh563496.aspx
As of Dec 2012, this seems to be supported on most current browsers.
This will hide window.parent in the child frame/window, but not the top property.
BUT the window.parent property is STILL accessible till the end of the onload event of the child window/frame.
<html>
<head>
<style type="text/css">
#wrapper {width:1000px;height:600px;}
</style>
<script type="text/javascript">
window.onload = function() {
var frm = document.getElementById('childFrame');
var win = frm.contentWindow || (frm.contentDocument && frm.contentDocument.parentWindow) || (frm.document && frm.document.parentWindow);
if (win) win.parent = null;
}
</script>
</head>
<body>
<div id="wrapper">
<iframe id="childFrame" src="child.html" frameborder="0" style="width:100%;height:100%;"></iframe>
</div>
</body>
</html>

JavaScript: find dimensions of iframe body

I have a web page which embeds another page in an iframe. I want a JavaScript function in the outer document to fetch the size, in pixels, of the document in the iframe.
I have been exploring the DOM and the only attributes I can find relating to width and height give the height of the iframe itself, which is smaller than the document inside it.
e.g.
<iframe src="tall_page.php" id="my_iframe" style="height: 101px"></iframe>
...
var i = document .getElementById ("my_iframe");
i = i .contentDocument .body;
alert (i .offsetHeight); // or scrollHeight or clientHeight
The above will give "101". The value I am after is the size of the whole content document, not the frame -- and not just the <body>: if the CSS gives html{padding:x} for example, I want this to be inorporated.
How do I get this value?
Using jQuery this should work:
var height = $("my_iframe").height();
var width = $("my_iframe").width();
Hope that helps
I got it to work using the contentWindow object.
I think the idea here is to get the width/height of an
element that you set inside the iframe. In my example,
I set the iframe body to 3000px using css, and got it to work.
Keep in mind that since you're accessing an iframe's data,
you need to adhere to the same origin policy
Here is my example:
iframe.html
<!DOCTYPE html>
<html lang='en'>
<head>
<title>My Iframe</title>
</head>
<body id='mybody' style='width:3000px;'>
Hello!
</body>
</html>
index.html
<!DOCTYPE html>
<html lang='en'>
<head>
<title>My Test</title>
<script>
window.onload = function() {
var f = document.getElementById('myiframe');
console.dir(f.contentWindow.document.getElementById('mybody').clientWidth);
}
</script>
</head>
<body>
<iframe id='myiframe' src='iframe.html' height='480' width='640'>
</iframe>
</body>
</html>
Hope that helps.

jQuery $("*").fadeTo() moves content before fading

Why does all content get jerked downwards before fading in the following, and how can i fix it?
Using FireFox 3.6.3, thanks in advance.
<html>
<head>
<script type="text/javascript" language="javascript" src="http://localhost/javascript/jquery-1.4.2.js"></script>
<script type="text/javascript" language="javascript">
$(document).ready(function(){
$("#button").click(function(){
$("*").fadeTo("slow",0.0);
});
});
</script>
</head>
<body>
<p>Just a normal paragraph doing my job!</p>
<p>Me too!</p>
<input type="button" id="button">
</body>
</html>
It has something to do with trying to fade all elements, including those outside the <body>. Try:
$("body > *").fadeTo(..)
But why would you want to fade every single element, when you can simply do a fade on the body itself.
$("body").fadeTo(..)
Edit: Some more research shows that when trying to fade the <style> and <head> elements, in no particular order, causes everything to move down. Don't know why yet, but you can see an example here - http://jsfiddle.net/UKn8r/2/
Edit 2: Ok, I think I may have a reason here. The <head> and its children elements such as <style>, <script>, etc. are by default set to display: none in the user agent's stylesheet. When fading them out, jQuery ends up setting their display property to display: block. Now the contents of these child elements are not meant to be displayed on the screen, but by setting them to display: block, it gets displayed as a horizontal block about 20px high with no content, which shifts everything else downwards. Note that if you were to empty out the <script> element and make the onclick inline, then you wouldn't see the jump on Firefox since the element will be empty and not consume any space on screen even when displayed as a block. So changing it to:
<html>
<head>
<script src="http://localhost/javascript/jquery-1.4.2.js"></script>
</head>
<body>
<p>Just a normal paragraph doing my job!</p>
<p>Me too!</p>
<input type="button" id="button" onclick='$("*").fadeTo("slow",0.0);'>
</body>
</html>
will not cause any jumps.
Also, your original code verbatim, will work properly on Webkit browsers (Chrome, Safari) as the display style property for <script> elements does not get overridden as block. For these browsers, however, if you were to have a style element with some content inside it, then you would see the same behavior as <style> will have an inline style attribute having display: block. Now it may seem utterly useless to have something like, <style style="display: block; opacity: 0">..</style>, but this is just an explanation for why you're seeing the behavior that you're seeing. So to reproduce the same problem on these browsers, try this code:
<html>
<head>
<script src="http://localhost/javascript/jquery-1.4.2.js"></script>
<style>p {}</style>
</head>
<body>
<p>Just a normal paragraph doing my job!</p>
<p>Me too!</p>
<input type="button" id="button" onclick='$("*").fadeTo("slow",0.0);'>
</body>
</html>
The <style> property must have some content, and not pure whitespace, so I put the junk p {} there.
This concludes my wasteful search for something that shouldn't be done in the first place :)
Try to fade out your main container, or all elements at body level. For example:
$('body > *').fadeTo('slow', 0.3)
Fading out * doesn't look like a good idea. When you have nested elements (and you probably do), they will both be fade out, having odd effects and exceptionally poor performances.

Categories

Resources