Touch events within iFrame are not working on iOS - javascript

Touch events like touchstart or touchend are not fired when attached to the window inside an IFrame on iOS devices.
Here's a very simple example:
parent.html
<!DOCTYPE HTML>
<html style="height: 100%;">
<head>
<meta charset="UTF-8">
<title>Touch Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body style="height: 100%; margin: 0; overflow: hidden;">
<iframe style="width: 100%; height: 100%; border: none;" src="child.html"></iframe>
</body>
</html>
child.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Touch Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
padding: 8px;
}
div.header {
margin-bottom: 8px;
}
div.text-entry {
font: 300 1rem/1.25 'Open Sans', 'Helvetica Neue', helvetica, arial, sans-serif;
color: rgb(64, 64, 64);
}
</style>
<script>
window.onload = function() {
function addEvent(event) {
var div = document.createElement('div');
div.className = 'text-entry';
div.textContent = new Date().toLocaleTimeString() + ' Event "' + event.type + '" detected';
document.body.appendChild(div);
}
window.addEventListener('touchstart', addEvent.bind(null), false);
window.addEventListener('touchend', addEvent.bind(null), false);
}
</script>
</head>
<body>
<div class="text-entry header">Clicks/touches on the viewport should add some text entries...</div>
</body>
</html>
I've found multiple questions regarding scroll issues on iOS within IFrames and some regarding events, but none seems to have a valid solution for the issue I'm experiencing right now.
I've created a CodePen and a JSFiddle for everyone to play around which show the exact same behavior since both execute the code within an IFrame.

