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.
Related
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>
I have a table that handles mouse clicks using onclick for the entire row. The problem is that these rows also contain checkboxes that should not be activating the onclick event. Clicking on a row should cause an event but selecting a checkbox should not.
I found a lot of answers to this but I've been unable to make it work. I'm using simple html/css/js without anything like jQuery.
The answer seems to be due to event propagation and lots of answers say to use stopPropagation(). But Chrome says this function undefined for the event.
Here is my test example.
Clicking on the table results in a "table!" alert. I don't want clicks on the checkbox within the table to cause this alert.
function clickTable(e) {
alert('table!');
}
function clickCheckbox(e) {
// Error: e.stopPropagation is not a function
e.stopPropagation();
}
table {
border: 1px solid black;
width: 120px;
height: 120px;
}
input {
width: 32px;
height: 32px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox Click</title>
</head>
<body>
<table>
<tr onclick="clickTable(this);">
<th><label>
<input type="checkbox" name="test" onclick="clickCheckbox(this);">
</label></th>
</tr>
</table>
<script>
</script>
</body>
</html>
I can however use window.event.stopPropagation(); which works as expected. But my IDE PhpStorm reports that using window.event in this way is deprecated.
What is the correct use of stopPropagation() for this event?
You should use Event delegation where you will get clicked element in table using event.target and do stuff accordingly.
document.querySelector("table").addEventListener("click", function(event) {
event.target ? console.log(event.target.nodeName) : console.log(event.target.nodeName)
});
table {
border: 1px solid black;
width: 120px;
height: 120px;
}
input {
width: 32px;
height: 32px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox Click</title>
</head>
<body>
<table>
<tr>
<th><label>
<input type="checkbox" name="test">
</label></th>
</tr>
</table>
<script>
</script>
</body>
</html>
What is DOM Event delegation?
.stopPropagation() is the correct method, but in order to use it, you must call it on the event object reference that is passed to your event handler. This object reference is automatically passed to DOM event handlers that are set up using the .addEventListener() method and not using the legacy onXyz inline event attributes (which should not be used in today's code).
And, when setting up DOM event handlers using .addEventListener(), there's no need to pass this as this will automatically be bound to the element that triggered the event in the first place, so you can just use this in your callback functions without passing it.
Additionally, your HTML is not valid. <th> elements belong inside of <thead> elements, not <tr> elements.
So, just rework the event handler setup:
table {
border: 1px solid black;
width: 120px;
height: 120px;
}
input {
width: 32px;
height: 32px;
}
<table>
<thead>
<th>
<label>
<input type="checkbox" name="test">
</label>
</th>
</thead>
</table>
<script>
// Do the event binding in JavaScript, not in HTML
document.querySelector("input[name='test']").addEventListener("click", clickCheckbox);
document.querySelector("thead").addEventListener("click", clickTable);
function clickTable(e) {
alert('table!');
}
function clickCheckbox(e) {
// Now, e will properly recieve the event reference
e.stopPropagation();
}
</script>
Inline event listeners do not pass the event object to the function.
Therefore, the function doesn't receive the event object(e) argument, preventing you from invoking the stopPropagation() method.
That is why I would also advice you avoid inline event listeners.
If you need the element that invoked the event, rather than using this you can use e.target to get the event's targeted element
function clickTable(e) {
alert('table!');
}
function clickCheckbox(e) {
e.stopPropagation();
}
const tr = document.querySelector('tr');
const input = document.querySelector('input[type=checkbox]');
tr.addEventListener('click', clickTable);
input.addEventListener('click', clickCheckbox);
table {
border: 1px solid black;
width: 120px;
height: 120px;
}
input {
width: 32px;
height: 32px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox Click</title>
</head>
<body>
<table>
<tr>
<th><label>
<input type="checkbox" name="test">
</label></th>
</tr>
</table>
<script>
</script>
</body>
</html>
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;
}
I have got the problem, that a window with the file content pops up though e.stopPropagation() is called. When I drag the file from the desktop in the div in my browser, my current tab is reloaded with the new content (pictures, text). As a browser I am using Chrome, but I have also tried it with Safari and Firefox!
I simply don't get it, here's my code:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>File Uploader</title>
<link href="../styles/allgemein.css" rel="stylesheet" type="text/css"/>
<link href='http://fonts.googleapis.com/css?family=Prosto+One' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css"/>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<!--<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">//-->
<!--<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap-theme.min.css">//-->
<!--<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>//-->
</head>
<body style="position:relative;">
<script>
var dropZone = document.getElementById('drag_upload');
dropZone.addEventListener('drop', handleDrop, false);
</script>
<script>
$(document).ready(function () {
$("#home").button();
$("#frm_logout").button();
});
function handleDrop(e) {
e.stopPropagation(); // Stops some browsers from redirecting.
e.preventDefault();
var files = e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
alert(files[i]);
}
}
</script>
<div style="width:100%; text-align:center; font-family: Arial; font-size:28px;"><b>Cloudservice</b></div>
<form action="../" method="post">
<input type="submit" name="logout" value="Logout" id="frm_logout" style="z-index: 1000; float:right;"/>
</form>
<input type="button" value="Home" id="home"/>
<div style="width:100%; font-size:18px; text-align:left; font-family:Arial; position:absolute; left:5px; top:100px;">
<?php echo "User: " . $user . "<br/>Maximum <br/>upload size: " . $size . " MB<br/><br/>"; ?>
<a href="fileupload">
<div
style="width: 125px; height: 40px; background-color: #003366; text-align: center; font-family: Arial; border-radius: 10px; color:#CCC;">
<div style="top: 10px; position: relative;">Fileupload</div>
</div>
</a><br/>
<div style="width: 250px; height: 435px; border: 2px solid #003366; border-radius: 15px;">
<div style="position: relative; top:200px; text-align: center;" id="drag_upload">Drop File here to upload!</div>
</div>
</div>
</body>
</html>
Deleted Answer:
You'll need to use e.preventDefault() in conjunction
with e.stopPropogation() as the default behavior is to load the file
in the current tab.
Why it's wrong is that you had preventDefault, but it's also possible the event is out of scope. e.stopPropogation() will only prevent the event from going higher up the DOM tree to parent elements. Here's some clean-up of the code that tested fine for me:
$(document).ready(function () {
$("#home").button();
$("#frm_logout").button();
});
$('body').on('dragover drop', function(e) { e.preventDefault(); });
$(document).on('draginit dragstart dragover dragend drag drop', function(e) {
e.stopPropagation();
e.preventDefault();
});
$("#drag_upload").on('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).addClass('dragHover');
}).on('dragleave dragend', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).removeClass('dragHover');
}).on('drop', function(e) {
e.stopPropagation(); // Stops parent elements from receiving event.
e.preventDefault(); // Stops some browsers from redirecting.
$(this).removeClass('dragHover');
$(this).addClass('loading');
if (window.FileReader) {
if (e.originalEvent.dataTransfer.files.length<1) {
// do stuff here
}
}
else alert('Internet Explorer 9 and below are not supported for this feature.');
});
I removed </script><script> as you can just continue the previous.
I used e.preventDefault() directly on both body & document to make sure it's not going to redirect the browser, although using both e.preventDefault() and e.stopPropogation() in the event should accomplish this, I'd rather be safe than sorry.
I made the functions inline using JQuery's .on() which has proven to be the most reliable way to me of binding events.
I also added some classes that are set/unset during the events, really just copy/paste remnants from code I use, but they're useful for styling the elements when events occur. Leave them or delete them, this is more for the user than yourself.
Next, you'll want to make sure the browser is capable of window.FileReader as there is no IE support below version 10.
Last, e.dataTransfer.files does not exist. You'll need e.originalEvent.dataTransfer.files (although as you've written it I'm not sure it's that way due to the scope of the event in the different methods).
I'm trying to fire an event every time a navigation link (a.nav) is clicked. For Safari, it works as expected; however, in every other browser it ONLY fires the first time a link is clicked. For some reason, I'm not getting the desire result, is there anything I'm missing?
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>PatrickCason.com</title>
<link href="stylesheet.css" rel="stylesheet" type="text/css">
<!--[if IE 7]>
<style type="text/css">
#main, #namespace {
padding-right: 115px;
}
#textarea {
padding-right: 115px;
padding-top: 15px;
}
#social {
bottom: 82px;
}
</style>
<![endif]-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js" type="text/javascript"></script>
<script src="jquery.history.js" type="text/javascript"></script>
<script src="spritely.js" type="text/javascript"></script>
<script src="ajax.js" type="text/javascript"></script>
<script type="text/javascript">
$(window).load(function() {
$("#main").show("slide", { direction: "up" }, 1000);
$("#copyright").show("slide", { direction: "down" }, 500);
$("#main").effect("bounce", { times: 1, distance: 5 }, 250);
$('a.nav').live('click', function() {
//For some reason... this only works with the first time I click a nav link... herein lies the problem!
$('#spark').sprite({fps: 30, no_of_frames: 12, play_frames: 12});
});
});
</script>
</head>
<body>
<div id="floater"></div>
<div id="container">
<div id="main">
<div id="spark"></div>
<div id="namespace"><span style="font-size: 20px;">hi. i am </span><h1 style="display: inline; font-size: 36px;">Patrick Cason</h1><span style="font-size: 20px;">.</span></div>
<div id="nav">
<ul>
<li>Contact Me!</li>
<li>About</li>
<li>Links</li>
<li>Blog</li>
</ul><br>
<p><b>Phone:</b><br><span style="font-size: 24px; font-weight: bold;">615.339.4300</span></p>
<p><b>Email:</b><br>pcason#comcast.net</p>
</div>
<div id="social">
<ul>
<li><img src="images/facebook.png"></li>
<li><img src="images/twitter.png"></li>
<li><img src="images/linkedin.png"></li>
<li><img src="images/feed.png"></li>
</ul>
</div>
<div id="home"><img src="images/home.png"></div>
<div id="textarea">
<div id="content"></div>
</div>
</div>
<div id="shadow"></div>
<script>
$(window).load(function () {
$("#shadow").fadeIn(1250);
});
</script>
<div id="copyright">
Copyright © 2011 Patrick Cason. All rights reserved.
</div>
</div>
</body>
</html>
I hope I'm not being to vague... I'd really appreciate the help.
I don't see the need for using live() in your case. That is for when you have dynamically added html. Or, when you have a decent number of elements that will bind to the same event handler (rather than click() which would bind individual handlers for each link).
$('a').click(function() {
$('#spark').sprite({fps: 30, no_of_frames: 12, play_frames: 12});
});
I think the problem might be your selector. Try just selecting <a> tags
http://jsfiddle.net/EmvQk/
EDIT:
Edit your code to comment out the sprite() method and add an alert() as indicated in the comments below. I am pretty confident the problem is stemming from the sprite() call and this little test will determine if that is the case.
Your Css needs to follow the set of rules that the library requires in order to function properly. After reviewing the library example and based on your #sprite css rule ... Try making these changes:
#sprite
{
background: transparent url(images/spark.png) 0 0 no-repeat;
position: absolute;
top: 0;
left: 223px;
width: 156px;
height: 567px;
z-index: 2000;
cursor: pointer;
}
It is an issue with Spritely, as of version 0.5 they have a destroy() method, so...
$("#spark").click(function() {
var $spark = $("#spark");
$coil.sprite({
fps: 30,
no_of_frames: 12,
on_last_frame: function(obj) {
obj.spStop(); // stop the animation on the last frame
obj.spState(1);
killSprite($spark);
}
});
});
function killSprite(yourEl){
yourEl.destroy();
}
I popped the destroy method in a function that is called on the last frame of your animation so hopefully it won't cause any issues
It should be that
$('#spark').sprite({fps: 30, no_of_frames: 12, play_frames: 12});
in and of itself doesn't work multiple times. Have you tried calling that function twice and see what happens.
To test if your click handler is the culprit, add an alert to the click handler and see if the issue is your click handler or the code snipped above.
It seems to me that your problem may be with the spritely plugin for jQuery. I set up a quick test at: http://jsfiddle.net/Htm4y/, that worked for me (in Chrome). In the example, clicking the .nav links fires a jQuery toggle. I would need to see your css to figure out what is supposed to be in that div, but it must be configured correctly in order for spritely to animate it.