I am using https://github.com/davidjbradshaw/iframe-resizer library and as suggested by its author, asking my question here.
I have an Angular application loaded into iframe and this application has some elements positioned at the bottom with "absolute" or "fixed". This means, that frame shrinking will not work properly with default methods and I find heightCalculationMethod: "taggedElement"to be the most reliable in my case. But there is just one issue. My page is using ui-router with animated state changes, and now during animation, the frame height gets set to 0 making animation invisible. So I figured out that I need to use minHeight option for iframe-resizer. It generally works fine, I usually set it up to be minHeight: window.innerHeight - 45 where 45 is the height of my page outside the iframe.
But I would like iframe-resizer to recalculate minHeight each time when resizing because user might have resized his browser window.
I guess, it would work if iframe-resizer accepted function for minHeight, so I could do it like this:
minHeight: function(){ return window.innerHeight - 45; }
But I see that currently it just does addStyle('minHeight'); during initial setup, so my solution would not work.
What would be the best way to make iframe-resizer to work with dynamic minHeight?
I expect the reason that the iFrame shrinks to zero is that the element that you have on the page is X number of px above your footer, so each time you resize the frame shrinks that much. Try adding padding to the bottom of this element to stop that happening, or play with the tolerance option.
That said it seem you want the page minSize to relate to the size of the parent window. In IE9 up you should in theory be able to do this with just CSS.
min-height: calc(100vh - 45px);
For IE8, here a slightly simpler version of your answer. It cuts out the need for the expensive call to getElementById and is a bit more generic.
(function() {
'use strict';
// iframe.minHeight gets set only once, but we need to ensure our frame
// is at least as high as current browser window+outer elements
// for animations to work without collapsing the frame to 0,
// therefore we control minHeight ourselves monitoring windoww.resize event
// cross-browser helper function
function addEvent(object, type, callback) {
if (object.addEventListener) {
object.addEventListener(type, callback, false);
} else if (object.attachEvent) {
object.attachEvent("on" + type, callback);
}
};
function minResizeFrame(iframe,offset){
iframe.style.minHeight = (window.innerHeight - offset) + 'px';
}
iFrameResize({
heightCalculationMethod: 'taggedElement',
log: true,
initCallback: function(iframe){
var offset = 45;
addEvent(window, 'resize', minResizeFrame.bind(undefined,iframe,offset));
minResizeFrame(iframe,offset);
}
});
})();
I didn't believe that this will work as expected without conflicting with iframeResizer, but it seems working now. In case there is no better solution, I'm posting this here for people to try and improve:
(function() {
'use strict';
// iframe.minHeight gets set only once, but we need to ensure our frame
// is at least as high as current browser window+outer elements
// for animations to work without collapsing the frame to 0,
// therefore we control minHeight ourselves monitoring windoww.resize event
// cross-browser helper function
function addEvent(object, type, callback) {
if (object === null || typeof(object) === 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
} else if (object.attachEvent) {
object.attachEvent("on" + type, callback);
} else {
object["on"+type] = callback;
}
};
function minResizeFrame(){
document.getElementById("appFrame")
.style.minHeight = (window.innerHeight - 45) + "px";
// 45 is my cumulative height of elements outside of iframe. you will need custom value for your project
}
addEvent(window, "resize", minResizeFrame);
iFrameResize({
// taggedElement is the most reliable method to shrink and grow the frame
// lowestElement also kinda works, but it does not shrink the frame height
// if there are fixed or absolute positioned elements at the bottom of the inner page
heightCalculationMethod: "taggedElement",
log: true
});
//call immediately on load
minResizeFrame();
})();
I have an SVG logo which I need to dynamically change colour when it scrolls past a section. Each section is 100% in height and width of the browser window.
It will either be black or white depending on the background colour. I'm happy to set this once I get the gist of this.
Here is my work in progress, I've searched for something but can't find exactly what I need.
http://digitronix-dev.co.uk/dev/digitronix-holding/
Code I've tried -
var t = $(".dba").offset().top;
$(document).scroll(function(){
if($(this).scrollTop() >= t)
{
$('svg.digi-logo polygon').css({"fill":"#000000"});
$('svg.digi-logo path').css({"fill":"#000000"});
$('svg.digi-logo rect').css({"fill":"#000000"});
} else {
$('svg.digi-logo polygon').css({"fill":"#ffffff"});
$('svg.digi-logo path').css({"fill":"#ffffff"});
$('svg.digi-logo rect').css({"fill":"#ffffff"});
}
});
However this was only for one section.
I would suggest you to use a better scrolling slider such as fullpage.js and to use its jQuery callbacks such as afterLoad or onLeave.
$('#fullpage').fullpage({
afterLoad: function(anchorLink, index){
//afterLoading section 3
if(index == 3){
$('svg.digi-logo polygon').css({"fill":"#ffffff"});
$('svg.digi-logo path').css({"fill":"#ffffff"});
$('svg.digi-logo rect').css({"fill":"#ffffff"});
}
}
});
Alternatively, if you prefer to deal just with CSS, you can use the class fullPage.js adds to the body to trigger one or another CSS rule.
An example of this can be seen in this video tutorial.
You can easily track scroll positions relative to html elements with waypoints.js.
For example:
$('#foo').waypoint(function(direction) {
if (direction === 'down' && $('#bar').is(':visible')) {
$('#bar').velocity({
# velocity.js is simalar to $.animate()
# but better optimized
height: 'hide'
}, 500);
} else {
$('#bar').velocity({
height: $(window).height()
}, 500);
}
}, {
offset: '70px'
});
If you don't need direction its even easier, check docs above for more tips.
What happens is that waypoint.js triggers handler function when top/bottom of viewport 'collides' (well, not really collide but you get the picture) with DOM obj defined in element property. All you need to do is to put your logic into handler method or in if/else body and you're good to go.
EDIT: Never mind. Figured out the issue. Just made height an array and name each id. Der. thanks anyway.
I've searched and not turned up much on this. I've got a simple script that just animates a drop down menu sliding out when hovered over. The issue is if I quickly move back and forth between two or more of the menu items the height variable I set at the begin of each hover can be overwritten. I've got a work around that fixes it after moving off and then back on again by resetting the height to auto when it's off screen but I wont to prevent it from happening at all. Usually I would make a dynamic avariable in other languages I've worked in like:
$height = $(this).attr("id")+"height";
alert($$height);
//which would theoretically alert the height of whatever triggered it.
Is there a way to do this in jQuery so each element that calls the function has it's own height variable?
EDIT2: Since there is interest I'll paste the whole thing.
$("#NavMenu > li").hover(
function () {
var height = {};
height[$(this).attr("id")] = $(this).find("ul").css("height");
$(this).find("ul").css("height", "0px");
$(this).find("ul").css("left", "auto");
$(this).find("ul").animate({ height: height[$(this).attr("id")] }, 300)
},
function () {
$(this).find("ul").css("left", "-999em");
$(this).find("ul").css(height, height[$(this).attr("id")])
}
)
Yes, you can do that:
$('.obj').get(0).key = 'value';
or
$('.obj').get(0)['key'] = 'value'
Though it's better to use .data():
$('.obj').data('key', 'value');
Tips
1. You can chain multiple calls, like this:
$('.obj').css('color', 'red').data('key', 'value');
2. You can pass an object to .css():
$('.obj').css({
'width': 100,
'height': 100
});
3. If you don't change other CSS properties, you can use .width()and .height() to set and get an element's width:
HTML
<div id='box'></div>
CSS
#box {
width: 200px;
height: 100px;
}
JavaScript
var $box = $('#box');
// 200x100
alert( $box.width() + 'x' + $box.height() );
4. You may have noticed in the previous example that I saved a reference to $('#box') in a variable. There are some cases when you can't use chaining, like this:
var $box = $('#box');
setTimeout(function() {
$box.animate({ 'width': 100 }, 100);
}, 1000);
setTimeout(function() {
$box.animate({ 'height': 200 }, 100);
}, 1000);
If you have to do that, always save a reference to the element – that's called caching. Otherwise jQuery would have to search for it multiple times.
Im not sure why you are making your own methods for sliding in and out.
you can use jquery's hide() and show() methods (or even toggle) with an animation inside the parenthesis of the method
I need to get height of an element that is within a div that is hidden. Right now I show the div, get the height, and hide the parent div. This seems a bit silly. Is there a better way?
I'm using jQuery 1.4.2:
$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
You could do something like this, a bit hacky though, forget position if it's already absolute:
var previousCss = $("#myDiv").attr("style");
$("#myDiv").css({
position: 'absolute', // Optional if #myDiv is already absolute
visibility: 'hidden',
display: 'block'
});
optionHeight = $("#myDiv").height();
$("#myDiv").attr("style", previousCss ? previousCss : "");
I ran into the same problem with getting hidden element width, so I wrote this plugin call jQuery Actual to fix it. Instead of using
$('#some-element').height();
use
$('#some-element').actual('height');
will give you the right value for hidden element or element has a hidden parent.
Full documentation please see here. There is also a demo include in the page.
Hope this help :)
You are confuising two CSS styles, the display style and the visibility style.
If the element is hidden by setting the visibility css style, then you should be able to get the height regardless of whether or not the element is visible or not as the element still takes space on the page.
If the element is hidden by changing the display css style to "none", then the element doesn't take space on the page, and you will have to give it a display style which will cause the element to render in some space, at which point, you can get the height.
I've actually resorted to a bit of trickery to deal with this at times. I developed a jQuery scrollbar widget where I encountered the problem that I don't know ahead of time if the scrollable content is a part of a hidden piece of markup or not. Here's what I did:
// try to grab the height of the elem
if (this.element.height() > 0) {
var scroller_height = this.element.height();
var scroller_width = this.element.width();
// if height is zero, then we're dealing with a hidden element
} else {
var copied_elem = this.element.clone()
.attr("id", false)
.css({visibility:"hidden", display:"block",
position:"absolute"});
$("body").append(copied_elem);
var scroller_height = copied_elem.height();
var scroller_width = copied_elem.width();
copied_elem.remove();
}
This works for the most part, but there's an obvious problem that can potentially come up. If the content you are cloning is styled with CSS that includes references to parent markup in their rules, the cloned content will not contain the appropriate styling, and will likely have slightly different measurements. To get around this, you can make sure that the markup you are cloning has CSS rules applied to it that do not include references to parent markup.
Also, this didn't come up for me with my scroller widget, but to get the appropriate height of the cloned element, you'll need to set the width to the same width of the parent element. In my case, a CSS width was always applied to the actual element, so I didn't have to worry about this, however, if the element doesn't have a width applied to it, you may need to do some kind of recursive traversal of the element's DOM ancestry to find the appropriate parent element's width.
Building further on user Nick's answer and user hitautodestruct's plugin on JSBin, I've created a similar jQuery plugin which retrieves both width and height and returns an object containing these values.
It can be found here:
http://jsbin.com/ikogez/3/
Update
I've completely redesigned this tiny little plugin as it turned out that the previous version (mentioned above) wasn't really usable in real life environments where a lot of DOM manipulation was happening.
This is working perfectly:
/**
* getSize plugin
* This plugin can be used to get the width and height from hidden elements in the DOM.
* It can be used on a jQuery element and will retun an object containing the width
* and height of that element.
*
* Discussed at StackOverflow:
* http://stackoverflow.com/a/8839261/1146033
*
* #author Robin van Baalen <robin#neverwoods.com>
* #version 1.1
*
* CHANGELOG
* 1.0 - Initial release
* 1.1 - Completely revamped internal logic to be compatible with javascript-intense environments
*
* #return {object} The returned object is a native javascript object
* (not jQuery, and therefore not chainable!!) that
* contains the width and height of the given element.
*/
$.fn.getSize = function() {
var $wrap = $("<div />").appendTo($("body"));
$wrap.css({
"position": "absolute !important",
"visibility": "hidden !important",
"display": "block !important"
});
$clone = $(this).clone().appendTo($wrap);
sizes = {
"width": $clone.width(),
"height": $clone.height()
};
$wrap.remove();
return sizes;
};
Building further on Nick's answer:
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").css({'position':'static','visibility':'visible', 'display':'none'});
I found it's better to do this:
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").removeAttr('style');
Setting CSS attributes will insert them inline, which will overwrite any other attributes you have in your CSS file. By removing the style attribute on the HTML element, everything is back to normal and still hidden, since it was hidden in the first place.
You could also position the hidden div off the screen with a negative margin rather than using display:none, much like a the text indent image replacement technique.
eg.
position:absolute;
left: -2000px;
top: 0;
This way the height() is still available.
I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.
flexibox example
I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.
Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.
$('[selector]').onVisibleChanged(function(e, isVisible)
{
var realWidth = $('[selector]').width();
var realHeight = $('[selector]').height();
// render or adjust something
});
For more information, Please visit at my project GitHub.
https://github.com/Soul-Master/visible.event.js
demo: http://jsbin.com/ETiGIre/7
Following Nick Craver's solution, setting the element's visibility allows it to get accurate dimensions. I've used this solution very very often. However, having to reset the styles manually, I've come to find this cumbersome, given that modifying the element's initial positioning/display in my css through development, I often forget to update the related javascript code. The following code doesn't reset the styles per say, but removes the inline styles added by javascript:
$("#myDiv")
.css({
position: 'absolute',
visibility: 'hidden',
display: 'block'
});
optionHeight = $("#myDiv").height();
optionWidth = $("#myDiv").width();
$("#myDiv").attr('style', '');
The only assumption here is that there can't be other inline styles or else they will be removed aswell. The benefit here, however, is that the element's styles are returned to what they were in the css stylesheet. As a consequence, you can write this up as a function where an element is passed through, and a height or width is returned.
Another issue I've found of setting the styles inline via js is that when dealing with transitions through css3, you become forced to adapt your style rules' weights to be stronger than an inline style, which can be frustrating sometimes.
By definition, an element only has height if it's visible.
Just curious: why do you need the height of a hidden element?
One alternative is to effectively hide an element by putting it behind (using z-index) an overlay of some kind).
In my circumstance I also had a hidden element stopping me from getting the height value, but it wasn't the element itself but rather one of it's parents... so I just put in a check for one of my plugins to see if it's hidden, else find the closest hidden element. Here's an example:
var $content = $('.content'),
contentHeight = $content.height(),
contentWidth = $content.width(),
$closestHidden,
styleAttrValue,
limit = 20; //failsafe
if (!contentHeight) {
$closestHidden = $content;
//if the main element itself isn't hidden then roll through the parents
if ($closestHidden.css('display') !== 'none') {
while ($closestHidden.css('display') !== 'none' && $closestHidden.size() && limit) {
$closestHidden = $closestHidden.parent().closest(':hidden');
limit--;
}
}
styleAttrValue = $closestHidden.attr('style');
$closestHidden.css({
position: 'absolute',
visibility: 'hidden',
display: 'block'
});
contentHeight = $content.height();
contentWidth = $content.width();
if (styleAttrValue) {
$closestHidden.attr('style',styleAttrValue);
} else {
$closestHidden.removeAttr('style');
}
}
In fact, this is an amalgamation of Nick, Gregory and Eyelidlessness's responses to give you the use of Gregory's improved method, but utilises both methods in case there is supposed to be something in the style attribute that you want to put back, and looks for a parent element.
My only gripe with my solution is that the loop through the parents isn't entirely efficient.
One workaround is to create a parent div outside the element you want to get the height of, apply a height of '0' and hide any overflow. Next, take the height of the child element and remove the overflow property of the parent.
var height = $("#child").height();
// Do something here
$("#parent").append(height).removeClass("overflow-y-hidden");
.overflow-y-hidden {
height: 0px;
overflow-y: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent" class="overflow-y-hidden">
<div id="child">
This is some content I would like to get the height of!
</div>
</div>
Here's a script I wrote to handle all of jQuery's dimension methods for hidden elements, even descendants of hidden parents. Note that, of course, there's a performance hit using this.
// Correctly calculate dimensions of hidden elements
(function($) {
var originals = {},
keys = [
'width',
'height',
'innerWidth',
'innerHeight',
'outerWidth',
'outerHeight',
'offset',
'scrollTop',
'scrollLeft'
],
isVisible = function(el) {
el = $(el);
el.data('hidden', []);
var visible = true,
parents = el.parents(),
hiddenData = el.data('hidden');
if(!el.is(':visible')) {
visible = false;
hiddenData[hiddenData.length] = el;
}
parents.each(function(i, parent) {
parent = $(parent);
if(!parent.is(':visible')) {
visible = false;
hiddenData[hiddenData.length] = parent;
}
});
return visible;
};
$.each(keys, function(i, dimension) {
originals[dimension] = $.fn[dimension];
$.fn[dimension] = function(size) {
var el = $(this[0]);
if(
(
size !== undefined &&
!(
(dimension == 'outerHeight' ||
dimension == 'outerWidth') &&
(size === true || size === false)
)
) ||
isVisible(el)
) {
return originals[dimension].call(this, size);
}
var hiddenData = el.data('hidden'),
topHidden = hiddenData[hiddenData.length - 1],
topHiddenClone = topHidden.clone(true),
topHiddenDescendants = topHidden.find('*').andSelf(),
topHiddenCloneDescendants = topHiddenClone.find('*').andSelf(),
elIndex = topHiddenDescendants.index(el[0]),
clone = topHiddenCloneDescendants[elIndex],
ret;
$.each(hiddenData, function(i, hidden) {
var index = topHiddenDescendants.index(hidden);
$(topHiddenCloneDescendants[index]).show();
});
topHidden.before(topHiddenClone);
if(dimension == 'outerHeight' || dimension == 'outerWidth') {
ret = $(clone)[dimension](size ? true : false);
} else {
ret = $(clone)[dimension]();
}
topHiddenClone.remove();
return ret;
};
});
})(jQuery);
If you've already displayed the element on the page previously, you can simply take the height directly from the DOM element (reachable in jQuery with .get(0)), since it is set even when the element is hidden:
$('.hidden-element').get(0).height;
same for the width:
$('.hidden-element').get(0).width;
(thanks to Skeets O'Reilly for correction)