I am having an issue with Bootstrap 4.1 modals displayed within iframes in Safari on iOS 12. Every other browser tested works as expected (even Safari on iOS 11). The issue seems to be specific to iOS 12.
I have created a minimal example demonstrating the issue. The first two buttons seem to function as expected, however the last 4 you can see the issue, with each one being worse as you traverse downward, where the last one disappearing all together when you attempt to scroll or focus on an element inside of the modal (see below screenshots):
I will note that we are handling this functionality in a way that may be a tad unorthodox, as instead of allowing the content of the iframe to scroll, we adjust it's height after it has loaded by passing messages between parent and child via a message
event handler and postMessage
: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
This is where I suspect something has gone afoul (but have yet been able to track it down (and as mentioned previously this is only an issue on ios devices running version 12)).
It has recently been discovered that this issue is not specific to Safari on iOS 12, but chrome as well.
Code below is from the previous minimal example link:
Parent (/modal-test/index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Title</title>
<link rel="stylesheet" href="./bootstrap.min.css">
<script src="./jquery.min.js"></script>
<script src="./popper.min.js"></script>
<script src="./bootstrap.min.js"></script>
<script>
$(document).ready(function(){
$ifCon = $("#ifCon");
window.addEventListener("message", function(event){
if(event.data.method === "returnWindowSize"){
$ifCon.height(event.data.content);
}
}, false);
});
</script>
<style>
#ifCon {
display: flex;
width: 100%;
height: 100%;
flex-direction: column;
background-color: #F2F2F2;
overflow: hidden;
border-radius:10px;
border:1px solid grey;
margin-left:auto;
margin-right:auto;
/*box-shadow:2px 2px 3px #000;*/
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.75);
}
#ifCon iframe {
flex-grow: 1;
border: none;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col text-center">
<div id="ifCon">
<iframe height="100%" width="100%" scrolling="no" src="/modal-test/frameable.html"></iframe>
</div>
</div>
</div>
</div>
</body>
</html>
Child (/modal-test/frameable.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Title</title>
<link rel="stylesheet" href="./bootstrap.min.css">
<script src="./jquery.min.js"></script>
<script src="./popper.min.js"></script>
<script src="./bootstrap.min.js"></script>
<script>
var $embedContent, modalState;
$(document).ready(function(){
$embedContent = $('#embedContent');
parent.postMessage({method:"returnWindowSize", content:$embedContent.height()}, '*');
$('.modal').on('shown.bs.modal', function(e){
modalState = {
id:$(this).attr('id'),
contentElement:$(this).find('.modal-content'),
initialContentContainerHeight:$embedContent.height(),
invokerElement:$(e.relatedTarget)
};
adjustModal();
});
$('.modal').on('hidden.bs.modal', function(e){
modalState = null;
$(this).find('.modal-content').css("margin-top", "0px");
$embedContent.css('height', 'auto');
parent.postMessage({method:"returnWindowSize", content:$embedContent.height()}, '*');
});
});
function adjustModal(){
if(modalState.contentElement.css('margin-top') !== modalState.invokerElement.offset().top){
modalState.contentElement.animate({'margin-top':modalState.invokerElement.offset().top}, 200, "linear");
}
if(
// modal position + modal height is greater than or equal to the height of the embedContent so we need to resize the
// embedContent (make it taller)
((modalState.invokerElement.offset().top + modalState.contentElement.height()) >= $embedContent.height()) ||
// modal position + modal height is less than or equal to the height of the embedContent AND the current height of the
// embedContent is greater than or equal to the size of the embedContent height when the modal was originally shown
(((modalState.invokerElement.offset().top + modalState.contentElement.height()) <= $embedContent.height()) &&
($embedContent.height() > modalState.initialContentContainerHeight))
){
var newEmbedContentHeight = modalState.invokerElement.offset().top + modalState.contentElement.height() + 30;
$embedContent.height(newEmbedContentHeight);
parent.postMessage({method:"returnWindowSize", content:newEmbedContentHeight}, '*');
}
}
</script>
</head>
<body>
<div id="embedContent" class="container">
<div class="row" style="height:200px;margin-top:100px;">
<div class="col text-center">
<button id="btn1" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
<div class="row" style="height:200px;">
<div class="col text-center">
<button id="btn2" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
<div class="row" style="height:200px;">
<div class="col text-center">
<button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
<div class="row" style="height:200px;">
<div class="col text-center">
<button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
<div class="row" style="height:200px;">
<div class="col text-center">
<button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
<div class="row" style="height:200px;">
<div class="col text-center">
<button id="btn3" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
</div>
</div>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog"
aria-labelledby="exampleModalLabel" aria-hidden="true" data-focus="false" style="overflow-y:hidden;">
<div class="modal-dialog" role="document">
<div class="modal-content" style="margin-top:500px;">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="exampleFormControlInput1">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="[email protected]">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="[email protected]">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select class="form-control" id="exampleFormControlSelect1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select class="form-control" id="exampleFormControlSelect1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select class="form-control" id="exampleFormControlSelect1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select class="form-control" id="exampleFormControlSelect1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</body>
</html>
The solution to this ended up being that we needed to force hardware acceleration by including the following css in the <head></head>
of the child document (element within the iframe (frameable.html in our case)):
<style>
body{
transform: translate3d(0,0,0);
}
</style>
I however cannot explain why this resolves the issue, or why it's required for ios12 as opposed to ios11. If anyone else can shed some light onto that topic, I'm sure it would help someone else down the road.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With