Implementing drag and drop with image array using JavaScript in HTML5? - javascript

I want to create a list of images that can be dragged and dropped into a container.
Then I want to iterate the list of images and render them. The code below works only for images that are outside of the array(boxA). How do I implement this design as to have the images that are inside the array to be drag-and-drop-able inside the container (boxB)?
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
html, body {
margin-left:auto;
margin-right:auto;
width:980px;
}
#boxA { background-color: #6633FF; width:75px; height:75px; }
#boxB{
float:right;
padding:10px;
margin:10px;
}
#boxB { background-color: #FF6699; width:500px; height:500px; }
#lolo {
padding:10px;
width:800px;
list-style:none;
float:left;
}
#lolo ul{
display:inline;
}
#lolo ul li{
display:inline;
}
</style>
<script type="text/javascript">
function dragStart(ev) {
ev.dataTransfer.effectAllowed='move';
ev.dataTransfer.setData("Text", ev.target.getAttribute('id'));
ev.dataTransfer.setDragImage(ev.target,0,0);
return true;
}
function dragEnter(ev) {
event.preventDefault();
return true;
}
function dragOver(ev) {
return false;
}
function dragDrop(ev) {
var src = ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(src));
ev.stopPropagation();
return false;
}
<-- this my array -->
var myArray = new Array;
myArray[0] = '<img src="image/pic1.png">'
myArray[1] = '<img src="image/pic2.png">'
myArray = ev.dataTransfer.getData("text");
function lala(){
for (var i=0; i < myArray.length; i++)
{
document.write("<ul>" + "<li>" + myArray[i] + "</li>" + "</ul>");
}
}</script>
</head>
<body>
<!-- this the HTML code -->
<div id="boxA" draggable="true"
ondragstart="return dragStart(event)">
</div>
<div id="lolo">
<script>
lala();
</script>
</div>
<div id="boxB" ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)"
ondragover="return dragOver(event)">
</div>
</body>
</html>