Solution 1:
Inside the iframe, add a dummy listener to the document object:
document.addEventListener('touchstart', {}); // in iframe
It seems Safari on IOS denies touch listeners to window unless other DOM objects also have listeners.
Solution 2:
Inside the top window, add a dummy listener to any object (including window):
window.addEventListener('touchstart', {}); // in top window
It seems Safari on IOS denies touch listeners to window in iframes unless the parent also has listeners.
Either of the above solutions works (they're not both needed, only one).
Tested on:
Safari 9.0 / IOS: 9.3.5
Safari 11.0 / IOS: 11.3

Adding following CSS to the IFrame content (child.html in my example above) solved the issue for me:
html, body {
touch-action: auto;
}

Related

How to change canvas style on tsParticles.js

I'm trying to change position and dimensions of the canvas but it's set as inline code with !important added, editing style attribute is instantly changed to original styling.
There is fullscreen option but it doesn't change anything for me: docs.
I've spend a lot of time trying to change it with no luck.
Simple example showing issue:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>tsParticles</title>
<style>
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #212121;
}
#tsparticles {
width: 200px;
height: 200px;
position: absolute;
}
</style>
</head>
<body>
<div id="tsparticles"></div>
<script
src="https://cdn.jsdelivr.net/npm/tsparticles-preset-bubbles#2/tsparticles.preset.bubbles.bundle.min.js"></script>
<script>
(async () => {
await loadBubblesPreset(tsParticles); // this is required only if you are not using the bundle script
await tsParticles.load("tsparticles", {
autoPlay: false, //works
// FullScreen: false, //nothing changes
FullScreen: { enable: false, zIndex: 99 }, ////nothing changes
preset: "bubbles",
});
})();
</script>
</body>
</html>
`
I've tried changing inline styling and find a way to set it in config. Version v1 was sized according to its parent. With version v2 I'm stuck and out of ideas.
The property for disabling the full screen options is fullScreen, not FullScreen. The options are case sensitive.

Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element' I am getting this error

I am new to Javascript and I can't figure out why am I getting this error.
Here is my code:
const bckg = document.querySelector(".bckg");
const compStyle = getComputedStyle(bckg);
body {
box-sizing: content-box;
position: relative;
}
.visible {
position: relative;
top: 50px;
margin: auto;
background-color: bisque;
height: 300px;
width: 600px;
overflow: hidden;
}
.bckg {
position: absolute;
left: 0px;
width: 1200px;
height: 300px;
background-image: url("bckg.jpg")
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<script src="script.js"></script>
<div class="visible">
<div class="bckg" id="bckg">
<div class="player"></div>
<div class="obstacle"></div>
</div>
</div>
</body>
</html>
This error I get in Mozilla: "Uncaught TypeError: Window.getComputedStyle: Argument 1 is not an object."
What could it be?
Your script tag needs to be moved to the very end of the body. The script loaded to early.
This error I get in Mozilla: "Uncaught TypeError: Window.getComputedStyle: Argument 1 is not an object." What could it be?
If you read the error message it says it could not find the argument.
The reason for this is that browsers loads JavaScript tags synchronously. It has been a standard since JavaScript was first introduced.
Solution snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="visible">
<div class="bckg" id="bckg">
<div class="player"></div>
<div class="obstacle">
</div>
</div>
</div>
<!-- I moved the script to the end of the body -->
<script src="script.js"></script>
</body>
</html>
Adding a console.log you could have seen that the argument for Window.getComputedStyle is indeed not an object
const bckg = document.querySelector(".bckg");
console.log(bckg); // will return undefined
You can also try out the "defer" attribute, however it has been best practice to move the script tags to the end of the body for compatibility with different older browsers. See https://www.w3schools.com/tags/att_script_defer.asp
Another alternative is to use the "DOMContentLoaded" event
https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event
document.addEventListener("DOMContentLoaded", function() {
// code...
});
Delta Boukensha is right - your document hasn't finished loading.
So either put your script at the bottom of your markup as Delta suggests or use a utility function to run code once the document has rendered such as :
function whenDOMready( func ) {
switch( String( document.readyState ) ) {
case "complete":
case "loaded":
case "interactive":
func();
break;
default:
window.addEventListener("DOMContentLoaded", (e) => func());
}
}
Then call it from wherever you want like this :
let bckg;
let compStyle;
function domReady() {
bckg = document.querySelector(".bckg");
compStyle = getComputedStyle(bckg);
/* ...other code here */
}
whenDOMready( domReady );
There are two benefits this utility gives you :
No matter when you call it it will work - either calling your function (func) immediately or when the DOMContentLoaded event fires. If you just add the event listener directly you have to worry about if it has already fired or not.
You are guaranteed that your DOM is ready for inspection and access.

scope issue with iframe

I'm having a page with some inline css & javascript, the javascript on the original page contains some click and scroll logic which looks like this
$('#abc').on('click', function() {
$('html, body').scrollTop($('#xyz').offset().top)
});
it works fine on the original page;
Now I'm having a new page, which imports the original page as an iframe on the new page, but because it is iframe, the scope of the javascript code on original page inside this iframe is now bind to the iframe itself, and because it's bind to iframe itself, the $('html, body').scrollTop no longer works...
Is there anyway I can modify the original page to make it work through iframe?
for obvious security reasons, iframes are isolated in their own sandbox.
however, if you are in control of the code of the parent page and the iframe page, then you can use the message mechanism and pass your information through the event.data part.
here the example only passes text, for more extensive data, use JSON.stringify () / JSON.parse ()
file z1.html (parent)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Z1</title>
<style>
#iFrameZ2 { width: 400px; height: 150px; border: 1px solid red; }
#receiveTxt { margin: .5em; border: 1px solid blue; padding: .2em; width: 15em; }
</style>
</head>
<body>
<h4>main page (z1.html)</h4>
<input type="text" id="messageTxt" placeholder="a message...">
<button id="btSender">send to iframe</button>
<div id="receiveTxt">.</div>
<br> <br> iframe:<br>
<iframe id="iFrameZ2" src="z2.html" frameborder="0"></iframe>
<script>
window.onmessage = e =>
{
receiveTxt.textContent = e.data
/* ----------------------
if (e.data==='scroll')
{
document.querySelector('#xyz').scrollTop
}
------------------ */
}
btSender.onclick = _ =>
{
let info = messageTxt.value.trim()
if (info!='')
iFrameZ2.contentWindow.postMessage( info,'*')
}
</script>
</body>
</html>
file z2.html (iframe)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h6>z2.html</h6>
<input id="msgTxt" type="text" placeholder="send a message">
<button id="btSender">send msg</button>
<p id="getMsg">...</p>
<script>
btSender.onclick=_=>
{
let info = msgTxt.value.trim()
if (info!='')
window.parent.postMessage(info,'*')
}
window.onmessage = e =>
{
getMsg.textContent = e.data
}
</script>
</body>
</html>

Alternatives to resizing background images

I am creating a website that has this kind of structure:
Where the red box represents the user's browser window. When the user clicks a button on the home (bottom), it slides up to the new scene (stratosphere for example). Each scene is an entire image. Now the problem is, I need to account for users using different screen sizes and when they resize the window. I've looked up ways to resize backgrounds images using CSS or JavaScript, and that doesn't work well for me. I need to find some way to make them all fit for everyone using different screen sizes. An idea I have - I know this sounds clunky but would it be viable to write a PHP script which resizes an image to the dimension given by the JS? JS finds the browser window's size, hands it to PHP, PHP returns the image JS needs. And have this happen when a user resizes the browser window too...
How can I do this?
Update:
I tried SVG, and it's working beautifully. But now I am wondering how I can get the other elements to be in accordance with the SVG?
Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>untitled</title>
<meta name="generator" content="TextMate http://macromates.com/">
<!-- Date: 2012-08-01 -->
<style type="text/css" media="screen">
body { margin: 0px; }
.area { border: 3px solid red; background: green; margin-bottom: 0px; background: url(http://www.alistapart.com/d/using-svg-for-flexible-scalable-and-fun-backgrounds-part-ii/beetle.svg) no-repeat; }
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
function scroll_to(id, speed, margin) {
$('html, body').animate({
scrollTop: $('#' + id).offset().top - margin
}, speed);
}
var slide = 'a3'
$(".area").height($(window).height());
$(window).resize(function() {
$(".area").height($(window).height());
$(".area").width($(window).width());
scroll_to(slide, 1, 0);
});
scroll_to('a2', 'slow', 0);
});
</script>
</head>
<body>
<div class="area" id="a3">
<h1>scene 3</h1>
</div>
<div class="area" id="a2">
<h1>scene 2</h1>
<div style="height: 100px; border: 1px solid black;" id="text">
hi
</div>
</div>
<div class="area" id="a1">
<h1>scene 1</h1>
</div>
</body>
</html>
Why don't you use an SVG as background image? Your scene seems fairly simple.
All browser but IE ≤ 8 understand background: url(some.svg): http://caniuse.com/svg-css
Use one large background-image. Set it up with something like this:
html, body {
margin: 0;
padding: 0;
width: 100%
}
body {
position: absolute;
bottom: 0;
height: 2000px;
background-image: url('background.png')
}
Then use JavaScript to set the bottom property of body to move up, like this:
window.addEventListener('keydown', keypressed, false);
function keypressed(e) {
if(String.fromCharCode(e.charCode) == ' ') {
document.body.style.bottom += parseInt(document.body.style.bottom) + 10 + 'px';
}
}
I'm afraid re-sizing the background image is going to be your best bet. Why don't you post the code you've already tried for such a solution and others can help you along from that angle.
You should definitely split the image into multiple images, one for each tab.
This is how I would do it: http://jsfiddle.net/4CwdX/3/
You don't need to resize the image. The browser can automatically stretch it for you with background-size: 100%.

OnDrop Event never fired for Safari

I am building a website specifically for the iPad. I am attempting to get drag & drop to work but the ondrop event seems to never fire for some reason.
Can you help me get the div to detect when I drop an element on top of it?
I have a very simple HTML page that demonstrates that the ondrop event is never fired in Safari. This fails in both desktop safari & ipad safari.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> </title>
<style type="text/css">
<!--
body {
margin: 100px;
}
.dropTarget {
width: 300px;
height: 100px;
float: left;
display: inline;
margin: 10px;
background-color: green;
-webkit-user-drop: element;
}
.drag {
width: 100px;
height: 100px;
background-color: red;
-webkit-user-drag: element; /*element*/
}
-->
</style>
<script type="text/javascript">
<!--
function onDrop(e, ele) { alert("drop"); }
-->
</script>
</head>
<body>
<div class="drag">
</div>
<br/>
<div class="dropTarget" ondrop="onDrop(e,this);">
</div>
<br/>
</body>
</html>
You need to cancel the dragover event's default action before you can use the drop event for something, otherwise the browser ignores it.
Change your dropTarget <div> from this:
<div class="dropTarget" ondrop="onDrop(e,this);">
To this:
<div class="dropTarget" ondragover="return false;" ondrop="onDrop(event,this);">
If you use inline event registration you have to use event to pass the event instead of e.
Another reason might be:
In reference with the drag-and-drop-processing-model, the drop event surprisingly does not get fired if the platform is changing the e.dataTransfer.dropEffect which (preferably should match) is not matching with e.dataTransfer.effectAllowed property of the event.

Categories