Here I have been able to drop elements onto a canvas and create connections between them. But every time I drag a dropped element within the canvas, the anchors do not move along with the dragged element. Instead when I try to create a connection from the isolated anchor to another element it immediately re-positions itself with its parent element. This is one issue and I would also like to delete the anchors/ connections whenever its parent element is deleted.
<!doctype html>
<html>
<head>
<script src="../lib/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="../lib/jquery-ui.min.js"></script>
<script src="../lib/jquery.jsPlumb-1.6.4-min.js"></script>
<style>
.chevron-toolbox{
position: absolute;
width: 72px;
height: 80px;
background-color: powderblue;
background-image: url("../dist/img/bigdot.png");
border: solid 3px red;
}
#dropArea{
cursor: pointer;
border: solid 1px gray;
width: 800px;
margin-left: 80px;
height: 400px;
position: relative;
overflow-x: scroll;
overflow-y: scroll;
}
.chevron {
position:absolute;
cursor:pointer;
width: 72px;
height: 80px;
background-color: powderblue;
background-image: url("../dist/img/bigdot.png");
}
</style>
</head>
<body>
<div class="chevron-toolbox" id="cId">
</div>
<div id="dropArea">
</div>
<button id="go">Double Click Me</button>
<script>
jsPlumb.ready(function(e)
{
jsPlumb.setContainer($('#dropArea'));
$(".chevron-toolbox").draggable
({
helper : 'clone',
cursor : 'pointer',
tolerance : 'fit',
revert : true
});
$("#dropArea").droppable
({
accept : '.chevron-toolbox',
containment : 'dropArea',
drop : function (e, ui) {
droppedElement = ui.helper.clone();
ui.helper.remove();
$(droppedElement).removeAttr("class");
jsPlumb.repaint(ui.helper);
$(droppedElement).addClass("chevron");
$(droppedElement).draggable({containment: "dropArea"});
$(droppedElement).appendTo('#dropArea');
setId(droppedElement);
var droppedId = $(droppedElement).attr('id');
var common = {
isSource:true,
isTarget:true,
connector: ["Flowchart"],
};
jsPlumb.addEndpoint(droppedId, {
anchors:["Right"]
}, common);
jsPlumb.addEndpoint(droppedId, {
anchors:["Left"]
}, common);
alert(droppedId);
//Delete an element on double click
var dataToPass = {msg: "Confirm deletion of Item"};
$(droppedElement).dblclick(dataToPass, function(event) {
alert(event.data.msg);
$(this).remove();
});
}
});
//Set a unique ID for each dropped Element
var indexer = 0;
function setId(element){
indexer++;
element.attr("id",indexer);
}
});
</script>
</body>
</html>
In order to properly manipulate the connections, you can use the connect method in jsPlumb placing anchors at desired points.
jsPlumb.connect({
source:'window2',
target:'window3',
paintStyle:{lineWidth:8, strokeStyle:'rgb(189,11,11 )'},
anchors:["Bottom", "Top"],
endpoint:"Rectangle"
});
This is merely an example. Following this pattern in your implementation will be useful when it comes to accessing details regarding those connections and deleting the connections alongside the elements
Related
$('#add').click(function () {
$('#taskCont').append('<div class="task"></div>');
$('.task').append('<input type="checkbox">', '<div></div>', '<small>Delete</small>');
});
.task{
width : 200px;
height : 50px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="add">add</p>
<div id="taskCont"></div>
Click on the add button multiple time. How can I get rid of this problem?
You're appending to .task each time which selects more divs as they are added. Instead, create a variable each time and append that.
$('#add').click(function() {
let task = $('<div class="task"></div>')
task.append('<input type="checkbox">', '<div></div>', '<small>Delete</small>')
$('#taskCont').append(task)
});
.task {
width: 200px;
height: 50px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="add">add</p>
<div id="taskCont"></div>
You only want to append to the last task?
$('.task:last-of-type').append(
In my web app, there is a draggable element.
I need to set the left position of this element when the element reaches a certain limit while dragging.
Using jQuery draggable widget, I have access to the position of the element:
function drag(e, ui) {
console.log(ui.position.left);
}
Let say my left attribute is setted to 1100px, I need to set it to 500px and this, without stopping the dragging.
I have three functions: dragStart, drag, and gradEnd.
Currently, I managed to get only one result: when setting ui.position.left = 500; on the drag function (using a condition), the left position is set to 500 but of course, the element is then stuck at 500px. The reason is that every time the drag function is triggered, the left position is setted to 500.
If the code runs only once the line ui.position.left = 500; the position left attribute is set to 500, but directly reset to 1100.
How can I set the left attribute once and for all?
$("#divId").draggable({
drag: drag,
})
function drag(e, ui) {
if (ui.position.top > 50) {
ui.position.left = 100;
}
}
#divId {
height: 70px;
background-color: white;
border: 4px solid #000000;
text-align: center;
color: black;
cursor: grab;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="divId">
Bubble
</div>
I am not sure how jQuery Draggable handles things under the hood, but even after setting ui.position.left = 100, it does not register in the event until after dragging has stopped - that is why I opted to check the actual CSS property of the element that is being targeted.
I have also provided an example (closure/functional based) which demonstrates how to handle this without having to check CSS..
First example:
$("#divId").draggable({
drag: drag
});
function drag(e, ui) {
if (ui.position.top > 50) {
$("#container").css('padding-left', '100px');
$(this).css('left', '0px');
}
if (ui.position.left < 0) {
ui.position.left = 0
}
}
#divId {
height: 70px;
background-color: white;
border: 4px solid #000000;
text-align: center;
color: black;
width: 300px;
cursor: grab;
}
#container {
height: 100vh;
width: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="container">
<div id="divId">
Bubble
</div>
</div>
Second example, more of a 'closure based functional approach': does not require you to check CSS..
$("#divId").draggable({
drag: drag()
});
function drag(e, ui) {
let TRIGGER = false, TOP_THRESHOLD = 50, LEFT_POSITION = 100;
return function(e, ui) {
if (TRIGGER) {
ui.position.left = LEFT_POSITION;
} else if (ui.position.top > TOP_THRESHOLD) {
TRIGGER = true;
ui.position.left = LEFT_POSITION;
}
}
}
#divId {
height: 70px;
background-color: white;
border: 4px solid #000000;
text-align: center;
color: black;
cursor: grab;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="divId">
Bubble
</div>
I'm working on a image gallery based on OpenSeaDragon, and I'd like to be able to use overlays in collection mode. Based on the various examples on the OSD website (http://openseadragon.github.io/) I managed to hack together a minimal working example, but there are several issues I've not been able to fix (see https://jsfiddle.net/7ox0hg9L/).
First, the on/off overlay toggle works fine, but if I pan/zoom the image the overlay reappears, even though toggle-off deletes the element from the DOM using parentNode.removeChild().
Second, I can't seem to get the overlay tooltips to work consistently on the first page, and they never appear on the following pages. The tooltip on the radiobutton label works fine on any page though, so I'm not sure why the tooltips on the overlays do not.
Any suggestion would be welcome. Please bear in mind that I am new to javascript. Thanks!
EDIT: iangilman's answer below and his edits on jsfiddle put me back on track, and helped me create the gallery I had in mind. I post here the full solution for those who may need similar features. Thanks Ian!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openseadragon/2.3.1/openseadragon.min.js"></script>
<style>
body {
margin: 0;
color: #333;
font-family: Helvetica, Arial, FreeSans, san-serif;
background-color: #121621;
}
.openseadragon{
width: 800px;
height: 600px;
border: 1px solid black;
color: #333;
background-color: black;
}
.highlight{
opacity: 0.4;
filter: alpha(opacity=40);
outline: 6px auto #0A7EbE;
background-color: white;
}
.highlight:hover, .highlight:focus{
filter: alpha(opacity=70);
opacity: 0.7;
background-color: transparent;
}
.nav {
cursor: pointer;
display: inline-block;
font-size: 25px;
}
.controls {
text-align: center;
display: table;
background-color: #eee;
table-layout: fixed;
width: 800px;
}
</style>
</head>
<body>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="controls">
<label class="labels"><input id="showOverlays" type="checkbox"><a id="selector" title="">Show overlays</a></label>
<a class="nav previous" title="Previous" id="prv"> < </a>
<a class="nav next" title="Next" id="nxt"> > </a>
</div>
<div id="example-runtime-overlay" class="openseadragon" />
<script type="text/javascript">
var tileSource = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
Format: "jpg",
Overlap: "2",
TileSize: "256",
Size: {
Height: "9221",
Width: "7026"
}
}
};
var runtimeViewer = OpenSeadragon({
id: "example-runtime-overlay",
prefixUrl: "openseadragon/images/",
showSequenceControl: true,
sequenceMode: true,
nextButton: "nxt",
previousButton: "prv",
tileSources: [{
tileSource: tileSource,
overlay: [{
id: 'example-overlay',
x: 0.43,
y: 0.47,
width: 0.15,
height: 0.20,
className: 'highlight',
caption: 'Nice painting'
}]
},{
tileSource: tileSource,
overlay: [{
id: 'example-overlay',
x: 0.65,
y: 0.05,
width: 0.12,
height: 0.12,
className: 'highlight',
caption: 'Milton'
}]
}]
});
var page = 0;
runtimeViewer.addHandler("page", function (data) {
page = data.page;
});
$('.next').click(function() {
radio.prop('checked', false);
});
$('.previous').click(function() {
radio.prop('checked', false);
});
var radio = $('#showOverlays')
.prop('checked', false)
.change(function() {
if (radio.prop('checked')) {
var overlay = runtimeViewer.tileSources[page].overlay[0];
var elt = document.createElement("div");
elt.id = overlay.id;
elt.className = overlay.className;
elt.title = "";
$(elt).tooltip({
content: overlay.caption
});
runtimeViewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(overlay.x, overlay.y, overlay.width, overlay.height)
});
} else {
var overlay = runtimeViewer.tileSources[page].overlay[0];
var element = document.getElementById(overlay.id);
if (element) {
runtimeViewer.removeOverlay(element);
delete element;
}
}
});
$(function() {
$(document).tooltip();
});
</script>
</body>
</html>
Looks like you're off to a good start!
You're correctly adding the overlays with addOverlay, so you need to remove them with removeOverlay:
runtimeViewer.removeOverlay(element);
For the tooltips, unfortunately OpenSeadragon's event handling can interfere with jQuery, so you'll have to use the OpenSeadragon MouseTracker:
function bindTooltip(elt) {
new OpenSeadragon.MouseTracker({
element: elt,
enterHandler: function(event) {
// Show tooltip
},
exitHandler: function(event) {
// Hide tooltip
}
}).setTracking(true);
}
I am trying to change the cursor of the draggable item in chrome. Everything i tried it is not working. There are solution on Stackoverflow but they are all outdated and not working with the actual chrome version.
On drag the item is copied to a container which is the dragimage for the draggable.
What i want is to have a grabbing cursor while dragging. How would that be possible? Any Ideas?
See my code snippet for an example.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area',
drag_elements: [
{text: 'one', selected: true},
{text: 'two', selected: false},
{text: 'three', selected: false},
{text: 'four', selected: false},
]
},
computed: {
selected_elements(){
let selected = [];
this.drag_elements.map((drag) => {
if(drag.selected){
selected.push(drag);
}
})
return selected;
}
},
methods: {
drag_it(event){
let html = document.getElementById("dragElement");
let drop_docs = this.selected_elements;
if(drop_docs.length > 1){
let multiple = document.createElement('div');
multiple.classList.add('dragMultiple');
multiple.innerHTML = drop_docs.length + ' items';
html.innerHTML = '';
html.appendChild(multiple)
}else{
html.innerHTML = event.target.outerHTML;
}
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.setDragImage(html, 0, 0);
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
},
select(event, drag_element){
if(event.metaKey || event.shiftKey){
drag_element.selected = !drag_element.selected;
} else {
this.drag_elements.map((drag) => {
if(drag === drag_element){
drag.selected = true;
}else{
drag.selected = false;
}
})
}
}
}
})
#Dragme{
width: 200px;
height: 50px;
margin-left: 20px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 500px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
.selected{
border: 2px solid yellow !important;
}
.dragMultiple{
border: 1px solid black;
padding: 10px;
background-color: white;
}
#dragElement{
position: absolute;
top: 400px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div id="drag_elements">
<div v-for="drag in drag_elements"
#dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
#mouseup="select($event, drag)"
draggable="true"
:class="{selected: drag.selected}"
id="Dragme">{{drag.text}}</div>
</div>
</div>
<div id="dragElement">
</div>
Update
Actually it can be solved with the following answer
CSS for grabbing cursors (drag & drop)
It is important to add the dndclass
thx
Blockquote
#Carr for the hint
Update
After Dragend or drop the cursor is not set to default. Only when moved it changes back. Any Ideas?
Update
With they command key on mac or the shift key multiple items can be selected and dragged. A new dragitem is created for that purpose but the cursor does not allways fall back after dragend or drop.
Update
Integrate method to from answer -by Carr
In fact, setDragImage api is to set the image for replacing that plain document icon which be aside with default cursor, not cursor itself. So your code about '.dragElement' is not working as you expected, it's unstable and causes weird effect when I am testing, I have removed them in my answer.
What I've done below is a little bit tricky, but I think it's at least in correct logic. However, maybe there is a more elegant solution.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area'
},
methods: {
drag_it(event){
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
}
}
})
#Dragme{
width: 200px;
height: 50px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 300px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div #dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
draggable="true"
id="Dragme">{{text_drag}}</div>
</div>
Update - derivative problems about original question
"dragImage" sticks at bottom, all elements are disappeared, or flashing sometimes.
And here is still a weird part, id attribute should be unique:
And add quote from MDN document about setDragImage, I wrongly recalled svg in comment, it should be canvas :
... The image will typically be an <image> element but it can also be a
<canvas> or any other image element. ...
We could draw text in canvas, it's another question.
I am implementing a 'blades' experience in a page. When I append an additional Blade into the Container...the previous blades 'pop' down.
Q: How do I append a new element into view without effecting previous elements?
MY FIDDLE:
I created a JSFiddle...but the service is not currently available...I will append it shortly.
https://jsfiddle.net/PrisonerZ3RO/oynae1hd/4/#
MY CSS:
<style>
/** DASHBOARD CONTAINER **/
.dashboard-container { border-right: solid 1px #000; margin-top: 5px; margin-bottom: 5px; overflow-x: scroll; white-space: nowrap; width: 100%; }
.dashboard-container .widget { clear: both; display: inline-block; vertical-align: top; }
/** FORM CONTAINER **/
.form-container { border: 1px solid #ccc; border-radius: 3px; height: 500px; margin-bottom: 5px; padding: 5px; width: 500px; }
/** BLADE CONTAINER **/
.blade-container .blade { border: 1px solid #ccc; border-radius: 3px; display: inline-block; height: 506px; margin-right: 2px; padding: 2px; width: 200px; }
</style>
MY HTML:
<script id="tmplBlade" type="text/template">
<div class="blade">
Blade
</div>
</script>
<div class="dashboard-container">
<div class="widget">
<div class="form-container">
Form Controls go here
<input id="btnAppend" type="button" value="Append Blade" />
</div>
</div>
<div class="widget">
<div class="blade-container">
</div>
</div>
</div>
MY JAVASCRIPT:
<script type="text/javascript">
$(document).ready(function () {
function PageController()
{
var that = this,
dictionary = {
elements: { btnAppend: null, bladeContainer: null },
selectors: { btnAppend: '#btnAppend', bladeContainer: '.blade-container', tmplBlade: '#tmplBlade' }
};
var initialize = function () {
// Elements
dictionary.elements.btnAppend = $(dictionary.selectors.btnAppend);
dictionary.elements.bladeContainer = $(dictionary.selectors.bladeContainer);
// Events
dictionary.elements.btnAppend.on('click', that.on.click.btnAppend);
};
this.on = {
click: {
btnAppend: function (e) {
var html = $(dictionary.selectors.tmplBlade).html().trim();
var $element = $(html);
$element.hide();
dictionary.elements.bladeContainer.prepend($element);
// Slide-in
$element.show('slide', { direction: 'left' });
}
}
};
initialize();
}
var pageController = new PageController();
});
</script>
I've come across this problem before. The only way I've found to get around it is to do the following:
1) Create a .hidden class width margin-left: -200px
2) Add a CSS transition on margin-left to the .blade class
3) Apply the .hidden class to a new blade
4) Show the new blade
5) Remove the .hidden class from the new blade
Please see the following fork of your fiddle for a working solution: https://jsfiddle.net/yxL4embt/
How do I append a new element into view without effecting previous elements?
I'm not sure if I entirely get what you're asking since you'll always be affecting the other elements by moving them over when you append a new element. You can, however, prevent the pop-down effect you're seeing. The .ui-effects-wrapperadded by jQuery UI is display: block, so add the following to your CSS:
.blade-container .ui-effects-wrapper {
display: inline-block !important;
}
Then make sure your other blades are always aligned to the top of your container:
.blade-container .blade {
...
...
vertical-align: top;
}
This will bump all the blades over (right) and allow a new blade to slide in from the left.