The problem context
I need to resolve the height of the content of an iframe after loading it (in order to adapt the height of the iframe element itself). The problem is that the iframe could be in a hidden state (one of its containers/parents set to display:none), when the loading is done.
I can't find a way to get the correct height of the iframe content as long as I don't display it. Using jQuery.height() returns 0 on Firefox.
An example demo here:
https://codepen.io/anon/pen/gKBQeP?editors=1111
(you'll notice how the height is reported differently in case you immediately click on the Tab3, where the iframe is, making that visible, or if you wait a couple of seconds after loading and then click on the Tab3)
Cannot write height on the element, right after displaying it.
Moreover, after making it visible again I still cannot get the real height of the content; it still returns 0 like it is hidden. I assume because the iframe-content is still in the process of getting rendered, even if the DOM tree of the iframe has been shown already.
If I setTimeout few milliseconds after making it visible then I can get the correct height (that doesn't make much sense to me....🤔).
I really don't like to set a timeout in order to read the content height.
What is a reliable, cross-browser, way to get the height of a hidden element, even when this is hidden (or in the process of becoming visible)?
My solution
At the moment I:
trigger the read/write of the height right after I know the element is visible again.
use setTimeout() to wait half-second (feels sluggish 😒) before reading/writing the height of the element.
Note (the actual question)
I am trying to find less hacky as possible solutions; so I want to avoid:
displaying (or cloning) the element quickly (taking care saving+restoring css properties, making them persistent and inline; or taking care of avoiding flickering in the page), to read the dimensions and quickly set it back to hidden (😖).
using setTimeout to wait the element dimensions being restored (and readable/writeable correctly) in order to work on them immediately after showing the element itself.
It's a bit hacky but rather than display:none (I assume that's how it's being hidden) you could set something like:
top: -10000px;
left: -10000px;
position: absolute;
It's "hidden" since it won't be visible, but you will still be able to get its height. Then after you get the height you can remove these styles to make it visible.
Related
I'm trying to replicate jQuery slideDown() in GSAP and I'm having trouble working out how jQuery calculates the height of an item which is currently hidden as if it was set to height:auto.
I've tried trawling the code on GitHub but can't find any code which seems to be doing this in jQuery.fn.slideDown or jQuery.fn.animate which it calls.
There are several similar questions on SO and several solutions proposed, all of which seem to have their own problems:
Clone the element, position it off screen and calculate its height. This won't work if the element or any of its child elements have a height set by CSS styles which require the element to be in its original place in the DOM (e.g. an .accordianItem might only be styled if it's inside its .accordian).
Display the item, remove height:0 and quickly calculate the height before hiding the element again and then stating the animation. This might flash the content quickly while calculating the height.
Use visibility:true to show it in place while calculating the height. This would stop the flash and still keep the element in the same position in the DOM for correct height calculation, but it would still push other items below it down because visibility:false items still have a height.
Calculate the height of an item before it's hidden and store it in a data attribute so we know it when we want to open the item later. This won't work if any dynamic content changes the height of the item whilst it's hidden.
jQuery slideDown() "just works" every time so I'd be really interested to know how it works, but I just can't work out where it's doing this. I'm also surprised that GSAP can't do this out of the box, or that nobody has shared a proper solution to this before.
Any help would really be appreciated.
It turns out that if you use $.height() to get the height of an element with display:none it doesn't return 0 as you would expect, it actually sets visibility:hidden, position:absolute etc. and sets display to block to give you the correct height back. I assume this is what's being used internally when doing a slidedown.
This answer helped me a lot.
jQuery: height()/width() and "display:none"
Just to be clear about how this seems to avoid all the problems in my original question. It's basically doing number (3) but avoiding the problem of pushing lower content down the page because it's also set to position:absolute while the height is being calculated. A very simple elegant solution
I am using parallax.js on my web page.
http://www.edizarca.com/karpaz/ (page has been now updated, parallax is remove until a feaseable solution found)
But open the page and keep refreshing. Sometimes the background image starts eerie(off position). Ive tested both in chrome and firefox and I can't figure out why its doing this.
Do you have any idea?
Thanks
The following is what I've discovered by inspection.
parallax.js stores the initial top offset of the element to which it is applied. This value is stored in a variable called firstTop. The background-position will be placed based on the value of firstTop and the current scroll position, in such way that background-position will only be able to reach 0 if firstTop is 0 [for example if firstTop is 96 background-position will not be less of 67].
Note: more about 96 later.
That's right, firstTop is not always 0, form time to time I manage to get to a non-zero value. I suspect it has something to do with browser cache.
So, from where does the value firstTop come from? Here is the code:
//get the starting position of each element to have parallax applied to it
$this.each(function(){
firstTop = $this.offset().top;
});
Where offset is a function defined in JQuery. In the particular case of querying an element it will fall back to getBoundingClientRect. So, if the problem where there, it would be a bug of the browser...
So, to look in another direction... the value I've got is 96 and it is just [sometimes] after a cache clean. At the break point there is nothing rendered yet except for a gray background an a scroll bar... but if I have not cleared the cache at the break point the layout of the page is visible.
About 96, it is the height of the div with id "header". By the moment I hit the break point the height differs, depending of if I have cleaned the cache. If I have cleared cache it is [sometimes] 96, if have not it is 150 (but now the div with id "wrapper" has position: relative and it's top offset is 0).
So.... how do we explain this behaviour?
The file style.css of course. If the cache is invalidated the browser has to download it again, meanwhile it will go to execute javascript. If it reads the position of the div with id "wrapped" before the css is applied then the offset is wrong. Of course when the style is loaded from cache it is applied right away leaving no chance for javascript to misbehave.
Note: I also got a firstTop of 233 and a height of the div with id "header" of 195.875.
The solution? I don't know. I suspect parallax.js could allow some way to set a fixed initial offset. (there is no way to set the initial offset as the code stands).
You could look for a library that allows to check for the styles. (Note: reading the styles with javascript doesn't work, because they may be loaded but not applied).
If I were you, I would modify parallax.js (or try another version). There is no need to modify parallax.js.
EDIT 1:
You should try to:
Run your code with the defer attribute.
try to use setTimeout
the idea is to make the browser execute the initialization of parallax "a bit later" so that it will apply css but not "too late" that the user will start interacting with the page.
EDIT 2:
Place the styles before any javascript (or put javascript at the end of the document).
Use $(document).load rather than $(document).ready to run parallax.js
I have to place the content of service provider in an iframe on parent website.
The height of the iframe content would dynamically change depending on user interaction.
Problem I face is that there is some extra height added to the iframe. I'm not sure where the height is coming from.
Any insight appreciated.
LINK TO PAGE
I don't believe this to be the fault of easyXDM.
It appears that the height being calculated is for the current width of the iframe. If you remove line 28 in your HTML file, you will see that the height is completely filled. (Or the following line) (Or you can leave the code as is, and disable the height style in your developer tools and see the result)
this.container.getElementsByTagName("iframe")[0].style.width = "500px";
Since it's hard to modify the code in the debugger, the next thing I would try is, setting the width to what you would like it to be prior to filling with content and a calculated height.
I had the same problem
When you do assign the height to the iframe element, don't assign it to all iframe. Because it affects iframes in the Ads and social media plugins.
So I did the following
$('#divID iframe').height(easyXDMmessage);
$('#divID iframe').width('100%');
In the case of empty iframes, I ran into an issue where if you set the height to 0px, the parent block element will still show at least one line of empty text. This is because iframes are actually inline elements, so their parent block will still show one line-height of text even if the iframe itself is zero height. Here's the simple fix:
iframe#my_iframe { display:block; }
Will the element be visible even for a blink of an eye If it is added to DOM and instantly removed?
var feed = $('<div class="entry"></div>').text(data.status).appendTo(app.twitter_feed);
console.log(feed.height());
feed.remove();
I've tried the above code on a few browsers and couldn't see the element. But is this behaviour consistent through all platforms/browsers?
After reading your previous question as well, it seems that you very badly want to calculate the display height of an element before actually displaying it. I 'm not entirely clear why you want to do this (it gives off a bad smell), but here's how to anyway.
Put a <div> in your page with height: 0, overflow: hidden, and the desired width of your element¹. Add the <div> we 're talking about inside that outer helper div (it will not show no matter what), and get its height after the browser performs layout. After that you can proceed however you want (e.g. by moving the inner <div> to another position in the DOM tree).
¹ it would be best to put it exactly where you want the .entry to end up (i.e. the .entry and the helper div will end up being siblings).
PS: It's always better for everyone if you mention your real purpose.
I can imagine a situation wherein for the browser to be able to compute the element's effective height, it will have to render it on the window, or at least have the element's box reflow against the current site layout. It might not be visible (as, yeah, it's instantly removed), but a situation like this will reflow the page, and the movement of the affected elements on the page can be seen.
Images come to mind, for example, because browsers generally have no idea what the dimensions of images are until they try to lay them out (correct me if I'm wrong there though).
So, no, I wouldn't say that this is consistent behavior.
Implement it like this. Make a clone of app.twitter_feed, and send it to hell (Coordinates: x:-30000, y:-30000) and try whatever you like there.
var cloneTWFeed = $(app.twitter_feed).clone();
cloneTWFeed.css("position", "absolute").css("top","-30000").css("left","-30000");
var feed = $('<div class="entry"></div>').text(data.status).appendTo(cloneTWFeed);
console.log(feed.height());
feed.remove();
You current code works just fine, but you never get to see the element, since just after you append it, you remove it.
The browser sees it, by the time that he takes to remove it, just after being appended.
See this working Fiddle Example!
There I've replaced the console.log with an alert() to force the browser to wait for my response, thus enabling me to see the element on the page.
Note: Also works fine with console.log(), given me the 18px of height.
You either have the remove() wrapped on a timer to actually see the element (visually), or if the HTML markup is to intense, or the desired effect is to only collect data from the element, place your element inside an hidden one, that way you can remove it whenever you've done collecting data from it.
If what you must acheive is an height measurement, try to add the element without visibility (see CSS:
http://reference.sitepoint.com/css/visibility)
In a zone of the page that causes no problems on the element flow.
I have images who are set to display:none. I am using javascript (document.getElementById('elem').height) to get the height/width of these images.
This works in other browsers, but IE reports the height to be 0 (presumably due to the fact that its display is set to none - because when I remove the display:none, the height is reported correctly). I tried wrapping the images in a div and setting the div's display to none instead of the images - but this didn't work either.
What is the typical work around for this?
If you are interested in the size of the image itself, apart from any styles or attributes set in the html, you can measure a new Image with the same src.
It doesn't add anything to the document's html or stylesheets, or even to document.images.length if you are only testing included images.
var im=new Image();
im.src=element.src;
return [im.src, im.width, im.height];
you could use visibility: hidden;, maybe in combination with position:absolute too prevent "flickering" which you will remove after reading out the height.
Try this:
Position it offscreen
set it to display:block
get its height
set it back to display:none
re-position it back where it was
display:none; elements are defined as not having any display properties, so height and width shouldn't be used while it's in this state.
You could try setting it to visibility:hidden;, which would retain height and width. The downside of this is that visibility doesn't affect it's position in the page flow, so it will also retain the space it takes up in the layout. You could counter-act that by setting the position to either absolute or fixed to take it out of the context flow. You may also want to set the z-index to a negative value to ensure it gets hidden behind the rest of the page elements, otherwise it might block other elements from being clicked, etc, even though it would be invisible.