2 window.parent.onscroll calls on page do not work - javascript

I have an HTML page with 2 iframes within it. I want each iframe to display the Y scroll offset of the parent window. One iframe works on page load, the other does not.
The parent HTML page just has the iframe embeds. Here is the code running in the iframe:
function run(){
document.write("<div id='scrollY' style='float:left;'></div><br>");
scrollY = 'no';
document.getElementById("scrollY").innerHTML = scrollY;
window.parent.onscroll = function(){
scrollY = window.parent.pageYOffset;
document.getElementById("scrollY").innerHTML = scrollY;
}
}
run();
The result on load and a scroll downwards will display something like:
iframe1 = 100
iframe2 = no
Can I only call window.parent.onscroll once? This doesn't seem right to me.

This is happening because in both frames, window.parent makes reference to the same window object, and you are setting the onscroll property/event on the same object, twice. This means that the second time around you set the window.parent.onscroll property, you're actually overwriting the first attempt to set the event, and you're overwriting it with the second frame's window.document object, which is why it only updates the contents of the elements in the second frame.
To overcome this issue, you can simply just use addEventListener instead of onscroll:
http://jsfiddle.net/x046o0p7/
function run() {
document.write("<div id='scrollY' style='float:left;'></div><br>");
scrollY = 'no';
document.getElementById("scrollY").innerHTML = scrollY;
window.parent.addEventListener('scroll', function() {
scrollY = window.parent.pageYOffset;
document.getElementById("scrollY").innerHTML = scrollY;
});
}
addEventListener allows you to set multiple events of the same type for the same element, while the .onstuff listeners don't.

Related

Proper way to get dimensions of a nested iframe

