Ok, I feel like this should have already been asked on stackoverflow, but apparently it hasn't. How do you make an object draggable using no jQuery?
I understand how to make an object move on the hovering of the mouse so it contantly follows it, and tried to apply it to mousedown instead of mousemove and set it on an interval of every 10 milliseconds, but with no success.
So here is what I have:
document.querySelector(".box").addEventListener(function(e) {
let s = document.querySelector(".box");
var e = e || window.event;
let run = setInterval(function() {
s.style.marginLeft = e.clientX + "px";
s.style.marginTop = e.clientY + "px";
getMouseCoords(e);
}, 10);
});
So how would I drag and drop an object without any jQuery?
Follow this. It uses only pure JavaScript.
Simply you can drag div content
<div id="draggable-element">Drag me!</div>
Link for Draggable Project
There is a really good codepen example I've used in the past for this:
https://codepen.io/byronglover/pen/oxjgEK
Step 1 - create containers for the objects
<!--First Drop Target-->
<div data-drop-target="true">
<div id="box1" draggable="true" class="box navy"></div>
<div id="box2" draggable="true" class="box red"></div>
<div id="box3" draggable="true" class="box green"></div>
<div id="box4" draggable="true" class="box orange"></div>
<div id="box5" draggable="true" class="box navy"></div>
<div id="box6" draggable="true" class="box red"></div>
<div id="box7" draggable="true" class="box green"></div>
<div id="box8" draggable="true" class="box orange"></div>
</div>
<!--Second Drop Target-->
<div data-drop-target="true"></div>
Step 2 - apply some javascript invoking draggable and data-drop-target and event listeners
//Function handleDragStart(), Its purpose is to store the id of the draggable element.
function handleDragStart(e) {
e.dataTransfer.setData("text", this.id); //note: using "this" is the same as using: e.target.
}//end function
//The dragenter event fires when dragging an object over the target.
//The css class "drag-enter" is append to the targets object.
function handleDragEnterLeave(e) {
if(e.type == "dragenter") {
this.className = "drag-enter"
} else {
this.className = "" //Note: "this" referces to the target element where the "dragenter" event is firing from.
}
}//end function
//Function handles dragover event eg.. moving your source div over the target div element.
//If drop event occurs, the function retrieves the draggable element’s id from the DataTransfer object.
function handleOverDrop(e) {
e.preventDefault();
//Depending on the browser in use, not using the preventDefault() could cause any number of strange default behaviours to occur.
if (e.type != "drop") {
return; //Means function will exit if no "drop" event is fired.
}
//Stores dragged elements ID in var draggedId
var draggedId = e.dataTransfer.getData("text");
//Stores referrence to element being dragged in var draggedEl
var draggedEl = document.getElementById(draggedId);
//if the event "drop" is fired on the dragged elements original drop target e.i.. it's current parentNode,
//then set it's css class to ="" which will remove dotted lines around the drop target and exit the function.
if (draggedEl.parentNode == this) {
this.className = "";
return; //note: when a return is reached a function exits.
}
//Otherwise if the event "drop" is fired from a different target element, detach the dragged element node from it's
//current drop target (i.e current perantNode) and append it to the new target element. Also remove dotted css class.
draggedEl.parentNode.removeChild(draggedEl);
this.appendChild(draggedEl); //Note: "this" references to the current target div that is firing the "drop" event.
this.className = "";
}//end Function
//Retrieve two groups of elements, those that are draggable and those that are drop targets:
var draggable = document.querySelectorAll('[draggable]')
var targets = document.querySelectorAll('[data-drop-target]');
//Note: using the document.querySelectorAll() will aquire every element that is using the attribute defind in the (..)
//Register event listeners for the"dragstart" event on the draggable elements:
for(var i = 0; i < draggable.length; i++) {
draggable[i].addEventListener("dragstart", handleDragStart);
}
//Register event listeners for "dragover", "drop", "dragenter" & "dragleave" events on the drop target elements.
for(var i = 0; i < targets.length; i++) {
targets[i].addEventListener("dragover", handleOverDrop);
targets[i].addEventListener("drop", handleOverDrop);
targets[i].addEventListener("dragenter", handleDragEnterLeave);
targets[i].addEventListener("dragleave", handleDragEnterLeave);
}
And Finally some CSS to tie it all together
h2 {
color: #a7a3a4;
margin-left: 80px;
}
[data-drop-target] {
height: 400px;
width: 200px;
margin: 25px;
background-color: gainsboro;
float: left;
}
.drag-enter {
border: 2px dashed #000;
}
.box {
width: 100px;
height: 100px;
float: left;
}
.box:nth-child(3) {
clear: both;
}
.navy {
background-color: navy;
}
.red {
background-color: firebrick;
}
.green {
background-color: darkgreen;
}
.orange {
background-color: orange;
}
Summary
The long and short of it is that the draggable content needs to have some sort of event listener to bind to it for this to work effectively.
Let me know if you have any other questions and I will do my best to help!
Related
I have set up a drag and drop box on my Vue JS web app. The element is a simple div which handles a file when it is dragged and dropped onto it.
I used https://www.raymondcamden.com/2019/08/08/drag-and-drop-file-upload-in-vuejs as a guideline.
HTML:
<div v-if="status == 'upload'">
<h3> Please fill the data sheet and upload it here!</h3>
<div class="downbox" #drop.prevent="addFile" #dragover.prevent>
<span style="font-size: 60px"><font-awesome-icon icon='cloud-upload-alt'/></span>
<br>
Click to Browse
<br>
or Drag and Drop a File Here
</div>
</div>
JS:
addFile: function(e){
let droppedFiles = e.dataTransfer.files;
if ((droppedFiles[0].name).slice(-5) !== '.xlsx') {
this.$swal({
text: "Please upload a file of type .xlsx",
title: "Incorrect File Type!",
icon: "error",
})
this.status = 'upload'
}
else {
this.file = droppedFiles
this.status = 'show'
}
},
removeFile: function(){
this.file = null
this.status = 'upload'
}
CSS:
.downbox {
width: 500px;
border-radius: 50px;
border-width: 6px;
border-color: white;
border-style: dashed;
background-color: #7a2ab3;
padding: 10px;
font-size: 25px;
font-weight: bold;
transition: 0.6s;
margin-left: auto;
margin-right: auto;
}
.downbox:hover{
background-color: #c56bc5;
}
As you can see, the background colour is changed when you mouse over the div.
However when I am dragging a file onto the div, this change of colour does not show up. So I don't know whether it does not count as a ":hover" if you are click dragging a file.
Either way I would like to know what I can add to the code in order to make the CSS background-color property change when I drag a file onto the div.
I use the following solution (simplified here):
<template>
<div ref="drag" :class="{over: isOver}">
...
</div>
</template>
<script>
...
mounted () {
// add the needed event listeners to the container
this.$refs.drag.addEventListener("dragover", () => {
this.isOver = true; // add class on drag over
});
this.$refs.drag.addEventListener("dragleave", () => {
this.isOver= false; // remove class on drag leave
});
}
</script>
<css>
.over {...}
</css>
You can listen for dragenter and dragleave events to handle this. But it can be tricky if you have other elements within your drop target element, because those will trigger dragleave events on your drop target that you will want to ignore. But something like this should work:
<div :class="['downbox', drag_over ? 'over' : '']"
#dragenter="dragEnter"
#dragleave="dragLeave"
#dragover.prevent
#drop.prevent="addFile"
>
<div ref="others">
<span style="font-size: 60px"><font-awesome-icon icon='cloud-upload-alt'/></span>
<br>
Click to Browse
<br>
or Drag and Drop a File Here
</div>
</div>
In your event handlers, use over_others to decide whether to set drag_over to false, which you don't want to do if you are still dragging over a child element. (see also, info on Vue $refs)
You may have more difficulty with your 60px span and the font-awesome-icon, but you should be able to extend the same principle to those elements if they give you trouble.
data() {
return {
drag_over: false, // dragging over the target OR any child element
over_others: false, // dragging over a child element
}
},
methods: {
dragEnter(e) {
if(e.target === this.$refs.others) this.over_others = true
else this.drag_over = true
},
dragLeave(e) {
if(e.target === this.$refs.others) this.over_others = false
else if(!this.over_others) this.drag_over = false
},
// ...
}
And add the css class:
.downbox:hover,
.downbox.over {
background-color: #c56bc5;
}
Check out this solution, apparently you have to use X and Y coordinates:
https://stackoverflow.com/a/8615260/12448004
This question already has answers here:
Attach event handlers for click event on all elements in the DOM
(5 answers)
Closed 3 years ago.
i try to make onclick event to all the decomnt that give me back the elment id that i click on .
thank you
i tried to you the parent id
document.getElementById("parent").onclick = function(){btnIsClicked(this.id)};
this work but give me the parent id not the clicked one..
document.getElementById(*).onclick = function(){btnIsClicked(this.id)};
function btnIsClicked(id){
console.log(id);
}
please i need some thing like * to make it for all but in same time give me the id of the clicked elment (not the just the parent one)
You can add the event listener to the document and access the element that was clicked as event.target
document.addEventListener('click', (event) => {
console.log(event.target.id);
})
.box {
width: 50px;
height: 50px;
background-color: lightblue;
margin: 10px;
display: inline-block;
}
<div id="box1" class="box"></div>
<div id="box2" class="box"></div>
<div id="box3" class="box"></div>
You can attach an event listener to the document body.
document.getElementsByTagName('body')[0].onclick = function(e) {console.log(e.target.id)};
You can use querySelectorAll and addEventListener for achive this.
const elements = document.querySelectorAll('*');
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
element.addEventListener('click', e => {
e.stopPropagation();
btnIsClicked(e.target.id);
});
}
function btnIsClicked(id) {
console.log(id);
}
div {
display: inline-block;
width: 100px;
height: 100px;
}
#element-one {
background: blue;
}
#element-two {
background: red;
}
<div id="element-one"></div>
<div id="element-two"></div>
A possible approach using querySelectorAll and stopPropagation() to get the exact element clicked.
[...document.querySelectorAll("*")].forEach(em => {
em.addEventListener("click", (e) => {
e.stopPropagation();
console.log(e.target)
})
});
<h1>title</h1>
<div>text inside div
<span>text inside span</span>
</div>
I would like to detect a control key hit, without losing the user's current focus.
For example (see below) a user is writing something into a textarea, the textarea is the current element focused.
Then my end user moves his mouse on a div (just hovered, not clicked), and if he presses a control key I would like to execute a function (the keyDown ones below).
To make it works I had to add a tabIndex to my div, and I had to uncomment the theDiv.focus() line which makes me sad because it causes a loss of focus on my active textarea.
How can I simultaneously detect if someone hits a key when his mouse is on a specific element, without losing the current element's focus?
var theDiv = document.getElementById("theDiv");
function entered(e) {
theDiv.addEventListener("keydown", keyDown);
//theDiv.focus();
}
function keyDown(e) {
alert(e.key)
}
theDiv.addEventListener("mouseover", entered);
#theDiv {
width: 100px;
height: 100px;
border: 1px silver solid;
}
#theDiv:hover {
border: 1px silver dashed;
}
<div id="theDiv" tabindex="-1">
</div>
<div>
<textarea id="a">Click here, then hover the div above and hold "Shift" key</textarea>
</div>
You can add mouseover event listener to the document to store the element that is being hovered over in a global variable. When there is a keydown event on the document, prevent the default action if the div if being hovered over (so no text will be printed into the textarea).
var theDiv = document.getElementById("theDiv");
var hoverTarget = document.body;
var res = document.getElementById("result");
document.addEventListener("mouseover", function(e){
hoverTarget = e.target;
});
function keyDown(e) {
res.innerText = "Key: "+e.key+" KeyCode: "+e.keyCode;
}
document.addEventListener("keydown", function(e){
if(hoverTarget===theDiv){
e.preventDefault();
keyDown(e);
}
});
#theDiv {
width: 100px;
height: 100px;
border: 1px silver solid;
}
#theDiv:hover {
border: 1px silver dashed;
}
<div id="theDiv" tabindex="-1">
</div>
<div>
<textarea id="a">Click here, then hover the div above and hold "Shift" key</textarea>
</div>
<span id="result">
</span>
I'm trying to figure out how to properly do drag and drop, and from what I read on MDN, it sounds like i should be able to indicate via dragenter that a drop is possible on that element. I simply haven't been able to get it to work, but when I switch to drag over, it seems to start working. I don't really want to register a dragover event handler tho, because I don't need that to be called every time the mouse moves inside an area. Is there a way to completely avoid registering a dragover handler for drag and drop?
This is what I have working with dragover:
<div id="main">
<div id="a" draggable="true">a</div>
<div id="c">b</div>
</div>
<style>
div {
display: inline-block;
border: 1px solid black;
}
#c {
height: 100px;
width: 100px;
}
</style>
<script type='text/javascript'>
var a = document.getElementById('a')
var c = document.getElementById('c')
a.addEventListener('dragstart', function(event) {
var dt = event.dataTransfer;
dt.setData("object/jsObject", {test:1});
dt.setData("text/plain", "test one");
})
c.addEventListener('dragover', function(event) {
if(event.preventDefault){
event.preventDefault();
}
console.log(event)
})
c.addEventListener('drop', function(event) {
console.log(event)
c.innerHTML = 'dropped '+event.dataTransfer.getData(event.dataTransfer.types[0])
event.dataTransfer.dropEffect = 'move';
})
</script>
Is there any way to replace dragover with dragenter?
UPDATE:
Oh here's something interesting, apparently the HTML5 drag and drop api is absolute shite. I was beginning to come to that conclusion myself.
I have for example three draggable elements like follows:
<div class="draggable main">Main</div>
<div class="draggable followers">Follower</div>
<div class="draggable followers">Follower2</div>
If .main is dragged, We need .followers to be dragged too. Not just positions but also triggering all the draggable events in them like start, drag, stop. Note, the followers will be normal draggable elements. Just when .main is dragged they should be moving with it as if they are in the same draggable element triggering all the .follower draggable elements' events too.
Here is the JS
jQuery('.draggable.followers').draggable();
jQuery('.draggable.main').draggable({
// If .main is dragged, We need .followers to be dragged too. Not just positions but also triggering all the draggable events in them like start, drag, stop.
});
Here is the jsfiddle
http://jsfiddle.net/9x56E/1/
Just "cheat" a little by shuffling the HTML around a little like this:
<div class="special-draggable">
<div class="main">Main</div>
<div class="draggable followers">Follower</div>
<div class="draggable followers">Follower2</div>
</div>
Adjust the CSS a little:
.draggable{
border: 1px solid red;
width: 60px;
height: 60px;
}
.main{
width: 300px;
border: 1px solid red;
height: 60px;
}
And then the jQuery:
jQuery('.draggable.followers').draggable();
jQuery('.special-draggable').draggable();
http://jsfiddle.net/Niffler/9x56E/2/
EDIT: Sorry, missed the part about it having to trigger the draggable events...
How about this:
$(function() {
$('.draggable.followers').draggable();
$('.draggable.main').draggable();
/* trigger dragstart on followers when main is dragged */
$('.draggable.main').on('dragstart', function() {
$('.draggable.followers').each(function() {
$(this).trigger('dragstart');
});
});
/* trigger drag on followers when main is dragged and adjust position */
$('.draggable.main').on('drag', function() {
var maintop = $(this).css('top');
var mainleft = $(this).css('left');
$('.draggable.followers').each(function() {
$(this).trigger('drag');
$(this).addClass('ui-draggable-dragging');
$(this).css('margin-left', mainleft);
});
$('.draggable.followers:first').css('margin-top', maintop);
});
/* trigger dragstop on followers when main is dragged and adjust position */
$('.draggable.main').on('dragstop', function() {
var maintop = $(this).css('top');
var mainleft = $(this).css('left');
$('.draggable.followers').each(function() {
$(this).trigger('dragstop');
$(this).removeClass('ui-draggable-dragging');
$(this).css('margin-left', mainleft);
});
$('.draggable.followers:first').css('margin-top', maintop);
});
/* test different actions on .followers */
$('.draggable.followers').on('dragstart', function() {
// do something
});
$('.draggable.followers').on('drag', function() {
// do something
});
$('.draggable.followers').on('dragstop', function() {
// do something
});
});
Here's a fiddle: http://jsfiddle.net/Niffler/6TZp3/