Give 'id's to images and use ondragstart handler for div 'lolo'
myArray[0] = '<img id="img1" src=........
myArray[1] = '<img id="img2" src=........
.....
<div id="lolo" ondragstart="return dragStart(event)">
......
</div>
`

Related

HTML5 DnD - Why is ghost image only working as expected AFTER dragging & dropping an element?

I'm trying to get a p element with a nested img to show as the ghost image when dragging.
I'm not sure how to debug this but I have noticed that once the images are cached or have been dragged and dropped somewhere on the page, it works as expected. I've made a MWE here:
The smiley face is dragged on first load of the page and shows the erroneous behavior - the emoji doesn't show during the drag. The sad face is dragged, released, and then redragged, which results in the expected behavior - the emoji does show as part of the ghost image. This is true of all the images.
What I've tried:
I thought it might be an issue with the way the page elements are loaded, so I moved the javascript to the bottom of the body (trying to ensure all elements are loaded before the script runs). This doesn't solve the issue.
MWE code:
I got the emojis from here, but I guess any pngs you have lying around on your machine will do to reproduce this.
index.php:
<html>
<head>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
</head>
<body>
<h2>Order these items</h2>
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item" draggable="true" ondragstart="drag(event)" id="drag'.$i.'"><img draggable="false" src="/images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
var dataList = ev.dataTransfer.items;
dataList.add(ev.target.id, "text/plain");
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
// If the dropzone already contains something (not text due to
// spaces in markup being counted as text), don't allow
// another drop to occur.
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data));
}
function reset() {
// Put all drag items back into the home container
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
</script>
</body>
</html>
image_set.json:
{
"happy": {
"fn":"happy.png",
"text":"A happy face"
},
"sad": {
"fn":"sad.png",
"text":"A sad face"
},
"angry": {
"fn":"angry.png",
"text":"An angry face"
},
"confused": {
"fn":"confused.png",
"text":"A confused face"
},
"sleepy": {
"fn":"sleepy.png",
"text":"A sleepy face"
}
}
stylesheet.css:
* {
box-sizing:border-box;
padding:0px;
margin:0px;
font-family:sans-serif;
font-weight:100;
}
body {
padding:20px;
}
h2 {
padding:20px 0;
font-size:4em;
}
p.drag_item {
text-align:center;
transition:0.5s;
width:200px;
height:200px;
}
.drag_item:hover {
cursor:move;
}
#home_container, #dropzone_wrapper {
min-height:200px;
width:100%;
display:flex;
flex-direction:row;
justify-content:space-around;
margin:20px 0;
align-items:center;
}
#dropzone_wrapper {
font-size:3em;
}
#dropzone_wrapper p {
font-size:initial;
}
#home_container {
border:1px solid black;
border-radius:8px;
background-color:#e5e5e5;
}
#home_container p {
width:200px;
font-size:16px;
}
#msg {
display:block;
font-size:2.5em;
}
.dropzone {
min-height:200px;
width:200px;
border:1px dashed black;
background-color:#00a8bd;
}
I've done a bit research to find the problem. This was a bit hard for me, because Firefox was the only browser where the ghost image was not shown on the first load of the page and the first drag. I opened the Network tab and found out that the image is only requested on the first drag (which I don't really understand, because the images were completely loaded).
Anyways, I finally I managed to get this to work, by changing the draggable element to the image instead of the paragraph.
index.php:
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item"><img ondragstart="drag(event)" id="drag'.$i.'" draggable="true" src="images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
JS:
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
// get the cursor position relative to the element
var x = (ev.pageX - ev.target.offsetLeft) + document.body.scrollLeft;
var y = (ev.pageY - ev.target.offsetTop) + document.body.scrollTop;
ev.dataTransfer.setData("text", ev.target.id);
// set the parent element (the paragraph) as the custom ghost image and set the position of the ghost image (x, y)
ev.dataTransfer.setDragImage(ev.target.parentElement, x, y);
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data).parentElement);
}
function reset() {
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
This works pretty well in: Chrome, Edge, IE 11
NOTE: This only works perfectly in Firefox (the paragraph text only appears in this browser)

Drag and Drop ( Copy ) in another Div is lining up

I'm doing a Drag and Drop project where when dropping an image in another Div, it has to stay in the same place where I left it.
The problem is the img are lining up the left.
HTML CODE :
<html>
<head>
<link type="text/css" rel="stylesheet" media="screen" href="css/testednd.css" /> <!-- CSS das div's -->
<script src="js/testednd.js"></script> <!-- Script clickImagem -->
</head>
<body>
<div id="conteudo-left" style="position:static; left:10px; top:20px; width:300; height:660; z-index:-1; overflow: auto">
<form name="form_dnd_left" border = 1>
<ul>
<li><img id="drag1" src="images/Comp3.jpg" draggable="true" ondragstart="drag(event)" alt="" /></li>
</ul>
</form>
</div>
<div class="conteudo" id="conteudo" ondrop="drop(event)" ondragover="allowDrop(event)"><!-- abrimos a div conteudo do meio-->
</div>
</body>
</html>
CSS CODE
#conteudo-left{
width:300px;
height:660px;
float:left;
background-color:#FFF;
}
#conteudo{
width:600px;
height:460px;
float:left;
background-color:#ff1;
display: initial;
margin: auto;
.columns {
}
}
JavaScript Code
///Drag'n Drop functions
function allowDrop(ev)
{
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("text", ev.target.id);
ev.dataTransfer.effectAllowed = "copy";
}
function drop(ev)
{
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var copyimg = document.createElement("img");
var original = document.getElementById(data);
copyimg.src = original.src;
ev.target.appendChild(copyimg);
}
Can somebody help me??
Thanks for all !!
Update:
From the event you can get the position of the drag and minus the offset of the parent, thus we can drop it in that exact location.
function drop(ev)
{
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var copyimg = document.createElement("img");
var parent = document.createElement("conteudo");
var original = document.getElementById(data);
copyimg.src = original.src;
copyimg.style.position = "absolute";
copyimg.style.left = ev.clientX - ev.target.offsetLeft+"px";
copyimg.style.top = ev.clientY - ev.target.offsetTop+"px";
ev.target.appendChild(copyimg);
}
Old Answer:
Do you want something like this?
CSS used to make this is:
padding-left: 150px;
padding-top: 125px;
box-sizing: border-box;
So I gave half of the width and height as padding so that the images get positioned there! also I am using box-sizing:border-box so that the padding does not get added to the dimensions of the div.
Note: I have reduced the dimesions of the boxes so that they fit perfectly inside the demo window, please set the padding-top andpadding-left` to about half of the width of the respective dimensions!
///Drag'n Drop functions
function allowDrop(ev)
{
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("text", ev.target.id);
ev.dataTransfer.effectAllowed = "copy";
}
function drop(ev)
{
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var copyimg = document.createElement("img");
var parent = document.createElement("conteudo");
var original = document.getElementById(data);
copyimg.src = original.src;
copyimg.style.position = "absolute";
copyimg.style.left = ev.clientX - ev.target.offsetLeft+"px";
copyimg.style.top = ev.clientY - ev.target.offsetTop+"px";
ev.target.appendChild(copyimg);
}
#conteudo-left{
width:150px;
height:330px;
float:left;
background-color:#FFF;
}
#conteudo{
width:300px;
height:250px;
position:relative;
float:left;
background-color:#ff1;
display: initial;
margin: auto;
}
<html>
<head>
<link type="text/css" rel="stylesheet" media="screen" href="css/testednd.css" /> <!-- CSS das div's -->
<script src="js/testednd.js"></script> <!-- Script clickImagem -->
</head>
<body>
<div id="conteudo-left" style="position:static; left:10px; top:20px; width:300; height:660; z-index:-1; overflow: auto">
<form name="form_dnd_left" border = 1>
<ul>
<li><img id="drag1" src="http://via.placeholder.com/50x50" draggable="true" ondragstart="drag(event)" alt="asdfasdf" /></li>
</ul>
</form>
</div>
<div class="conteudo" id="conteudo" ondrop="drop(event)" ondragover="allowDrop(event)">
</div>
</body>
</html>
On the drop event you get the x and y coords of the mouse and set the style to be absolute in that position. Note that the top left corner of the image will snap to the exact coord of the mouse pointer. See below:
///Drag'n Drop functions
function allowDrop(ev)
{
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("text", ev.target.id);
ev.dataTransfer.effectAllowed = "copy";
}
function drop(ev)
{
ev.preventDefault();
var x = ev.clientX;
var y = ev.clientY;
var data = ev.dataTransfer.getData("text");
var copyimg = document.createElement("img");
var original = document.getElementById(data);
copyimg.src = original.src;
ev.target.appendChild(copyimg);
copyimg.setAttribute("style", "position: absolute; top: "+y+"px; left:"+x+"px;");
}
#conteudo-left{
width:150px;
height:330px;
float:left;
background-color:#FFF;
}
#conteudo{
width:300px;
height:250px;
float:left;
background-color:#ff1;
display: initial;
margin: auto;
box-sizing: border-box;
}
<html>
<head>
<link type="text/css" rel="stylesheet" media="screen" href="css/testednd.css" /> <!-- CSS das div's -->
<script src="js/testednd.js"></script> <!-- Script clickImagem -->
</head>
<body>
<div id="conteudo-left" style="position:static; left:10px; top:20px; width:300; height:660; z-index:-1; overflow: auto">
<form name="form_dnd_left" border = 1>
<ul>
<li><img id="drag1" src="http://via.placeholder.com/50x50" draggable="true" ondragstart="drag(event)" alt="asdfasdf" /></li>
</ul>
</form>
</div>
<div class="conteudo" id="conteudo" ondrop="drop(event)" ondragover="allowDrop(event)">
</div>
</body>
</html>

