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
Related
I have a div with the attribute contenteditable = true. I can activate the div content editing by double clicking the div, this is because my div is draggable, so I use the dooble click event to activate the div edition. The fact is that I want to eliminate the complete div by clicking on it and then pressing the Delete key on the keyboard. How can I do that? How can I make it so that when I write something on the div and press the delete key, the entire div will not be deleted? I only want to delete the div when the div edition is not activated, just click on the div and then hit the delete key and voila, it is deleted.
This is my HTML Code:
$(document).ready(function() {
$('.draggable').draggable({
containment: "parent"
});
$(".draggable").resizable();
$('#MyFirstDiv').click(function() {
//HERE I WANT TO PUT THE CODE TO DELETE THE DIV.
});
$("#myContainer").on("dblclick", "#MyFirstDiv", function(e) {
e.preventDefault();
$(".draggable").draggable('disable');
this.querySelector(":scope > :first-child").focus();
});
$("#myContainer").on("blur", "#MyFirstDiv", function(e) {
e.preventDefault();
$(".draggable").draggable('enable');
});
});
#myContainer {
border: 1px solid black;
height: 400px;
width: 100%;
}
#DraggableDiv {
border: 1px solid blue;
}
<!DOCTYPE html>
<html>
<head>
<title>My Delete Div</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
</head>
<body>
<div id="myContainer">
<div id="MyFirstDiv">
<div class="draggable" contenteditable="true" id="DraggableDiv">
THIS IS MY TEXT INSIDE THE DIV
</div>
</div>
</div>
</body>
</html>
Easiest way to to capture the keydown on the delete key.
$('#MyFirstDiv').click(function(e){
e.preventDefault();
});
$('#MyFirstDiv').keydown(function(e){
e.preventDefault();
if(e.keyCode == 46) {
this.remove();
}
});
You could first just make a variable: divClicked, I store the clicked state of the div
var divClicked = false;
Then in your event listener, update divClicked (it'll be a toggled button):
$("#MyFirstDiv").click(function(e) {
e.preventDefault();
divClicked = !divClicked;
}
Finally, add a delete key event listener like so:
$("#MyFirstDiv").keydown(function(e) {
e.preventDefault();
if (e.keyCode == 46) {
if (divClicked) {
$(this).remove();
} else {
alert("Click the div first then press Delete to remove it");
}
}
})
Full code:
var divClicked = false;
$("#MyFirstDiv").click(function(e) {
e.preventDefault();
divClicked = !divClicked;
}
$("#MyFirstDiv").keydown(function(e) {
e.preventDefault();
if (e.keyCode == 46) {
if (divClicked) {
$(this).remove();
} else {
alert("Click the div first then press Delete to remove it");
}
}
})
It is not advisable to use Delete while the content is being edited. You will want to ensure that the user can click the <div> element itself without editing the content.
Since the <div> is draggable, I would advise using a handle since the click event and keypress events may get capture for content editing and not for your script.
$(function() {
function disDrag(part) {
var drag = part.closest(".draggable");
drag.draggable("disable");
$(".drag-content", drag).removeAttr("contenteditable").blur();
part.toggleClass("ui-icon-locked ui-icon-unlocked");
}
function enDrag(part) {
var drag = part.closest(".draggable");
drag.draggable("enable");
$(".drag-content", drag).attr("contenteditable", true).focus();
part.toggleClass("ui-icon-locked ui-icon-unlocked");
}
function delDrag(part) {
var drag = part.closest(".draggable");
var res = confirm("Are you sure you wish to delete this item?");
if (res) {
drag.remove();
}
}
$('.draggable')
.draggable({
containment: "parent",
handle: ".ui-drag-handle",
start: function() {
$(".ui-drag-handle", this).data("selectable", false);
},
stop: function() {
$(".ui-drag-handle", this).data("selectable", true);
}
})
.resizable();
$(".ui-drag-handle")
.data("selectable", true)
.click(function(e) {
var drag = $(this).closest(".draggable");
if ($(this).data("selectable")) {
drag.toggleClass("drag-selected");
}
});
$(".btn").click(function(e) {
switch (true) {
case $(this).hasClass("ui-icon-unlocked"):
disDrag($(this));
break;
case $(this).hasClass("ui-icon-locked"):
enDrag($(this));
break;
case $(this).hasClass("ui-icon-close"):
delDrag($(this));
break;
}
});
$(document).keyup(function(e) {
if (e.which == 46 && $(".drag-selected").length) {
delDrag($(".drag-selected"));
}
});
});
#myContainer {
border: 1px solid black;
height: 400px;
width: 100%;
}
.draggable {
border: 1px solid blue;
}
.draggable.drag-selected {
border: 1px solid #0f0;
}
.center {
margin-left: 50%;
}
.right {
float: right;
}
.ui-icon.btn {
border: 1px solid #ccc;
border-radius: 3px;
margin-top: 0px;
margin-left: 1px;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div id="myContainer">
<div class="draggable ui-widget" id="DraggableDiv">
<div class="ui-widget-header">
<span class="right ui-icon ui-icon-close btn" title="Delete the item."></span>
<span class="right ui-icon ui-icon-unlocked btn" title="Lock and disable Drag"></span>
<div class="ui-drag-handle" style="width: calc(100% - 42px);">
<span class="center ui-icon ui-icon-grip-dotted-horizontal"></span>
</div>
</div>
<div class="drag-content" contenteditable="true">
THIS IS MY TEXT INSIDE THE DIV
</div>
</div>
</div>
You can see that this is draggable, resizable, and editable. The user can disable drag by clicking the lock icon. If the select the div and click Delete (or key code 46), or they click the close icon, they will be prompted to confirm that they want to delete the item. Once they confirm that Yes they want to, the item is removed.
Since the delete could be triggered by two different ways, I created a delete function.
In regards to structure, you may not be able to get away with such simple HTML structures when dealing with more complex UI interactions. This one <div> element had all sorts of interactions tied to the click event. The user clicks to edit, select, drag... It is better to make more specific targets for some of these events so that you can better script your events.
You could save yourself a lot of time by using Dialog Widget: https://jqueryui.com/dialog/
Hope that helps.
Test
Click on the text to select.
Press D to delete. [sadly delete key didn't work on stack overflow. Simply change the key code in the if statement to change the key from D to DELETE]
Explanation
There are two functions that help solve this problem.Select: Selected the div clicked.EventListener:Listens for the keypress and deletes the selected div.
Select function
Global variable selected stores the information on the div selected.
In select function we are fetching the id name of the div clicked by using currentTarget.id from the event object 'e'.
If statements inside the select function select and deselect the div.
EventListener
Uses event object from the keypress listener to fetch the key pressed.
e.keyCode gives the key. e.which is a fallback. [for ie users]
If they keyCode is 100 (D key), then use the selected variable to get the selected div and change its css display to 'none'.
Additionally there is a else statement, where u can add js to when nothing is selected and the key is pressed.Also the css for class selected is for feedback of when the div is selected.
Here is the code snippet:
let selected;
const select = e => {
//If already selected, this will deselect the div
if(selected == e.currentTarget.id) {
document.getElementById(selected).classList.remove('selected'); //some CSS
selected = null;
} else {
//select this div
selected = e.currentTarget.id;
document.getElementById(selected).classList.add('selected'); //some CSS
}
}
window.addEventListener('keypress', e => {
//Get key pressed
let key = e.keyCode || e.which;
if(selected != undefined) {
if(key == 100) {//If D is pressed
let target = document.getElementById(selected); //get the div
target.style.display = 'none'; //hide div
console.log('deleted: ' + selected);
}
} else {
//Runs if nothing is selected. Do as you please here.
}
})
.selected {
background: black;
color: white;
}
#DraggableDiv {
user-select: none;
cursor: pointer;
font-family: sans-serif;
width: 400px;
text-align: center;
padding: 10px 5px;
}
<!DOCTYPE html>
<html>
<head>
<title>My Delete Div</title>
</head>
<body>
<div id="myContainer">
<div id="MyFirstDiv">
<div id="DraggableDiv" onclick="select(event)">
Click me and press D
</div>
</div>
</div>
</body>
</html>
Languages involved: HTML, CSS, JS
Context: I'm relatively new to web development. I have two elements overlapping each other. One is a slider, one is a div. The slider is on top of the div.
Code snippets:
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
and
initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
I need to make it that when you click the slider, it doesn't click the div. How would I go about doing that? I've tried z-index, but that doesn't seem to change anything.
Thanks in advance!
As I'm sure you've figured out by now, events in JavaScript by default bubble up from a child to a parent. You need to stop that from happening at the child level, also known as preventing propagation.
Using the stopPropagation function, you can handle this as follows:
function sliderFunction(e) {
e.stopPropagation();
}
Simple. That event will no longer reach the parent.
EDIT
While stop propagation is the correct method to use, event listeners must also match in type. Therefore, both the slider and the parent DIV must have click event listeners (instead of input and click). stopPropagation stops propagation of a specific type of event.
function divFunction() {
console.log('DIV clicked!');
}
function sliderFunction(event) {
event.stopPropagation();
console.log('Slider clicked!');
}
function initListeners() {
document.getElementById('myDiv').addEventListener('click', divFunction);
document.getElementById('mySlider').addEventListener('click', sliderFunction);
}
initListeners();
/* unnecessary visual aides */
body *:not(label) {
padding: 2rem;
outline: 1px solid red;
position: relative;
}
label {
display: inline-block;
position: absolute;
background: #222;
color: #fff;
top: 0; left: 0;
}
<div id="myDiv">
<label>#myDiv</label>
<div id="tools">
<label>#tools</label>
<input type="range" id="mySlider">
</div>
</div>
You can also check the target once you fire that click event. I've used this approach before:
JSFiddle: http://jsfiddle.net/L4ck7ygo/1/
function divFunction(e) {
if (e.target !== this) {
return;
} else {
console.log('hit');
}
}
When the fiddle first loads, click the slider and you'll see the console log out some text. To see it work, remove the line that is being pointed to and rerun the fiddle. Now when you click the slider, you won't see anything logged in the console, but if you click on the div and not the slider, it will log to the console.
function initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
initListeners();
function divFunction(e) {
console.log('Firing...') // <-- This will log on any click
if (e.target !== this) {
return;
} else {
console.log('hit'); // <-- This will NOT log except for div click
}
}
function sliderFunction() {
console.log('Doing stuffs...');
}
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
UPDATE: Stupidity on my part. I had the ordering wrong for the elements which caused propagation to not act as intended.
Intro
I am extending photoswipe with my own button & modal dialog, similar to built in share dialog.
I already made code that worked, but then followed these modifications to photoswipe:
https://github.com/dimsemenov/PhotoSwipe/issues/1209
Now it doesn't work anymore. Issue is that photoswipe's event handlers get called before mine, so it appears as if user clicked on photoswipe controls and photoswipe hides image, controls & everything and only my modal is visible.
Diagnostics
I have modified onControlsTap and onGlobalTap and my button click to log to console and I see they are fired in this order:
onControlsTap
onGlobalTap
Settings button click
Html on the other hand looks like this:
<div id="globalTapContainer">
<div id="controlTapContainer">
<button id="myButton"></button>
</div>
</div>
And events are registered using addEventListener(..., false)
Code
This is my code which binds to click event
$("#pswp__settings__dropdown_background, .pswp__button--settings")
.click(function(ev) {
console.log('Settings button click');
ev.stopPropagation();
toggleSettings();
});
This is photoswipe code that binds events.
_controls = framework.getChildByClass(pswp.scrollWrap, 'pswp__ui');
// ...
framework.bind(_controls, 'pswpTap click', _onControlsTap);
framework.bind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
var framework = {
// ...
bind: function(target, type, listener, unbind) {
var methodName = (unbind ? 'remove' : 'add') + 'EventListener';
type = type.split(' ');
for(var i = 0; i < type.length; i++) {
if(type[i]) {
target[methodName]( type[i], listener, false);
}
}
}
}
My button and modal are one of child nodes of pswp__ui.
Question
How is it possible that their events are called before mine when I have registered click event to a specific button?
What to do to make photoswipe events not fire when you click on my controls?
I'm not familiar with photoswipe, but its events use a custom event called pswpTap, not click. Presumably this fires when an element is tapped or when the mouse button is pressed. click events don't fire until the mouse button is released, so that would explain why their events are firing before yours.
Example:
$('#outerdiv').on('mousedown', function() {
console.log('outer mousedown');
});
$('#innerdiv').on('click', function() {
console.log('inner click');
});
#outerdiv {
width: 100px;
height: 100px;
background-color: blue;
}
#innerdiv {
width: 40px;
height: 40px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outerdiv">
<div id="innerdiv"></div>
</div>
You should presumably be able to prevent this by having your element handle and cancel the mousedown event. You may also need to add an event handler for tap events if they work differently from mousedown (I'm not sure whether they are).
$('#outerdiv').on('mousedown', function() {
console.log('outer mousedown');
});
$('#innerdiv').on('mousedown', function(event) {
console.log('inner mousedown');
event.stopPropagation();
});
$('#innerdiv').on('click', function() {
console.log('inner click');
});
#outerdiv {
width: 100px;
height: 100px;
background-color: blue;
}
#innerdiv {
width: 40px;
height: 40px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outerdiv">
<div id="innerdiv"></div>
</div>
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!
Basically I want the user to be able to click and change the background, and for there to be multiple backgrounds, for a specific div.
This works perfectly in Google Chrome but not in IE or Firefox.
HTML:
<div id="palette">
<div class="lightblue"></div>
<div class="gold"></div>
<div class="aqua"></div>
</div>
<div id="primary">
</div>
CSS:
#palette {
border: 2px solid white;
width: 25px;
}
#palette div {
height: 25px;
width: 25px;
}
.lightblue {
background: lightblue url(http://www.textureking.com/content/img/stock/big/DSC_4279.JPG);
}
.gold {
background: gold url(http://www.textureking.com/content/img/stock/big/DSC_4287.JPG);
}
.aqua {
background: aqua url(http://www.textureking.com/content/img/stock/big/DSC_4274.JPG);
}
JS:
$(document).ready(function() {
// attach onclick event to your palette colors
$('#palette div').on('click', function() {
// get background of selected palette color
var bg = $(this).css('background');
// change the background of the body
$('#primary').css({ 'background': bg });
});
});
http://jsfiddle.net/KNutQ/1/
It's not showing any errors and other javascripts run, so I'm not really sure what the problem is.
If it's easy to fix please leave an answer, if not I will try it this way: http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_style_backgroundimage
According to the CSS specification getting the computed style of a shorthand should not return anything. You need to list out the individual properties as I've done below.
Perhaps the CSS spec or Chrome will change in the future but at the moment Firefox and IE's behaviour is correct.
$(document).ready(function() {
// attach onclick event to your palette colors
$('#palette div').on('click', function() {
// get background of selected palette color
var backgrounds = $(this).css(['background-color','background-image', 'background-repeat', 'background-attachment', 'background-position']);
// change the background of the body
$('body').css(backgrounds);
});
});