Chrome blocks animations inside iframe that is not in viewport - css

So we have a product that contains multiple videos with progressbars (think of it like an instagram story)
This product is placed inside iframe on partner websites. The behaviour of this "story" is to play by default. The progressbars are animated with css.
The problem is that if the iframe is outside of viewport, the video will start playing but the progressbar will start animating only after the iframe enters the viewport
I replicated the issue here: https://codepen.io/capraruioan/pen/VwXXNNz
hit the "reload iframe" button, wait a few seconds and scroll down and you will see the progressbar will start only when the iframe entered the viewport
Here is the code:
<button onclick="resetIframe()">reload iframe</button>
scroll down to see animations
<iframe id="iframe" style="margin-top: 110vh; height: 100px; display: block;" src="https://clipr.opsolutions.ro/animation.html" frameborder="0"></iframe>
and js
var url = "https://clipr.opsolutions.ro/animation.html"
function resetIframe() {
iframe.src = ""
window.setTimeout(() => {
iframe.src = url
console.log("done")
}, 100)
}
I cannot find anything on webkit docs about this or how to avoid/bypass it

Related

Remove Scrollsbar from Widget

I have a website where I use an Eventbrite Widget to checkout a page on my website.
It turns out that when running the script, the iframe, especially on cell phones, does not show the entire checkout. Scrollsbar appears to scroll and see some information.
It turns out that I would like to remove these scrollsbars, because some users don't bother to scroll them down 😱 and end up not filling in some data and not being able to proceed with the purchase.
The code I have is this:
I tried a height on the div, but it didn't work.
type her<div id="eventbrite-widget-container-489731288460" style="display: block; height: 100%;"></div>
<p>
<script src="https://www.eventbrite.com.br/static/widgets/eb_widgets.js"></script>
<script type="text/javascript">
var exampleCallback = function() {
console.log('Order complete!');
};
window.EBWidgets.createWidget({
// Required
widgetType: 'checkout',
eventId: '489731288460',
iframeContainerId: 'eventbrite-widget-container-489731288460',
// Optional
iframeContainerHeight: 900, // Widget height in pixels. Defaults to a minimum of 425px if not provided
onOrderComplete: exampleCallback // Method called when an order has successfully completed
});
</script>e
I also put the iframeContainerHeight: 900, but on the cell phone the scrollbar continues to appear
If anyone knows how to solve it, I'm grateful.
Remove eventbrite iframe scrollbar

Ipad Safari - Can't scroll page if scrolling inside iframe