How to get dataset attribute of dragged div box?

I want to get the dataset-attributes "data-price" with two decimalnumbers of dragged div boxes via Javascript to sum them up.
Here my div box:
<div name="qty" id="black" data-price="21.1" draggable="true" ondragstart="drag(event)">
I tried to do it with this script
<script type="text/javascript">
function findTotal(){
var arr = document.querySelectorAll("se > div");
var tot=0;
for(var i=0;i<arr.length;i++){
if(parseInt(arr[i].dataset.price))
tot += parseInt(arr[i].dataset.price);
}
document.getElementById('total').value = tot;
}
</script>
but it only counts the data-prices of the non-dragged boxes.
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
What i'm doing wrong here?
You should really add the whole HTML code for us to see how you have set the elements with form/name/attributes. I wrote a simple example based on your code and it works just fine for me and it calculates only the elements that are dropped to the target element. Also for decimal numbers, use parseFloat and not parseInt:
function findTotal(){
var arr = document.querySelectorAll(".target > div");
var tot=0;
for(var i=0;i<arr.length;i++){
if(parseInt(arr[i].dataset.price))
tot += parseFloat(arr[i].dataset.price);
}
document.getElementById('total').textContent = tot;
}
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
findTotal();
}
document.getElementById('reset').onclick = function() { window.location.reload(); }
#black {
background-color: black;
color:white;
}
#red {
background-color: red;
}
#yellow {
background-color: yellow;
}
#green {
background-color: green;
color:white;
}
.qty {
width:50px;
height:50px;
display: inline-block;
margin-right:5px;
margin-bottom:5px;
}
.target {
height:100px;
border:1px solid grey;
}
<button id="reset">Reset</button>
<hr>
<form>
<div class="qty" id="black" data-price="21.1" draggable="true" ondragstart="drag(event)">21.1</div>
<div class="qty" id="red" data-price="16.5" draggable="true" ondragstart="drag(event)">16.5</div>
<div class="qty" id="yellow" data-price="7.8" draggable="true" ondragstart="drag(event)">7.8</div>
<div class="qty" id="green" data-price="3.5" draggable="true" ondragstart="drag(event)">3.5</div>
<h3>Drop items here</h3>
<div class="target" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<h3>Total: </h3>
<div id="total"></div>
</form>
My example can also be found here: http://zikro.gr/dbg/html/jsdd/

