iFrame and matching sidebar div height - javascript

So I have a html page. I have an iframe in it, that resizes when the content is loaded in it, but i have a sidebar(div) that I need to set height equal to new iframe height.
My javascript for iframe resize is
function setIframeHeight( iframeId ) /** IMPORTANT: All framed documents *must* have a DOCTYPE applied **/
{
var ifDoc, ifRef = document.getElementById( iframeId );
try
{
ifDoc = ifRef.contentWindow.document.documentElement;
}
catch( e )
{
try
{
ifDoc = ifRef.contentDocument.documentElement;
}
catch(ee)
{
}
}
if( ifDoc )
{
ifRef.height = 1;
ifRef.height = ifDoc.scrollHeight;
/* For width resize, enable below. */
// ifRef.width = 1;
// ifRef.width = ifDoc.scrollWidth;
}
}
Actualy the code is not mine, Credits to Logic Ali .

You will need to have an onResize handler like such.
http://www.w3schools.com/jsref/event_onresize.asp
window.onresize=function(){
setIframeHeight( iframeId )
};

Related

How to determine when a child iframe element is within the visible area of a scrolling div?

I have a scrolling div that may or may not contain one or more iframes who's contents should only be loaded when the iframe is either partly/fully visible between the parent div's scroll area's top and bottom. However, when there are iframes in the div and when I inspect my web-page in Chrome, I see that the iframe's offsetTop values don't change as the parent div's scroll-bar is scrolled upward or downward. Also, I see that the offsetParent of the iframes references the body element rather than the scrolling div that actually contains the iframes.
Here is a simple web-page that shows the basic div and nested iframe structure that I'm talking about:
<!DOCTYPE html>
<html>
<body>
<!-- Scrolling div -->
<div id="scroller" style="height:316px; width: 322px; overflow-y:scroll;">
<!-- 5 iframes that each contain an img tag that is ignored -->
<iframe><img id="img" src="1.png"></iframe><br />
<iframe><img id="img" src="2.png"></iframe><br />
<iframe><img id="img" src="3.png"></iframe><br />
<iframe><img id="img" src="4.png"></iframe><br />
<iframe><img id="img" src="5.png"></iframe><br />
</div>
<script>
//
// Assign an onScroll function to the scroller div that monitors the div's
// scrolling ...
//
function scrollerDivScrolling() {
var div = scrollerDivScrolling.div;
//
// The following code only changes the div's background color when the
// div's scroll-bar is scrolled: white when less-than or 50px or
// yellow when more 50px.
//
if( div.scrollTop > 50 ) {
div.style.backgroundColor = "yellow";
}
else {
div.style.backgroundColor = "white";
}
//
// This is code needs to check each of the iframe's position to
// see if the iframe is displayed partially/fully between the
// parent div's scroll area's top and bottom, and if so then the
// iframe's content is set to the html statement(s) that are
// between to iframe's opening and closing tags. If the iframe's
// position is either completely above top of the parent div's
// scroll area or completely below the parent div's scroll area,
// then remove the contents.
//
}
scrollerDivScrolling.div = document.getElementById( 'scroller' );
scrollerDivScrolling.iframes = scrollerDivScrolling.div.getElementsByTagName( 'iframe' );
scrollerDivScrolling.div.onscroll = scrollerDivScrolling;
// Initialize the iframes with in the scroller div to 'show' their contents ...
for( var i = 0, iframes = scrollerDivScrolling.iframes, l = iframes.length, iframeDoc = null; i < l; ++i ) {
iframeDoc = iframes[ i ].contentWindow.document;
iframeDoc.open();
iframeDoc.write('Test' + ( i + 1 ) );
iframeDoc.close();
}
</script>
</body>
</html>
As shown in the above code, the iframes are actually loaded with "Test " followed by a number, rather than the image tag statement that is within the individual iframe tags, but this isn't an issue, as I know how to do this already. More importantly, the scrollerDivScrolling() function that handles scrolling the div doesn't actually change the content of each iframe depending on its position relative to the parent div's scroll-area's top and bottom, because I haven't figured out which properties of each iframe I need to use.
I expanded my search and found: Detect when elements within a scrollable div are out of view, which I integrated with my scrollerDivScrolling() function as follows:
<script>
const UNDEFINED = undefined;
// Assign an onScroll function to the scroller div that monitors the div's scrolling ...
function checkInView( elem, partial ) {
var container = $( '.scrollable' );
var contHeight = container.height();
var contTop = container.scrollTop();
var contBottom = contTop + contHeight - 1;
var $elem = $( elem );
var elemTop = $elem.offset().top - container.offset().top;
var elemBottom = elemTop + $elem.height();
var isTotal = ( ( elemTop >= 0 ) && ( elemBottom <= contHeight ) );
var isPart = false;
if( partial ) {
isPart = ( ( ( elemTop < 0 ) && ( elemBottom > 0 ) ) || ( ( elemTop > 0 ) && ( elemTop <= container.height() ) ) );
}
return ( isTotal || isPart );
}
function scrollerDivScrolling() {
var div = scrollerDivScrolling.div;
for( var i = 0, iframes = scrollerDivScrolling.iframes, l = iframes.length, iframeDoc = null; i < l; ++i ) {
// If the element is in the scrollable div's visible area ...
if( checkInView( scrollerDivScrolling.iframes[ i ], true ) ) {
// If the element had not already been loaded ...
if( ( iframes[ i ].loaded === UNDEFINED ) || !iframes[ i ].loaded ) {
// Mark the element as loaded and load the element ...
iframes[ i ].loaded = true;
iframeDoc = iframes[ i ].contentWindow.document;
iframeDoc.open();
iframeDoc.write( 'Test' + ( i + 1 ) + '&nbsp' + iframes[ i ].innerHTML );
iframeDoc.close();
}
}
else {
// Mark the element as NOT loaded and unload the element ...
iframes[ i ].loaded = false;
iframeDoc = iframes[ i ].contentWindow.document;
iframeDoc.open();
iframeDoc.write( '' );
iframeDoc.close();
}
}
}
scrollerDivScrolling.div = document.getElementById( 'scroller' );
scrollerDivScrolling.div.onscroll = scrollerDivScrolling;
scrollerDivScrolling.iframes = scrollerDivScrolling.div.getElementsByTagName( 'iframe' );
// Initialize the iframes with in the scroller div to 'show' their contents ...
for( var i = 0, iframes = scrollerDivScrolling.iframes, l = iframes.length, iframeDoc = null; i < l; ++i ) {
iframes[ i ].loaded = UNDEFINED;
}
scrollerDivScrolling();
</script>
For me, the checkInView(...) function shows that the first three iframe elements are 'in view'when the scroller is all the way at the top, meaning that the function returns true for the first three elements in the div, even though the scrolling div's height is only tall enough to show the first two iframe elements, but this isn't really a problem for me.
Using this technique I can 'pre-load' the iframes with the html that they will show when the scrolling div scrolls each iframe into view, and img tags contained in each iframe will normally load their images as they come into view.
For me, this helps in two ways: the initial page load doesn't need to load all of the iframes before the page can be viewed and the user can begin to scroll through the 'rows' iframes' sub-pages, and the over all amount of storage needed to contain all of the data is somewhat 'flattened' because only the page data needed for the elements that are showing is loaded and the hidden rows in the scrollable div are removed. when their contents are replaced with ''.
Here is a Pen demo showing a working sample of how this works: Scrolling Div of delay-loading iframes

