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();
})();
Related
I need to know if the end of a div element is currently visible in the users' browser.
I tried something I saw on the web, but scrollTop() always gave me zero in my Browser. I read something about an issue in Chrome, but I didn't understand quite well.
jQuery(
function($) {
$('#flux').bind('scroll', function() {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
alert('end reached');
}
})
}
);
My idea is the following:
1- User loads page and sees a Bar (sticky div at bottom visible page) with some information.
2- After scrolling a bit, and reaching the end of a div element, this bar will position there, after the div. This is the bar's original position
I wasn't really able to know when I was at the end of the div element. Eventually I found this code:
if ($(window).scrollTop() >= $('#block-homepagegrid').offset().top + $('#block-homepagegrid').outerHeight() - window.innerHeight) {
$('.hero-special-message').removeClass('hero-special-messege-scrolling');
} else {
$('.hero-special-message').addClass('hero-special-messege-scrolling');
}
});
I see that it's working, but I'm having a bit of trouble understanding what it does.
I know the following:
1. $(window).scrollTop();
this gives me the amount of pixels the user has scrolled, pretty self explanatory.
2. $('#block-homepagegrid').offset().top;
I THINK this is the distance between the start of the page and the start of the div. I know it's the current coordinates, but what is top exactly here?
3. $('#block-homepagegrid').outerHeight();
this gives the height of the element, I know there are 3, like
height(), innerHeight() and outerHeight(), if you want to take into
account border, margin, padding, which is the better to use?
4. window.innerHeight;
I understand this is what the user sees, but I'm having troubles understanding why does it matter for my situation.
Thanks!
You may be interested in the native JavaScript IntersectionObserver API. It automatically figures out what percentage of a given element is visible in the window and triggers callbacks based on that. So then you can do this:
function visibleHandler(entries) {
if (entries[0].intersectionRatio >= 1.0) {
// The whole element is visible!
} else {
// Part of it is scrolled offscreen!
}
}
const observer = new IntersectionObserver(visibleHandler, {threshold: 1.0});
observer.observe(document.getElementById('flux'));
Now, whenever the element with ID flux is 100% in view, it will trigger the visibleHandler. It will also trigger again if it's scrolled back out of view; that's why the function checks the ratio of visibility to see if it just hit 100% or just got reduced from 100%. You could be more fancy and use the observer entry's insersectionRect, which gives you the rectangle containing the visible portion of the element, and use that to determine top/bottom visibility.
If I have div A and div B, is there a way to say A.width = b.width = MAX(a.width, b.width) ? That is, whichever has the largest inner content would dictate how large both are.
The actual problem I'm trying to solve is with columns - left, middle, and right. I want the left and right to be the same fixed width (but this could vary depending on their content).
It is not possible to use CSS to achieve this. However, if there is a way to do it with a JS-based solution. Here I am using jQuery. Let's say you have two divs, with classes a and b respectively.
$(function() {
function equalizeSize($ele) {
if($ele.length > 1) {
// Let CSS automatically calculate natural width first
$ele.css({ width: 'auto' });
// And then we fetch the newly calculated widths
var maxWidth = Math.max.apply(Math, $ele.map(function(){ return $(this).outerWidth(); }).get());
$ele.css({ width: maxWidth });
}
}
// Run when DOM is ready
equalizeSize($('.a, .b'));
// Run again when viewport has been resized, which **may** affect your div width.
// This is optional, but good to have
// ps: You might want to look into throttling the resize function
$(window).resize(equalizeSize($('.a, .b')));
});
See proof-of-concept fiddle here: http://jsfiddle.net/teddyrised/N4MMg/
The advantages of this simple function:
Allows you to dictate what elements you want to equalize widths with.
Uses the .map() function to construct an array, which we then use Math.max.apply to get the maximum value in the array
Forces automatic calculation of width when the function first fires (especially when resizing the viewport)
Allows you to call to recalculate the size again, using the handler equalizeSize() when you change the content in the divs... you can call the function again, say, after an AJAX call that appends content to either element.
It is not very clear what you want from the description. but I can rewrite your code this way.
var properWidth = Math.max($("#a").width(), $("#b").width());
$("#a").css("width", properWidth + "px");
$("#b").css("width", properWidth + "px");
I am not sure if it is this kind of solution you want.
I'm not sure there is a way to do it like that. But why not make a default function to set the size:
function changeSize(w, h)
{
A.setAttribute('style', 'width:'+w+'; height:'+h);
b.setAttribute('style', 'width:'+w+'; height:'+h);
}
Working fiddle: http://jsfiddle.net/kychan/ER2zZ/
On my website I use a JS for parallax scrolling. I want to conditionally load that JS only on Desktop PCs with >= 1025 px width. And I desperately need help with optimizing and putting it together.
What I currently have
The sections that have the Parallax-Effect have several data-attributes:
<section id="profile" class="slide" data-speed="2" data-offsetY="185" data-type="background">
The sections also have a background image set up via css.
.slide {
padding:0;
width:100%;
position:relative;
margin:0 auto;
overflow:hidden;
}
#profile {
background: url(../assets/backgrounds/02_profile_back.jpg) 50% 185px no-repeat fixed, #0f0f11;
height:1027px;
}
JAVASCRIPT
What I got so far:
Custom Function (myFunction) that's executed on $(document).ready
Check on window.on('scroll resize load')
Execute JS #breakpoint of 1025 px via enquire.js (match)
Set alternative / default behaviour (unmatch)
Use the listener of enquire.js.
Here's the code so far:
(The Parallax Script is: by Richard Shepherd, modified by Ryan Boudreaux)
function myFunction(){
// Cache the Window object
$window = $(window);
// Cache the Y offset and the speed of each sprite
$('[data-type]').each(function() {
$(this).data('offsetY', parseInt($(this).attr('data-offsetY')));
$(this).data('Xposition', $(this).attr('data-Xposition'));
$(this).data('speed', $(this).attr('data-speed'));
});
// For each element that has a data-type attribute
$('section[data-type="background"]').each(function(){
// Store some variables based on where we are
var $self = $(this),
offsetCoords = $self.offset(),
topOffset = offsetCoords.top;
// This is the part I'm having trouble with //
// When the window is scrolled, resized, loaded...
$(window).on('scroll resize load',function(e){
// Check if Screen-width is higher or equal to 1025px
enquire.register('screen and (min-width:1025px)', {
match : function() {
// If this section is in view
if ( ($window.scrollTop() + $window.height()) > (topOffset) &&
( (topOffset + $self.height()) > $window.scrollTop() ) ) {
// Scroll the background at var speed
// the yPos is a negative value because we're scrolling it UP!
var yPos = -($window.scrollTop() / $self.data('speed'));
// If this element has a Y offset then add it on
if ($self.data('offsetY')) {
yPos += $self.data('offsetY');
}
// Put together our final background position
var coords = '50% '+ yPos + 'px';
// Move the background
$self.css({ backgroundPosition: coords });
}; // in view
},
unmatch : function() {
$('#profile').css('background-position', '50% 0px');
}
}).listen(); // Check Screen Width
}); // window load resize scroll
}); // each data-type
} // myFunction
What I want to implement but cannot figure out
I'm looking for a way to squeeze in another if-loop, that only executes the first "match"-part if the device is NOT a Touch Device. I found var isTouch = (('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator)); but I cannot figure out how to implement this so everything fits together.
Questions
Is there a way of realizing the condition "if desktop PC with 1025px and higher width"?
Is there a best practice / common solution for conditionally loading that parallax-JS at the end of mobile-first?
I know my code is a mess, you can probably tell by now that I'm a noob. Nevertheless, I'm eager to learn and am looking forward to your replies. Thanks a bunch!
Well you could use yepnope to load your JavaScript dynamically, and then use window.innerWidth to check the screen width. I'm not sure on checking specifically for a mobile device, unless you use browser user agent strings.
so suppose that clicking something would lead to a new content being loaded to the screen hence the height of document changes and whereas previously there are no scroll bars, now there actually are scrollbars...
how do I detect something like that happening using jquery
binding resize event onto window only detects window resize whereas binding it into document doesn't work
Update:
Please don't use the DOMSubtreeModified event. It is old, deprecated and not well-supported by browsers. In 99,9 % of the cases, there is a different event you can listen on. Most likely you are one of those people using jQuery and doing some AJAX stuff, so please take a look at their AJAX docs.
These are all available events. You would have to detect $(document).bind('DOMSubtreeModified', function() { ... }); and check for a dimension change to the previous firing.
var height = $(this).height(),
width = $(this).width();
$(document).bind('DOMSubtreeModified', function() {
if($(this).height() != height || $(this).width() != width) {
recalibrate();
}
});
This event is firing every time anything is done to the DOM. Therefore it will slowdown your browser.
We should get a better alternative. Could you please give us more information to your scenario?
Here is the solution.
// ajdust margins when page size changes (ie rotate mobile device)
$(window).resize(function() {
// Do something more useful
console.log('doc height is ' + $(window).height());
});
You could try a percentage scrolled event like this one:
http://www.jquery4u.com/snippets/jquery-detect-scrolled-page/
You might need to add a check to see whether the vertical scrollbar is present:
var hasVScroll = document.body.scrollHeight > document.body.clientHeight;
I have a horizontally scrolling website, and I have a block that I want to stay in frame at all times as the user scrolls right. It looks perfectly smooth in webkit browsers, but is crazy jagged in Firefox and I don't really care about IEs.
function fixMyId(){
$('#myId').css({'margin-left': 150 + $(window).scrollLeft()});
}
function fixMyIdAlt(){
$('#myId').stop().animate({'margin-left': 150 + $(window).scrollLeft()}, 300);
}
And then I have it triggered on window scroll.
What would be a best way to average out the scrolling, so that maybe every so many seconds or pixels of scrolling it fires the function, or upon stopping the scrolling it animates the block into place? I tried playing with delay() but that doesn't do anything. And this one just looks stupid (plus I have no idea what the overhead of this kind of crunching is):
function fixMyIdStupid(){
window.scrollCounter++;
if(window.scrollCounter % 20 == 0) $('#myId').stop().animate({'margin-left': 150 + $(window).scrollLeft()}, 300);
}
So what do I do? setTimeout and setInterval may be required, but those always make my head hurt.
EDIT: Here's a jsfiddle of it in action: http://jsfiddle.net/xsxSq/
The #f0f square is the #myId.
I tried to do such things as well, problem is that the scroll event isn't fired as much as you want. A nice workaround was subscribing the calculation function to the mousemove event, so it triggers A LOT. But on the other hand, I came up with another solution.
Why not turn things around and ask yourself:
Lets make it a position:fixed object and calculate what happens on resize. Because you actually are trying to create a position-x:fixed; and a position-y:absolute;
I actually did the following for the opposite kind of thing. A block that has to be exactly in the middle of the x-document, but in the y it was fixed.
$(document).ready(function ()
{
replaceFixed();
$(window).resize(replaceFixed);
$('#content').ajaxSuccess(replaceFixed);
$(window).scroll(replaceFixed);
function replaceFixed()
{
var jEl = $('#centeredFixedContainer');
var winW = $(window).width();
var docW = $(document).width();
var scrL = $(window).scrollLeft();
var divW = jEl.width();
var result = 0;
// window bigger than the element
if(winW > divW)
{
result = -scrL + ((docW-winW)/2);
}
else
{
result = $('#mainContainer').offset().left - scrL;
}
jEl.css('left',result);
}
});
Copying this code will not give you the solution, but will indicate another way to look at your problem.