I have a javascript in the head tag of a domain that is not mine that is looking for DIVs on the page to measure the height and width. The names of the DIVs are in an array. Each div has an undefined size. The size of the div will grow based on an iframe that loads from a different domain. The div may not always be the same size as the iframe so the div is not a reliable element to pull the size of. Therefore, I must measure the iframe. However, my problem is that my code loads in the head and sometimes loads before the div's size is defined which means the iframe isn't ready yet. I then do a setTimeout to check if the div's height and width is greater than 1. However, I notice that after the div and iframe content visually appears on the screen, there is a delay of a few seconds before the console logs the size of the iframe. I need the dimensions as soon as the iframe content loads. How can I make this code more efficient?
Head Tag of Page
var oDv = ["div-0", "div-1", "div-2"];
<script src="mydomain.js"></script>
Body of Page
<div id="div-0">
<script src="gets-an-iframe-from-some-other-domain.js"></script>
mydomain.js (aka my script)
window.addEventListener('load', pageFullyLoaded, false);
function pageFullyLoaded(e) {
var index;
for (index = 0; index < oDv.length; index++){
measure(oDv[index]);
}
}
function measure(div){
var divElement = document.getElementById(div);
if(divElement === null){
return;
}
var iframeElement = document.getElementById(div).getElementsByTagName('iframe')[0];
var iframeDimensions = window.getComputedStyle(iframeElement, null);
var iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
var iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
if((iframeHeight || iframeWidth) == 1){
timer();
}
else{
console.log(iframeHeight+" "+iframeWidth);
}
}
function timer(){
var T = setInterval(function(){
if((iframeHeight || iframeWidth) == 1){
iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
}
else{
clearInterval(T);
}
}, 100);
UPDATE 1
I think I need to work around the window.addEventListener('load', pageFullyLoaded, false); because sometimes the first DIV loads while the rest of the page content is still loading. And sometimes the iframe loads before the page content is done. Therefore the top DIV/iframe are ready to be measured but my code hasn't started because it is still waiting for the the entire page to load. Also, I'd like to get away from using the setTimeout if possible.
UPDATE 2
I tried the following code but I get the following error: "Uncaught TypeError: Cannot read property 'getElementsByTagName' of null". I believe this is because when I check for this the DIV exists but the iFrame does not yet. I don't want to try adding an event listener to the window for the page to have loaded because I need the size as soon as possible.
function iframeReady(div){
console.log(div+' Start Function');
var iframe = document.getElementById(div).getElementsByTagName('iframe')[0];
iframe.onload = function() {
console.log('iFrame loaded');
var iframeDimensions = window.getComputedStyle(iframe, null);
var iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
var iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
console.log('iframeLoaded '+div+' iframe dimensions: '+iframeWidth+'x'+iframeHeight);
}
}
iframeReady('a-div-id');
In the above I see the following in the console:
a-div-id Start Function
Uncaught TypeError: Cannot read property 'getElementsByTagName' of null
I guess I'll just need to put the above into a settimeout loop to keep checking for the existence of the iframe first?
Try to use the iframe.onload event
var iframe = document.getElementByTagName("iframe")[0];
iframe.onload = function() {
console.log(iframe.getBoundingClientRect());
}

Open new window, and scroll to element

I need to open a window after the dom is loaded, and scroll the y position to the element, preferably centered on the screen. I looked on stack overflow and I put something together with the bits i found.
The problem with this is that it's using a timer to see my element is available, and it is really slow - altering the interval time does not effect the speed at all. Also, it does not work in IE and i need it to. thought on this?
linkToComment: function(e){
var link = 'https://****';
var commentId = 21
var w = window.open(link);
var interval = setInterval(function(){
if ( $(w.document.body).find("div[data-commentoriginalid=" + commentId + "]")) {
$(w.document.body).animate({
scrollTop: ( $(w.document.body).find("div[data-commentoriginalid=" + commentId + "]").offset().top)
},500);
clearInterval(interval);
}
}, 500)
},
Try this instead of the interval:
$(w.document).ready(function () {
$(w.document.body).animate({
scrollTop: ( $(w.document.body).find("div[data-commentoriginalid=" + commentId + "]").offset().top)
},500);
});
but I don't understand why would you make this scroll from the parent window, instead of adding the javascript to the opened window...
Do you have access to the code/source in the child window? If I was doing this, I would add an onload handler in the child window, rather than trying to control it from the parent. If you need to pass in a variable to the child from the parent, you could either use a querystring or a hash parameter.
e.g. mysite.com#2345
And then pick that id up in the child window, select the element with that ID and scroll to it.
Remember - the parent window could be closed by the user while the child window is opening meaning the timer would be killed.

Change iframe height after content update

I have some tabs on my page, and inside one tab of that page I have an iframe (#quiz_iframe).
This iframe is quiz frame, which is contained in a div (.quiz-div). After hitting a button (.start-quiz), quiz is loaded, and new div (.quiz-content) appears inside (.quiz-div).
In quiz content I have 20 another divs (.quiz-question), which are paginated, so after clicking a button next, prev, you can hide or show 5 next/prev divs.
Well the point is, I'd like to adjust iframe height to that content and I don't know how, after clicking .start-quiz.
Now I have a code which works fine, when the iframe is loaded for the first time (it adjust height to the laoded content)
function iframeLoaded() {
var iFrameID = document.getElementById('quiz_iframe');
if(iFrameID) {
iFrameID.height = "";
iFrameID.height = iFrameID.contentWindow.document.body.scrollHeight + "px";
}
}
<iframe onload="iframeLoaded()" id="quiz_iframe" src="'.$link.'" style="width:100%;"></iframe>'
How I can modify that code, to update iframe height on every .quiz-div height change? And yes, iframe is in the same domain :)
Thanks!
You can use a solution based on postMessage
You'll have to include some JavaScript in the iframe and on the parent page as well.
In your iframe :
window.addEventListener("load", function() {
parent.postMessage(getDocumentHeight(), "*");
});
In your parent page :
function receiveIframeHeight(frameWindow, height) {
$("iframe").each(function () {
if (this.contentWindow === frameWindow) this.height = height;
});
};
window.addEventListener("message", function (e) {
receiveIframeHeight(e.source, e.data);
});
You can now listen to the resize event and postMessage from your iframe to the parent page. But beware of infinite loop.
To see the whole code you should take a look at this Github repo iframe-autoheight-using-postmessage
In jQuery, you could use
$("myContainer .quiz_div").on("resize", function () {
$("#quiz_idrame").attr("height", your height calc goes here);
});
The "on" is a delegated event binding and thus if you add/remove items with the class of .quiz_div to the "myContainer" i.e. whatever contains the divs, it should bind.

Resize iframe on content refresh or update

I have an iframe that I would like to resize to match its contents whenever the contents auto refreshes (every few minutes) or when the user interacts with it. Although the contents are in the same domain, it would be difficult for me to modify the contents. Ideally, the iframe would just self adjust to the size of its contents.
Current code that resizes only once:
<iframe id="ganglia-frame" src="ganglia.url" width="100%" height="500%">
blah not supported blah
</iframe>
<script language="Javascript">
function setIframeHeight(iframe) {
if (iframe) {
var iframeWin = iframe.contentWindow ||
iframe.contentDocument.parentWindow;
if (iframeWin.document.body) {
iframe.height =
iframeWin.document.documentElement.scrollHeight ||
iframeWin.document.body.scrollHeight;
}
}
}
$(window).load(function () {
setIframeHeight(document.getElementById('ganglia-frame'));
});
</script>
Related question: Adjust width height of iframe to fit with content in it
What you need to do is to set a timer and check the iFrame size after a few moments. You may have to check several times as not all browsers return the correct size immediately.
This method works only on same-domain iFrames.
Bind a function to the iFrame onload event and when it executes check the height of the iFrame. If no height is found schedule another check in a few moments.
var iFrameSizeCount = 0;
var onloadFunction = function(event){
var contentHeight = document.getElementById('iFrameId').contentWindow.document.body.offsetHeight;
if(contentHeight == 0){
// Schedule a recheck in a few moments
iFrameSizeCount++; // we keep a count of how many times we loop just in case
if(iFrameSizeCount < 10){ // after a while we have to stop checking and call it a fail
setTimeout(function(){ onloadFunction(event); }, 200);
return false;
}
else {
contentHeight = 100; // eventually if the check fails, default to a fixed height. You could possibly turn scrolling to auto/yes here to give the iFrame scrollbars.
}
}
contentHeight += 30; // add some extra padding (some browsers give a height that's slightly too short)
document.getElementById('iFrameId').style.height = contentHeight + 'px';
}
Then bind the event to the onload event of your iFrame (however you want to):
The "scrolling=auto" is useful, just in case the sizing fails at least they have scrollbars. The onload event fires if the iFrame reloads, so is useful if they've clicked a link inside it.
I have had good luck with this jQuery plugin - https://github.com/davidjbradshaw/iframe-resizer

Determine the mouse position inside an iframe, but inclusive of the parent

I have an iframe inside which i detect a right click and pass the mouse event to a function in the parent. Here, (inside the parent function), i have the logic to display a custom context menu and the context menu html markup is inserted into the parent DOM. Thus is need the mouse position according to the viewport (or the parent DOM), but what i received is relative to the iframe.
I tried using offsetTop and offsetParent, but this iterated only till the body tag of the innerpage.
This is the function I used:
// Define class that will hold Object coordinates
function CTopLeft(i_nTop, i_nLeft) {
this.nTop = i_nTop;
this.nLeft = i_nLeft;
}
function GetTopLeftFromIframe(i_oElem) {
var cTL = new CTopLeft(0, 0);
var oElem = i_oElem;
var oWindow = window;
do {
cTL.nLeft += oElem.offsetLeft;
cTL.nTop += oElem.offsetTop;
oElem = oElem.offsetParent;
if (oElem == null) { // If we reach top of the ancestor hierarchy
oElem = oWindow.frameElement; // Jump to IFRAME Element hosting the document
oWindow = oWindow.parent; // and switching current window to 1 level up
}
} while (oElem)
return cTL;
}
This is from http://codecorner.galanter.net/2012/02/26/absolute-coordinates-of-element-inside-of-iframe/
You can get the top and left by calling the iframe from the parent document. Say your iFrame has the id 'my_iframe' in the parent document:
offset_left = parent.document.getElementById('my_iframe').offsetLeft;
offset_top = parent.document.getElementById('my_iframe').offsetTop;
BUT this will only work if the content in the iFrame belongs to the same origin (same host, port and protocol)!
Edit: also found this similar question on bytes.com: iframe relative mouse position
The Yuriy solution works well until the element is a child of an element with position:absolute and that is scrolled. (I was looking for the element position rather than mouse, but I'm sure you could modify this to work for that too)
My fix :
function iframe_offset(e){
var x=e.getBoundingClientRect().x
,y=e.getBoundingClientRect().y
,w=e.ownerDocument.defaultView
do{
e = e.offsetParent
if(e == null){
e = w.frameElement
w = w.parent
if(e){
x += e.offsetLeft+e.scrollLeft
y += e.offsetTop+e.scrollTop
}
}
}while(e)
return {x:x,y:y}
}
At least now (probably before also), you have a lot of coordinates on the event object (layerX, screenX, etc.) that mean that you might be better to just use one of those sets if you can, to avoid this sort of extra calculation - it simplified things in my use case quite considerably to use screenX/screenY as a base rather than try and calculate, and did the trick.

Categories