Is it possible to continue scrolling through a webpage even if you are touching inside an iframe? This problem only happens with iOS devices and I couldn't find any solutions for this!
My current page contains an iframe in the middle with width:95% and about 500px height, so when I reach the iframe I can't scroll any more (unless I touch very close to the sides).
Thanks
In my case, I had full access to the iframe and was dynamically inserting its content. Still, none of the solutions suggested by Brandon S worked. My solution:
Create a transparent div overlaying the iframe.
Capture any click events on the overlay and replicate them within the iframe (to allow the user to click on links/buttons)
This works because the overlaying div is part of the outer document, making it respond to touch/click events normally, and prevents the user from directly interacting with the iframe content.
Html Template:
<div style="position: relative;">
<div
style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; opacity: 0;"
ng-click="$ctrl.handleOverlayClick($event)"
></div>
</div>
Controller (AngularJS component)
...
constructor ($document, $element) {
this.iframe = $document[0].createElement('iframe');
this.iframe.width = '100%';
this.iframe.height = '100';
this.iframe.sandbox = 'allow-same-origin allow-scripts allow-popups allow-forms allow-top-navigation';
const element = $element[0].children.item(0);
element.appendChild(this.iframe);
this.contentDocument = this.iframe.contentDocument;
}
handleOverlayClick ($event) {
// Overlay element is an invisible layer on top of the iframe. We use this to
// capture scroll events which would be in the iframe (which don't work properly on iPad Safari)
// When a click is detected, we propigate that through to the iframe so the user can click on links
const rect = $event.target.getBoundingClientRect();
const x = $event.clientX - rect.left; // x position within the iframe
const y = $event.clientY - rect.top; // y position within the iframe
// triggering click on underlaying element
const clickedElement = this.contentDocument.elementFromPoint(x, y);
clickedElement && clickedElement.click();
}
It sounds like the iframe is receiving the user's scroll event, instead of the page. This can happen when part of the iframe's content doesn't fit within the size of the iframe element.
A solution to this problem is to stop the iframe from ever trying to scroll. There are few ways to accomplish this:
In iframe's HTML, add this CSS:
html, body {
overflow: hidden
}
If you don't have access to the iframe's HTML (because maybe the iframe is loading a 3rd party's content), you can put a wrapper div around the iframe and disable scrolling that way. Add this to the parent page HTML:
<div style="overflow: hidden"><iframe src="example.com"></iframe></div>
You can add this to the parent page HTML CSS to make browser use momentum so that ends up scrolling past the bottom of the iframe and then scrolls the page:
*{
-webkit-overflow-scrolling: touch
}
Add the legacy "scrolling" attribute to the iframe to stop the iframe from trying to scroll:
<iframe src="example.com" scrolling="no"></iframe>

Bootstrap modal at top of iframe regardless of scroll position. How do I position it on screen?

When embedding a Bootstrap app in an iframe, modal dialogs always open at the top of the iframe, not the top of the screen. As an example, go to http://getbootstrap.com/javascript/ and open an example modal on the page. Then using the sample code below which places the same bootstrap page in an iframe, find a modal and open it:
<html>
<head>
</head>
<body>
<table width="100%">
<tr><td colspan="2" style="height:80px;background-color:red;vertical-align:top">Here's some header content I don't control</td></tr>
<tr><td style="width:230px;height:10080px;background-color:red;vertical-align:top">Here's some sidebar content I don't control either</td>
<td valign="top">
<iframe width="100%" height="10000px"
scrolling="no" src="http://getbootstrap.com/javascript/">
</iframe>
</td>
</tr>
</table>
</body>
</html>
Demo in fiddle
How do I go about positioning the modal on the screen in this scenario?
UPDATE: Unfortunately, my iFrame cannot fill the screen, nor can I make it fixed since it needs to blend into the rest of the page and the page itself has enough content to scroll. This is not my design and I ultimately intend to rework the whole thing, but this is what I have to work around for now. As a temporary option, I'm using javascript to tell the iframe parent to scroll to the top where the modal dialog pops up. While this is acceptable, this isn't the desired behavior.
I'm using angularjs and the ui-bootstrap library in my code but as you can see above, it's a bootstrap issue.
If your iframe has the same document.domain as the parent window or it is a sub domain, you can use the code below inside the iframe:
$('#myModal').on('show.bs.modal', function (e) {
if (window.top.document.querySelector('iframe')) {
$('#myModal').css('top', window.top.scrollY); //set modal position
}
});
show.bs.modal will fire after you call $('#myModal').show()
window.top.scrollY will get the scrollY position from the parent window
In case your document.domain differs from the parent, you can hack it getting the onmousedown position inside the iframe. For example:
$('#htmlElement').on('mousedown', function (event) {
event.preventDefault();
$('#myModal').data('y', event.pageY); // store the mouseY position
$('#myModal').modal('show');
});
$('#myModal').on('show.bs.modal', function (e) {
var y = $('#myModal').data('y'); // gets the mouseY position
$('#myModal').css('top', y);
});
Quite old question but I don't see the solution/workaround I've found. It might be helpful for someone in the future.
I had the same issue - my iFrame doesn't fill the entire screen, it displays bootstrap's modal and it is loading content from different domain than the parent page.
TL;DR
Use window.postMessage() API - Documentation here. for communication between parent and iframe (cross-domain)
pass message with currentScrollPosition and Y position of your iframe
Reveive message and update modal's padding from the top
In my case the workaround was to use window.postMessage() API - Documentation here.
It requires to add some extra code to the parent and handle message in an iFrame.
You can add EventListener and listen to 'scroll' event. Each time the event handling function is invoked you can get currentScrollPosition like document.scrollingElement.scrollTop.
Keep in mind that your iframe can have some margin from the top in the parent page so you should also get its 'offset'.
After that you can post these two values to your iframe e.g. ncp_iframe.contentWindow.postMessage(message, '*');
Note that the message has to be a String value
After that in your iFrame you need to add EventListener and listen to 'message' event.
The event handling function will pass your 'message' in event.data property. Having that you can update modal padding. (Looks much better if you don't use animations e.g. fade, in classes);
Quick Example:
Parent:
window.addEventListener('scroll', function(event){
var myIframe = document.querySelector('#myIframe');
var topOffset = myIframe.getBoundingClientRect().top + window.scrollY;
var currentScroll = document.scrollingElement.scrollTop;
myIframe.contentWindow.postMessage(topOffset + ':' + currentScroll, '*');
});
iFrame:
window.addEventListener('message', function(event) {
var messageContent = event.data.split(':');
var topOffset = messageContent[0];
var currentScroll = messageContent[1];
//calculate padding value and update the modal top-padding
}, false);
This is a bug in most browsers (IE appears fine) where the elements are fixed to the iframe, not the window. Typically, if you need something to be relative to the main document, it has to be in the main document.
A workaround is to wrap your iframe in a fixed position div that takes up the whole width of the screen and then maximize the iframe within that. This appears to resolve the issue
HTML:
<div class="fixframe">
<iframe src="http://getbootstrap.com/javascript/"></iframe>
</div>
CSS:
.fixframe {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}
.fixframe iframe {
height: 100%;
width: 100%;
}
Working Demo in fiddle
See Also:
position:fixed inside of an iframe
iframe with a css fixed position : possible?
position fixed div in iframe not working
It is because the class .modal has position: fixed attribute. Try position: relative instead.

Hover effect isn't working fine

How do i load the original image so that when the user brings the cursor onto top of the image, it should change automatically without showing white background then loading the original pic? Is there any code that loads the original image wheh my webpage loads? Please let me know. my code is :
#middlefoto{
background-image:url(../images/middleblack.jpg);
margin-left:1px;
height:158px;
width:333px;
}
#middlefoto:hover{
background:#fff url(../images/middlecolor.jpg) 0 0 no-repeat;
}
Use sprites with positioning.
Find more information at W3 Schools
The reason you are seeing the blank background for an instant is because the hover image has not yet been loaded from the server. To avoid this, preload the images. There are several ways to do this but the concept is the same: force the browser to load the image before it is actually needed. Here's a simple way to do this using JavaScript:
function preloadImages(sources)
{
var img = new Image();
for (var i = 0; i < sources.length; i++) {
img.src = sources[i];
}
}
preloadImages([ '../images/middlecolor.jpg', 'image2.jpg', 'image3.jpg' ]);
Include the image in an off-screen element (push it off screen with CSS). This will cause the browser to download the image so it should be ready for the rollover. You could clean up the offscreen images after page load.
<img src="rollover image" class="preloader" style="position:absolute; margin-left:-99999px" />
(don't really use inline styles)
Then, if you're using jquery
$(document).ready(function(){ $('.preloader').remove(); });
to clean up.

how to properly display an iFrame in mobile safari

I'm trying to display an iframe in my mobile web application, but I'm having trouble restricting the size of the iframe to the dimensions of the iPhone screen. The height and width attributes on the iframe element seem to have no effect, strangely. Surrounding it with a div manages to constrain it, but then I'm unable to scroll within the iframe.
Has anyone tackled iframes in mobile safari before? Any ideas where to start?
Yeah, you can't constrain the iframe itself with height and width. You should put a div around it. If you control the content in the iframe, you can put some JS within the iframe content that will tell the parent to scroll the div when the touch event is received.
like this:
The JS:
setTimeout(function () {
var startY = 0;
var startX = 0;
var b = document.body;
b.addEventListener('touchstart', function (event) {
parent.window.scrollTo(0, 1);
startY = event.targetTouches[0].pageY;
startX = event.targetTouches[0].pageX;
});
b.addEventListener('touchmove', function (event) {
event.preventDefault();
var posy = event.targetTouches[0].pageY;
var h = parent.document.getElementById("scroller");
var sty = h.scrollTop;
var posx = event.targetTouches[0].pageX;
var stx = h.scrollLeft;
h.scrollTop = sty - (posy - startY);
h.scrollLeft = stx - (posx - startX);
startY = posy;
startX = posx;
});
}, 1000);
The HTML:
<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" id="iframe" src="url" />
</div>
If you don't control the iframe content, you can use an overlay over the iframe in a similar manner, but then you can't interact with the iframe contents other than to scroll it - so you can't, for example, click links in the iframe.
It used to be that you could use two fingers to scroll within an iframe, but that doesn't work anymore.
Update: iOS 6 broke this solution for us. I've been attempting to get a new fix for it, but nothing has worked yet. In addition, it is no longer possible to debug javascript on the device since they introduced Remote Web Inspector, which requires a Mac to use.
If the iFrame content is not yours then the solution below will not work.
With Android all you need to do is to surround the iframe with a DIV and set the height on the div to document.documentElement.clientHeight. IOS, however, is a different animal. Although I have not yet tried Sharon's solution it does seem like a good solution. I did find a simpler solution but it only works with IOS 5.+.
Surround your iframe element with a DIV (lets call it scroller), set the height of the DIV and make sure that the new DIV has the following styling:
$('#scroller').css({'overflow' : 'auto', '-webkit-overflow-scrolling' : 'touch'});
This alone will work but you will notice that in most implementations the content in the iframe goes blank when scrolling and is basically rendered useless. My understanding is that this behavior has been reported as a bug to Apple as early as iOS 5.0. To get around that problem, find the body element in the iframe and add -webkit-transform', 'translate3d(0, 0, 0) like so:
$('#contentIframe').contents().find('body').css('-webkit-transform', 'translate3d(0, 0, 0)');
If your app or iframe is heavy on memory usage you might get a hitchy scroll for which you might need to use Sharon's solution.
This only works if you control both the outside page and the iframe page.
On the outside page, make the iframe unscrollable.
<iframe src="" height=200 scrolling=no></iframe>
On the iframe page, add this.
<!doctype html>
...
<style>
html, body {height:100%; overflow:hidden}
body {overflow:auto; -webkit-overflow-scrolling:touch}
</style>
This works because modern browsers uses html to determine the height, so we just give that a fixed height and turn the body into a scrollable node.
I have put #Sharon's code together into the following, which works for me on the iPad with two-finger scrolling. The only thing you should have to change to get it working is the src attribute on the iframe (I used a PDF document).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Pdf Scrolling in mobile Safari</title>
</head>
<body>
<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" id="iframe" src="data/testdocument.pdf" />
</div>
<script type="text/javascript">
setTimeout(function () {
var startY = 0;
var startX = 0;
var b = document.body;
b.addEventListener('touchstart', function (event) {
parent.window.scrollTo(0, 1);
startY = event.targetTouches[0].pageY;
startX = event.targetTouches[0].pageX;
});
b.addEventListener('touchmove', function (event) {
event.preventDefault();
var posy = event.targetTouches[0].pageY;
var h = parent.document.getElementById("scroller");
var sty = h.scrollTop;
var posx = event.targetTouches[0].pageX;
var stx = h.scrollLeft;
h.scrollTop = sty - (posy - startY);
h.scrollLeft = stx - (posx - startX);
startY = posy;
startX = posx;
});
}, 1000);
</script>
</body>
</html>
<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" src="url" />
</div>
I'm building my first site and this helped me get this working for all sites that I use iframe embededding for.
Thanks!
Sharon's method worked for me, however when a link in the iframe is followed and then the browser back button is pressed, the cached version of the page is loaded and the iframe is no longer scrollable. To overcome this I used some code to refresh the page as follows:
if ('ontouchstart' in document.documentElement)
{
document.getElementById('Scrolling').src = document.getElementById('SCrolling').src;
}
I implemented the following and it works well. Basically, I set the body dimensions according to the size of the iFrame content. It does mean that our non-iFrame menu can be scrolled off the screen, but otherwise, this makes our sites functional with iPad and iPhone. "workbox" is the ID of our iFrame.
// Configure for scrolling peculiarities of iPad and iPhone
if (navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('iPad') != -1)
{
document.body.style.width = "100%";
document.body.style.height = "100%";
$("#workbox").load(function (){ // Wait until iFrame content is loaded before checking dimensions of the content
iframeWidth = $("#workbox").contents().width();
if (iframeWidth > 400)
document.body.style.width = (iframeWidth + 182) + 'px';
iframeHeight = $("#workbox").contents().height();
if (iframeHeight>200)
document.body.style.height = iframeHeight + 'px';
});
}
Purely using MSchimpf and Ahmad's code, I made adjustments so I could have the iframe within a div, therefore keeping a header and footer for back button and branding on my page. Updated code:
<script type="text/javascript">
$("#webview").bind('pagebeforeshow', function(event){
$("#iframe").attr('src',cwebview);
});
if (navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('iPad') != -1)
{
$("#webview-content").css("width","100%");
$("#webview-content").css("height","100%");
$("#iframe").load(function (){ // Wait until iFrame content is loaded before checking dimensions of the content
iframeWidth = $("#iframe").contents().width();
if (iframeWidth > 400)
$("#webview-content").css("width",(iframeWidth + 182) + 'px');
iframeHeight = $("#iframe").contents().height();
if (iframeHeight>200)
$("#webview-content").css("height",iframeHeight + 'px');
});
}
</script>
and the html
<div class="header" data-role="header" data-position="fixed">
</div>
<div id="webview-content" data-role="content" style="height:380px;">
<iframe id="iframe"></iframe>
</div><!-- /content -->
<div class="footer" data-role="footer" data-position="fixed">
</div><!-- /footer -->
Don't scroll the IFrame page or its content, scroll the parent page. If you control the IFrame content, you can use the iframe-resizer library to turn the iframe element itself into a proper block level element, with a natural/correct/native height. Also, don't attempt to position (fixed, absolute) your iframe in the parent page, or present an iframe in a modal window, especially if it has form elements.
I also suspect that iOS Safari has a non-standards behavior that expands your iframe's height to its natural height, much like the iframe-resizer library will do for desktop browsers, which seem to render responsive iframe content at height 0px or 150px or some other not useful default. If you need to contrain width, try a max-width style inside the iframe.
The solution is to use Scrolling="no" on the iframe.
That's it.

Resources