Function prepend() jquery hover and counter click

I have a little problem with prepend() because if I "copy" my div and if click on counter the count change in whole divs the same is with hover. Is this possible change number count and hover only in clicked or hovered div?
Thank you for help and time:)
HTML
<div class="Wrap">
<div class="container">
<div class="count">My Counter</div>
<div class="background"></div>
<div class="hover"></div>
</div>
</div>
<button class=AddDiv>AddDiv</button>
And javascript
$('.AddDiv').on('click', function() {
$('.Wrap').prepend($('<div class="container"><div class="count">My Counter</div><div class="background"></div><div class="hover"></div></div>'));
});
var count = 0;
$(".count").click(function() {
count++;
$(".count").html(+count);
});
$(".background").on("mouseover", function() {
$(".hover").fadeIn(500);
});
$(".hover").on("mouseout", function() {
$(".hover").fadeOut(200);
});
FIDDLE
Yes sure, use the current clicked element object $(this) :
$(this).html(+count);
Instead of :
$(".count").html(+count);
And use event delegation on() to attach click event to the new elements added dynamically to the DOM :
$("body").on('click',".count",function() {
count++;
$(this).html(+count);
});
To increment count separatelly for every div you should get the current count then add 1 to it, like :
$("body").on('click',".count",function() {
var count = parseInt( $(this).text() );
if( isNaN(count) ){
count = 1; //For the first click
}else{
count++;
}
$(this).text(count);
});
Hope this helps.
$('.AddDiv').on('click', function(){
$('.Wrap').prepend($('<div class="container"><div class="count">My Counter</div><div class="background"></div><div class="hover"></div></div>'));
});
$("body").on('click',".count",function() {
var count = parseInt( $(this).text() );
if( isNaN(count) ){
count = 1; //For the first click
}else{
count++;
}
$(this).text(count);
});
$(".background").on("mouseover", function () {
$(".hover").fadeIn(500);
});
$(".hover").on("mouseout", function () {
$(this).fadeOut(200);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="Wrap">
<div class="container">
<div class="count"> Click Me
</div>
<div class="background">
</div>
<div class="hover">
</div>
</div>
</div>
<button class=AddDiv>AddDiv</button>
Here we are creating dynamic id for each prepend. And by sending that div id to the javascript function and using same count logic as #Zakaria Acharki used to maintain count value.
var divNumber = 1;
$('.AddDiv').on('click', function() {
$('.Wrap').prepend($('<div class="container"><div class="count" id="div'+divNumber+'" onclick="makeCount(this.id);">My Counter</div><div class="background"></div><div class="hover"></div></div>'));
divNumber++;
});
function makeCount(id){
var count = parseInt( $("#"+id).text());
if( isNaN(count) ){
count = 1; //For the first click
}else{
count++;
}
$("#"+id).text(count);
}
$(".background").on("mouseover", function() {
$(".hover").fadeIn(500);
});
$(".hover").on("mouseout", function() {
$(".hover").fadeOut(200);
});
.Wrap
{
width:650px;
height:800px;
}
.container
{
position:relative;
top:5px;
left:5px;
width:200px;
height:200px;
background-color:red;
float:left;
margin-left:5px;
margin-top:5px;
}
.AddDiv
{
position:absolute;
top:0px;
}
.count
{
position:absolute;
width:100px;
height:100px;
position:absolute;
left:50%;
top:50%;
margin-left:-50px;
margin-top:-50px;
background-color:white;
text-align:center;
line-height:100px;
cursor:pointer;
}
.background
{
width:20px;
height:20px;
background-color:green;
position:absolute;
left:170px;
top:10px;
}
.hover
{
width:200px;
height:200px;
background-color:rgba(255,255,255,0.8);
position:absolute;
z-index:1001;
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="Wrap">
<div class="container">
<div class="count" id="div0" onclick="makeCount(this.id);">My Counter</div>
<div class="background"></div>
<div class="hover"></div>
</div>
</div>
<button class=AddDiv>AddDiv</button>
Try .insertBefore() function:
check: https://jsfiddle.net/mpqtrjzx/
$( '<div class="container"><div class="count">My Counter</div><div class="background"></div><div class="hover"></div></div>' ).insertBefore( ".container:first" );

JS remove class from div, when text field in empty?

I have a search bar where the results are displayed centered of the page with a fixed div. And an absolute div covers the page behind the results with a semi transparent black. How could I remove the class ".coverOn" added on the keyup function, If I just deleted what I put into the search field without refreshing the page?
Thanks.
<styles>
.coverOn {
position:absolute;
width:100%;
height:100%;
background-color:rgba(0,0,0,0.7);
z-index:8;
}
#output { /* holds search results */
position:fixed;
left:0;
right:0;
top:130px;
width:400px;
margin:0 auto;
-webkit-border-radius:15px;
-moz-border-radius:15px;
border-radius:15px;
z-index:12;
}
</styles>
<body>
<div id="cover"></div>
<div id="Search">
<input type="text" name="search" onkeyup="searchq();" value="" required class="SearchField" />
<div id="output"></div>
</div>
</body>
<script>
function searchq() {
var searchTxt = $("input[name='search']").val();
$.post("../php/productSearch.php", {searchVal: searchTxt}, function(output) {
$("#output").html(output);
document.getElementById("cover").className = "coverOn";
});
}
</script>
Remove class if searchTxt string is empty:
function searchq() {
var searchTxt = $("input[name='search']").val().trim();
$.post("../php/productSearch.php", {searchVal: searchTxt}, function (output) {
$("#output").html(output);
document.getElementById("cover").className = searchTxt ? "coverOn" : "";
});
}
or since you are using jQuery it can be simpler:
$("#cover").toggleClass("coverOn", searchTxt);
use removeClass('selector');
$('input').on('keyup', function() {
$('.red').removeClass('red');
})
.red {
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="text" />
<div class="red">red</div>
What you could do, rather than operating with classes, is instead of
.coverOn {
position:absolute;
width:100%;
height:100%;
background-color:rgba(0,0,0,0.7);
z-index:8;
}
do
#output:not(:empty) {
position:absolute;
width:100%;
height:100%;
background-color:rgba(0,0,0,0.7);
z-index:8;
}
So long as when you do an empty search, it returns an empty result.
Your resulting javascript would be
function searchq() {
var searchTxt = $("input[name='search']").val();
$.post("../php/productSearch.php", {searchVal: searchTxt}, function(output) {
$("#output").html(output);
});

Categories