how to set iframe height dynamically depending on its content

I want to set the iframe height depending on the content height, here below code is working for local links, but i want to do it on dynamically.
please help.
<iframe id="frameDemo" src="https://myvook.blogspot.com"></iframe>
<script>
$(document).ready(function() {
$( "#frameDemo" ).on('load', function() {
var mydiv = $(this).contents().find("body");
var h = mydiv.height();
$("#frameDemo").height(h);
});
});
Following up with what Rory said, it's not possible to set the iframe height before before the iframe has been loaded since you'll be pulling content from another domain.
However, it is possible to set the height of the iframe after you've downloaded the content. Here is how I've tackled it:
function noteContentIframeLoaded(iframeId) {
// See http://stackoverflow.com/a/5788723 for how to implement this function
var iframeElement = $("#" + iframeId)[0];
setTimeout(function() {
setIframeHeight(iframeElement);
}, 10);
}
function setIframeHeight(iframe) {
if (iframe) {
if (iframe.contentWindow || iframe.contentDocument) {
var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow;
if (iframeWin.document.body) {
iframe.height = (iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight);
}
}
}
}

Google DFP - Resize SafeFrame custom creative outer Iframe container from inside (expand ad)

i'm searching for solution, that can expand SafeFrame custom ad from inside of custom creative in Google DFP, is that possible somehow?
There are 2 possible solutions:
1) using SafeFrame API
pros:
u can use it "out of the box"
you can use it on any website, no custom code on website is needed
it's safe to use
cons:
it's limited to fill just visible area of website
need to wait, until ad unit is visible to the user
2) code your own API with window.postMessage() javascript method
cons:
you need to add custom code to your website
it's a possible threat, if you use some 3th party creatives
pros:
you can do almost anything with your website from your creative
1) using SafeFrame API
This API is realatively easy to use, you can see some examples in
GPT Safeframe preview tool.
First you need to update your DFP initialization script in <head> of your website
var pageConfig = {
allowOverlayExpansion: true,
allowPushExpansion: true,
sandbox: true
};
googletag.pubads().setSafeFrameConfig(pageConfig);
This will allow to expand SafeFrame ads on your website. More about this in
Control SafeFrame Container behavior through GPT.
Now you can create custom creative and serve it as SafeFrame on your website. Here is my one example. This Example can "wait" util it's visible, and then will expand to height of <div id="container"> that is inside of SafeFrame:
<div id="container">
some lines to make container height<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
line<br>
</div>
<script>
// global expanded indicator variable, because API don't have any
var expanded = false;
function expand() {
var self= $sf.ext.geom().self;
var config = {
push: true, // we want to push expanded content
b: 0
};
var el = document.getElementById('container');
var containerHeight = el.offsetHeight;
// get height from bottom, that need to be expanded
var expandBottom = containerHeight - self.h;
// if container is whole inside a SafeFrame, it will not expand
if(expandBottom < 0) return;
config.b = expandBottom;
$sf.ext.expand(config);
}
function expandDelayed(forceExpand) {
// expand will run just once, or you can force it to run again
// but collapse first is needed
if(expanded && forceExpand || !expanded) {
$sf.ext.collapse();
expanded = false;
// there must be some timeout, because .collapse(); method is deplayed somehow
setTimeout(expand, 0);
}
}
$sf.ext.register(160, 150, function(status, data) {
// this code will do whole magic of "waiting" for right moment
if (status === 'geom-update') {
expandDelayed();
}
// change global expanded status
if (status === 'expanded') {
expanded = true;
}
});
// init
expandDelayed();
</script>
2. code your own API with window.postMessage() javascript method
First, you need to put this code, to your DFP initialization script in <head> of your website. This code will add an ID of Ad slot as #hash-tag to <iframe>'s src so you can get it from inside of your creative.
googletag.pubads().addEventListener('slotRenderEnded', function (event) {
var containerId = event.slot.getSlotElementId();
var containerEl = document.getElementById(containerId);
if (containerEl === null) return;
var iframeEl = containerEl.querySelectorAll('iframe')[0];
// it's delayed by 10 milliseconds, because iframe is not yet fully rendered
// and limited to max to 10 seconds to wait
var timeoutFunction = function () {
var src = "#" + containerId;
// `src` attribute is null, when iframe is FriendlyIframe, and
// when it's present, then it's SafeFrame
if (iframeEl.getAttribute('src') !== null) {
src = iframeEl.getAttribute('src').replace(/#.*/, "") + src;
} else {
var name = iframeEl.getAttribute('name') + "#" + containerId;
iframeEl.setAttribute('name', name);
}
iframeEl.setAttribute('src', src);
};
setTimeout(timeoutFunction, 10);
});
Second, you need to add this code to your website, better as separated .js file.
function onMessageReceivedGetStyle(e) {
// this will filter just setStyle commands from correct origin
if (
!(e.origin === 'http://tpc.googlesyndication.com' || e.origin === 'https://tpc.googlesyndication.com') ||
typeof e.data !== 'object' ||
typeof e.data.id !== 'string' ||
e.data.cmd !== 'setStyle' ||
typeof e.data.params !== 'object'
) {
return;
}
// remove # character from id, we don't use jquery
var elementId = e.data.id.replace(/#/, "");
var wrapperEl = document.getElementById(elementId);
if (wrapperEl === null) {
return;
}
var elements = [wrapperEl];
// you can target child elements too with query parameter
if (typeof e.data.query === 'string' && e.data.query) {
elements = wrapperEl.querySelectorAll(e.data.query);
}
elements.forEach(function (element) {
Object.keys(e.data.params).forEach(function (param) {
element.style[param] = e.data.params[param];
});
});
}
if (window.addEventListener) {
addEventListener('message', onMessageReceivedGetStyle, false);
}
else {
if (window.attachEvent) {
attachEvent('onmessage', onMessageReceivedGetStyle);
}
else {
window.onmessage = onMessageReceivedGetStyle;
}
}
And third thing is your custom code in custom type of creative in DFP. Here is example, that is similar to that in
first example, but here this script can wait until all content and image is loaded and then will expand/shrink your
iframe with creative:
<div id="container">
<a href="#" target="_blank">
<img src="%%FILE:JPG1%%">
</a>
<a href="#" target="_blank">
<img src="%%FILE:JPG2%%">
</a>
</div>
<style>
a {
display: block;
margin-bottom: .5em;
}
img {
display: block;
max-width: 100%;
}
*:last-child {
margin-bottom: 0;
}
</style>
<script>
var container = document.getElementById('container');
function resizeOutsideSafeFrame() {
if (!window.top.postMessage) {
return false;
}
// get ID of your Ad unit <div> with this creative
var divGptAdId = '%%PATTERN:url%%';
if (divGptAdId.indexOf('#') !== -1) {
divGptAdId = divGptAdId.split('#')[1];
} else {
divGptAdId = window.location.hash;
}
if(!divGptAdId) {
if (window.name.indexOf('#') !== -1) {
divGptAdId = window.name.split('#')[1];
}
}
if(!divGptAdId) {
return;
}
// set with to fullwidth, and height to height of container inside creative
var width = '100%';
var height = container.offsetHeight + 'px';
// send our request to website
window.top.postMessage({
cmd: 'setStyle',
id: divGptAdId,
query: 'div, iframe', // we want to target child div and iframe and don't change container styles
params: {
display: 'block',
height: height,
width: width
}
}, '*');
}
document.onreadystatechange = function () {
// resize iframe when all is loaded
if (document.readyState == "complete") {
resizeOutsideSafeFrame();
}
};
// first resize must occur little bit later
setTimeout(resizeOutsideSafeFrame, 100);
</script>
That's all. When you want to change anything on your website from inside of iframe, you can code your own cmd on your
website and call this command from inside of the iframe.
Edit 1:
just noticed now, that var divGptAdId = '%%PATTERN:url%%; will not return correct id of div on the page in #hash way, so now it's needed to give him a correct container div id change:
if(!divGptAdId) {
return;
}
to
if(!divGptAdId) {
divGptAdId = 'div-gpt-ad-container-div-id-1';
}
I couldn't find any solid documentation on this so the example from PayteR helped me immensely! I had to play with the new dimension to get the ad to expand the right way.
Here is some sample code I created from PayteR's example:
/* Main object for namespacing */
var Ad = function(obj) {};
/*
Register your ad with the dimensions of the current ad.
- This is basically an event handler for the the ad frame.
- 728 initial ad width
- 90 initial ad height
*/
Ad.prototype.registerExpand = function() {
$sf.ext.register(728, 90, function(status, data){});
};
/* Expand function to be called on click or load */
Ad.prototype.expandAd = function() {
/* Get the current geometry of your ad */
var self = $sf.ext.geom().self;
/*
Set the new geometry
- Increase height 315 pixels
- Expand downward by setting the bottom to the new height minus the current height
*/
$sf.ext.expand({
h: 315,
b: 315 - self.h
});
};
/* Intialize, register, and expand */
var ad = new Ad();
ad.registerExpand();
ad.expandAd();

Get clientHeight of content inside custom web-component nested in Polymer paper-dialog

I''ll try to explain my problem the clearest I can.
I'm using <paper-dialog> and <paper-dialog-scrollable>.
In <paper-dialog-scrollable> I have a form which is a custom web-component.
In this form I use an other custom web-component which expands and collapses content.
The default state of the content is collapsed.
In the expandcomponent I save the clientHeight of the content in a variable contentHeight and set the height of the content to 0.
I have a function toggle() which is executed when a trigger is clicked.
toggle() sets the contents height to contentHeight.
Now this works perfectly when I use my form or the expand component alone, but it doesn't work when they're nested inside a paper-dialog because the clientHeight is then 0.
Code:
<paper-dialog with-backdrop style="min-width: 90%;">
<paper-dialog-scrollable>
<my-custom-form-component></my-custom-form-component>
</paper-dialog-scrollable>
</paper-dialog>
Code from <my-custom-form-component> :
<div id="custom-expand-component-trigger"></div>
<custom-expand-component trigger="custom-expand-component-trigger">
blabla a lot of content......
</custom-expand-component>
toggle() function (inside <custom-expand-component>):
function toggle(){
if(!that.opened){
content.style.height = contentHeight + 'px'; //contentHeight is 0 when form is nested in <paper-dialog>
} else{
content.style.height = startHeight;
}
that.opened = !that.opened;
}
Any Ideas how I can get the clientHeight even if my form is inside the dialog?
I hope this is clear enough.
Help would be greatly appreciated
The clientHeight of an hidden element is 0 so you cannot get it until it is rendered.
The element is actually rendered first when the <paper-dialog> is opened. When it happens, the iron-overlay-opened event is fired. That's an opportunity to get the right clientHeight if you haven't set it before:
myDialog.addEventListener( "iron-overlay-opened", function ()
{
this.querySelector( "custom-expand-component" ).init()
} )
Inside the init() method, set the correct values for your private variables:
var CEC = Object.create( HTMLElement.prototype )
CEC.createdCallback = function () {
var that = this
var startHeight
var contentHeight
this.init = function () {
if ( !contentHeight ) {
contentHeight = this.clientHeight + "px" //OK
startHeight = this.getAttribute( "start-height" )
opened = false
this.style.height = startHeight
}
}
document.getElementById( this.getAttribute( "trigger" ) ).onclick = toggle
function toggle() {
opened = !opened
that.style.height = ( opened )? contentHeight : startHeight
}
}
document.registerElement( "custom-expand-component", { prototype: CEC } )
The CSS transition is working now:
custom-expand-component {
display: block ;
overflow-y: scroll ;
transition: height 0.5s ;
}

Get the height of the next or previous hidden element to set height of the parent element. jquery

I have a ul which contains images which are hidden and then loaded into a carousel using absolute positioning.
The height of these images is constrained in desktop view so I can control the height of the parent element as there is a small toggle menu below the ul.
When I switch to mobile I am using media queries to set the width of the image to 100% and reset the height. This means that the position of my toggle menu has to be calculated each time the next or previous slide is swiped as the image heights vary.
I currently have a function which is returning the height of the current slide and using that to set the height of the element, however I need this function to calculate the height of the next image if the user swipes right, or the previous for left.
This is my code:
// check height of slider
var chSliderHeight = function(){
var slider = $('.slider');
var sliderImg = slider.find('li:visible img');
var sliderImgNext = sliderImg.parent().next().find('img').height();
var sliderImgPrev = sliderImg.parent().prev().find('img').height();
var sliderH = sliderImg.get(0).height;
slider.css('height',sliderH + 40);
console.log(sliderH, sliderImgNext, sliderImgPrev);
};
$(window).on('load resize orientationchange',function(){
chSliderHeight();
});
The variables sliderImgNext and sliderImgPrev look like they are returning the values of the previous or next slides, but I don't know how to trigger the css height rule inside the swipe function.
And before this I have my swipe functions:
images.wipetouch({
wipeLeft: function(result) {
target = $('ul.triggers li.selected').index();
if ( target === lastElem ) { target = 0; }
else { target = target+1; }
sliderResponseMobile(target);
chSliderHeight();
},
wipeRight: function() {
target = $('ul.triggers li.selected').index();
lastElem = triggers.length-1;
if ( target === 0 ) { target = lastElem; }
else { target = target-1; }
sliderResponseMobile(target);
chSliderHeight();
}
});
I need some condition to run a slightly different function or parameter if swiping left or right. Maybe this should run inside a success function, I am not 100% sure. Maybe hoisting?
I am finding it difficult to get around the issue of writing a function inside chSliderHeight which is available to wipeLeft and wipeRight functions.
I have a full fiddle with a single dependency (and all other code loaded) HERE
This also has all the other code and vars contained.
OK managed to get this working.
One issue I was having, because the slider uses a fadeIn/fadeOut function, calling slider.find('li:visible img'); is returning a length of 2 as there are two slides visible at the time of calling the function.
So I changed my selector to this:
slider.find('li[class="active"] img');
And the length is now 1.
I then wrote a callback function like this:
var callback = function(){
var slider = $('.slider');
var sliderImg = slider.find('li[class="active"] img');
var sliderH = sliderImg.get(0).height;
slider.css('height',sliderH + 40);
};
And passed the callback function into the wipetouch functions, like so:
images.wipetouch({
wipeLeft: function(result) {
target = $('ul.triggers li.selected').index();
if ( target === lastElem ) { target = 0; }
else { target = target+1; }
sliderResponseMobile(target);
callback();
},
wipeRight: function() {
target = $('ul.triggers li.selected').index();
lastElem = triggers.length-1;
if ( target === 0 ) { target = lastElem; }
else { target = target-1; }
sliderResponseMobile(target);
callback();
}
});
sliderResponseMobile(target); now looks like this (so it adds an active class to each slide):
function sliderResponseMobile(target) {
images.removeClass('active').fadeOut(200).eq(target).fadeIn(400).addClass('active');
}
And here is a working FIDDLE

Categories