How to trigger droppable event on dragging element over with jQuery UI? - javascript

I'm making a huge project which should be good regarding performance like every software. But I'm struggling about drag and drop objects.
Let me start with my code.
Here is my HTML:
<div class="drag-me"></div>
<div class="drop-on-me"></div>
Here is my JavaScript:
$('.drag-me').draggable();
$('.drop-on-me').hover(function(){
let el = $(this);
el.droppable({
drop: function(){
console.log("dropped!");
}
});
}, function(){
let el = $(this);
el.droppable('destroy');
});
Codepen Example
I need to trigger droppable event on hovering while dragging objects, because there are so many droppable objects in the page and it consumes much of RAM with the browser and the page crashes.
How can I trigger while I'm hovering with draggable object?

You will need to do a level of collision detection. The drag event can block out some other events, like hover from bubbling up. Consider the following code snippet.
$(function() {
function getBounds(el) {
var p = {
tl: $(el).position()
};
p['tr'] = {
top: p.tl.top,
left: p.tl.left + $(el).width()
};
p['bl'] = {
top: p.tl.top + $(el).height(),
left: p.tl.left
};
p['br'] = {
top: p.bl.top,
left: p.tr.left
};
return p;
}
function isOver(el, map) {
var myPos = getBounds(el);
var tObj = false;
$.each(map, function(k, v) {
if (myPos.tl.left > v.tl.left && myPos.tl.left < v.tr.left && myPos.tl.top > v.tl.top && myPos.tl.top < v.bl.top) {
console.log("Over", k);
tObj = $(".drop-on-me").eq(k);
}
});
return tObj;
}
function makeDrop(el) {
if (!$(el).hasClass("ui-droppable")) {
$(el).droppable({
addClasses: false,
drop: function() {
console.log("Item Dropped.");
},
out: function() {
$(this).droppable("destroy");
}
});
}
}
var dropPositions = [];
$(".drop-on-me:visible").each(function(i, el) {
dropPositions.push(getBounds(el));
});
console.log("Mapping complete.", dropPositions);
$('.drag-me').draggable({
start: function() {
console.log("Drag Start.");
},
stop: function() {
console.log("Drag Stop.");
},
drag: function(e, ui) {
var target = isOver(ui.helper, dropPositions);
if (target) {
console.log("Make Drop, Index: " + target.index());
makeDrop(target);
}
}
});
});
.drag-me {
width: 30px;
height: 30px;
background-color: rgba(255, 0, 0, 0.75);
border: 1px solid #000;
border-radius: 3px;
z-index: 300;
}
.drop-on-me {
width: 100px;
height: 100px;
background-color: rgba(0, 0, 255, 0.75);
border: 1px solid #000;
border-radius: 3px;
position: absolute;
}
.drop-on-me.top {
left: 80px;
top: 10px;
}
.drop-on-me.mid {
left: 40px;
top: 120px;
}
.drop-on-me.bot {
left: 240px;
top: 640px;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="drag-me"></div>
<div class="drop-on-me top"></div>
<div class="drop-on-me mid"></div>
<div class="drop-on-me bot"></div>

Related

How to run the Callback function inside of the prototype in JavaScript?

According to this question and mdn.doc articles, I'm giving a Callback function inside of aprototype for managing the next code line after it's done.
But even if I create the Callback, the browser keeps ignoring it and running the next code line no matter the Callback is completed or not.
This is the code:
'use strict';
(function() {
function Box($el, $frame) {
// Reassign the Values
this.$el = $el;
this.$frame = $frame;
// Event Register Zone
this.$el.addEventListener('touchstart', (e) => this.start(e));
this.$el.addEventListener('touchmove', (e) => this.move(e));
this.$el.addEventListener('touchend', (e) => this.end(e));
}
Box.prototype = {
start: function(e) {
console.log('touchstart has been detected');
},
move: function(e) {
console.log('touchmove has been detected');
},
end: function(e) {
console.log('touchend has been detected');
this.getanAction(this.moveTop);
},
getanAction: function(callback) {
let bound = callback.bind(this);
bound();
this.$frame[1].classList.add('leftMover');
// Expectation: move the purple box first, and move the orange box next
},
moveTop: function() {
this.$frame[0].classList.add('topMover');
}
}
/***************************************************************/
// Declare & Assign the Original Values
let _elem = document.getElementById('box');
let _frame = _elem.querySelectorAll('.contents');
const proBox = new Box(_elem, _frame);
}());
* {
margin: 0;
padding: 0;
}
#box {
width: auto;
height: 800px;
border: 4px dotted black;
}
.contents {
position: absolute;
width: 200px;
height: 200px;
float: left;
top: 0;
left: 0;
transition: 800ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
.purple { background-color: purple; }
.orange { background-color: orange; }
.topMover { top: 600px; }
.leftMover { left: 600px; }
<div id="box">
<div class="contents purple">
</div>
<div class="contents orange">
</div>
</div>
My expectation is the .orange box moves after the .purple box moves done.
Did I miss or do something wrong from the code?
The problem is they are being called one after the other with no delay as JavaScript won't wait for the CSS transition to finish before moving to the next line.
I've fixed waiting for the first transition has finished before calling the bound callback. This way the purple box will move, wait for the transition to finish, then the orange box will move.
'use strict';
(function() {
function Box($el, $frame) {
// Reassign the Values
this.$el = $el;
this.$frame = $frame;
// Event Register Zone
this.$el.addEventListener('touchstart', (e) => this.start(e));
this.$el.addEventListener('touchmove', (e) => this.move(e));
// Added mouse up so it works on desktop
this.$el.addEventListener('mouseup', (e) => this.end(e));
this.$el.addEventListener('touchend', (e) => this.end(e));
}
Box.prototype = {
start: function(e) {
console.log('touchstart has been detected');
},
move: function(e) {
console.log('touchmove has been detected');
},
end: function(e) {
console.log('touchend has been detected');
this.getanAction(this.moveTop);
},
getanAction: function(callback) {
let bound = callback.bind(this);
// Listen for css transition end
this.$frame[0].addEventListener('transitionend', function() {
// Call callback to move orange box
bound()
});
// Move the purple box now
this.$frame[0].classList.add('topMover1')
},
moveTop: function() {
this.$frame[1].classList.add('topMover2');
}
}
/***************************************************************/
// Declare & Assign the Original Values
let _elem = document.getElementById('box');
let _frame = _elem.querySelectorAll('.contents');
const proBox = new Box(_elem, _frame);
}());
* {
margin: 0;
padding: 0;
}
#box {
width: auto;
height: 800px;
border: 4px dotted black;
}
.contents {
position: absolute;
width: 200px;
height: 200px;
float: left;
top: 0;
left: 0;
transition: 800ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
.purple { background-color: purple; }
.orange { background-color: orange; }
.topMover1 { top: 600px; }
.topMover2 { left: 600px; }
<div id="box">
<div class="contents purple">
</div>
<div class="contents orange">
</div>
</div>

How to clone selected divs

Javascript report designer should allow to create copies of selected divs into same panel.
I tried to use
function DesignerClone() {
$(".ui-selected").each(function () {
var newDiv = $(this).prop('outerHTML'),
parentpanel = $(this).parent(".designer-panel-body");
parentpanel.prepend(newDiv);
});
}
but all divs are lost. and empty panel appears.
To reproduce, run code snippet and select some divs by mouse click.
After that press clone button.
How to clone boxes ?
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<style>
.designer-panel-body {
min-height: 1px;
overflow: hidden;
margin: 0;
padding: 0;
}
.panel-footer {
background-color: inherit;
}
.designer-panel,
.designer-resetmargins {
margin: 0;
padding: 0;
}
.designer-verticalline,
.designer-horizontalline,
.designer-rectangle {
font-size: 1pt;
border: 1px solid #000000;
}
.designer-field {
border: 1px solid lightgray;
white-space: pre;
overflow: hidden;
}
.ui-selecting {
background-color: lightskyblue;
color: white;
}
.ui-selected {
background-color: lightskyblue;
border-color: darkblue;
color: white;
}
.designer-label {
white-space: pre;
}
.designer-field,
.designer-label {
font-family: "Times New Roman";
font-size: 10pt;
z-index: 2;
}
.designer-verticalline,
.designer-horizontalline,
.designer-rectangle,
.designer-field,
.designer-image,
.designer-label {
position: absolute;
}
</style>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script>
function DesignerClone() {
$(".ui-selected").each(function () {
var newDiv = $(this).prop('outerHTML'),
parentpanel = $(this).parent(".designer-panel-body");
parentpanel.prepend(newDiv);
});
}
function getpos(e) {
return {
X: e.pageX,
Y: e.pageY
};
}
function Rect(start, stop) {
this.left = Math.min(start.X, stop.X);
this.top = Math.min(start.Y, stop.Y);
this.width = Math.abs(stop.X - start.X);
this.height = Math.abs(stop.Y - start.Y);
}
$(function () {
var startpos;
var selected = $([]),
offset = {
top: 0,
left: 0
};
$(".designer-verticalline, .designer-rectangle, .designer-field, .designer-image").resizable();
var $liigutatavad = $(".designer-verticalline, .designer-horizontalline, .designer-rectangle, .designer-field, .designer-image, .designer-label");
$liigutatavad.draggable({
start: function (event, ui) {
var $this = $(this);
if ($this.hasClass("ui-selected")) {
// if this is selected, attach current offset
// of each selected element to that element
selected = $(".ui-selected").each(function () {
var el = $(this);
el.data("offset", el.offset());
});
} else {
// if this is not selected, clear current selection
selected = $([]);
$liigutatavad.removeClass("ui-selected");
}
offset = $this.offset();
},
drag: function (event, ui) {
// drag all selected elements simultaneously
var dt = ui.position.top - offset.top,
dl = ui.position.left - offset.left;
selected.not(this).each(function () {
var $this = $(this);
var elOffset = $this.data("offset");
$this.css({
top: elOffset.top + dt,
left: elOffset.left + dl
});
});
}
});
// ...but manually implement selection to prevent interference from draggable()
$(".designer-panel-body").on("click", "div", function (e) {
if ( /*!e.metaKey &&*/ !e.shiftKey && !e.ctrlKey) {
// deselect other elements if meta/shift not held down
$(".designer-panel-body").removeClass("ui-selected");
$(this).addClass("ui-selected");
} else {
if ($(this).hasClass("ui-selected")) {
$(this).removeClass("ui-selected");
} else {
$(this).addClass("ui-selected");
}
}
});
$(".designer-panel-body").selectable({});
});
</script>
</head>
<body>
<button type="button" class="btn btn-default" onclick="javascript:false;DesignerClone()">
<span class="glyphicon glyphicon-paste"></span>
</button>
<div class='panel designer-panel'>
<div class='panel-body designer-panel-body panel-warning' style='height:4cm'>
<div class='designer-field' contenteditable='true' style='top:2.30cm;left:5.84cm;width:10.24cm;height:0.63cm;font-family:Arial;font-size:14pt;font-weight:bold;'>vnimi+' '+dok.tasudok</div>
<div class='designer-field' contenteditable='true' style='top:2.30cm;left:16.37cm;width:2.68cm;height:0.61cm;font-size:14pt;'>DOK.kuupaev</div>
<div class='rectangle' style='border-width: 1px;background-color:#FFFFFF;top:2.99cm;left:1.34cm;width:18.05cm;height:5.29cm'></div>
<div class='designer-field' contenteditable='true' style='top:3.01cm;left:1.53cm;width:9.71cm;height:0.55cm;font-size:12pt;'>m.FIRMA</div>
<div class='designer-field' contenteditable='true' style='top:3.01cm;left:12.13cm;width:3.13cm;height:0.53cm;font-size:12pt;'>ise.telefon</div>
</div>
<div class='bg-warning'>
<div class='panel-footer'><i class='glyphicon glyphicon-chevron-up'></i> GroupHeader 1: str(dokumnr)+str(koopia,2)</div>
</div>
</div>
</body>
</html>
.appendTo takes selected element and removes it from previous position in the DOM.
jQuery.clone() is what you might be looking for.

my drag & drop is not working

i want move my #timebase1 div into draghere div. now its move only the start of the div, i want to drop it in anywhere inside the dragehere div.
function funct(e) {
var id = e.id;
mouseXY(id);
}
function mouseXY(id) {
//alert(id);
var x = event.pageX,
y = event.pageY
$('#' + id).css({
top: y,
left: x + ''
});
}
.activelevel1 {
background-color: #EA623E;
}
.timescalebase {
margin-top: 13px;
height: 7px;
position: relative;
width:0px;
}
<div id="draghere"style="width:100%;margin-top:25px;">
<div id="timebase1"draggable="true"class="timescalebase activelevel1" ondrag=funct(this)>
</div>
You must allowdrop on your target div like this :
function funct(e) {
var id = e.id;
mouseXY(id);
}
function allowDrop(ev) {
ev.preventDefault();
}
function mouseXY(id) {
var x = event.pageX,
y = event.pageY
$('#' + id).css({
left: x
});
}
.activelevel1 {
background-color: red;
width: 10px;
height: 10px;
}
.timescalebase {
margin-top: 13px;
height: 7px;
position: relative;
}
#draghere {
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="draghere" style="width:100%;margin-top:25px;" ondragover="allowDrop(event)">
<div id="timebase1" draggable="true" class="timescalebase activelevel1" ondrag="javascript:funct(this)">
</div>
</div>
EDIT
One example with jquery-ui :
https://jsfiddle.net/2gaq2r15/
$(document).ready(function(e) {
$("#timebase1").draggable({
containment: "#draghere",
axis: "x"
});
});

How do I change the placeholder like as the elements replace each other place?

How do I change the placeholder like as the elements replace each other place.
Please see the example
https://jsfiddle.net/98h31o9v/11/
JavaScript
indexOfCell = 0;
add boxes to #div element
$('#add_box').on('click', function() {
var cell = $("<div></div>");
var elementObj = cell.get(0);
$('#div').append(elementObj);
cell.addClass('content-box').attr('id', 'box_' + indexOfCell);
cell.text(indexOfCell);
indexOfCell += 1;
console.log(elementObj);
$(cell).draggable({
helper: 'original',
zIndex: 10001,
start: function(event, ui) {
if ($(this).data('placeholder') === undefined) {
$(this).data('placeholder', createPlaceholder($(this)));
}
setPlaceHolder($(this).data('placeholder'), $(this));
$(this).after($(this).data('placeholder'));
},
stop: function(event, ui) {
$(this).css('left', $(this).data('placeholder').css('left'));
$(this).css('top', $(this).data('placeholder').css('top'));
$(this).data('placeholder').after($(this));
$(this).data('placeholder').detach();
}
});
$(cell).droppable({
tolerance: 'intersect',
greedy: true,
over: function(event, ui) {
replaceTwoItem(ui.draggable.data('placeholder'), $(this));
}
});
create placeholder
function createPlaceholder(that) {
var className = that.attr('class');
var placeholder = $(document.createElement(that.get(0).nodeName))
.addClass(className || className + " ui-sortable-placeholder")
.removeClass("ui-sortable-helper").css({
background: 'yellow',
border: '1px solid grey'
});
return placeholder;
}
set the placeholder to cell
function setPlaceHolder(placeholder, cell) {
placeholder.css('width', cell.width());
placeholder.css('height', cell.height());
placeholder.css("display", 'block');
placeholder.css('position', 'absolute');
placeholder.css('top', cell.css('top'));
placeholder.css('left', cell.css('left'));
}
replace two item when drag
function replaceTwoItem(itemFrom, itemTo) {
var itemToInsert;
var action;
if (itemFrom.index() === 0) {
itemToInsert = itemFrom.parent();
action = "prepend";
} else {
itemToInsert = itemFrom.prev();
action = "after";
}
itemTo.before(itemFrom);
if (itemTo.get(0) != itemToInsert.get(0)) {
if (action == 'prepend') {
itemToInsert.prepend(itemTo);
} else if (action == 'after') {
itemToInsert.after(itemTo);
}
}
}
});
HTML
<button id="add_box">AddBox</button>
<div id="div">
</div>
CSS
.content-box {
width: 100px;
height: 100px;
background: green;
margin: 5px;
float: left;
}
After the brief discussion in the comments about your requirements I think you can get rid of most of the code you're currently using. The example on the jquery
ui website can be tweaked slightly to get what you want.
Fiddle Example
HTML:
<button id="add_box">AddBox</button>
<div id="sortable" class="ui-sortable">
</div>
JQuery:
$('#add_box').on('click', function() {
//Determine the existing child count.
var boxCount = $('#sortable').children().length;
//Create a new "box" and add it to the end of the list.
var newBox = $('<div class="ui-state-default">' + boxCount + '</div>');
$('#sortable').append(newBox);
//Invoke the sortable function to ensure the appropriate events are bound.
$("#sortable").sortable({
placeholder: "ui-state-highlight"
});
});
CSS:
The below can be cleaned up somewhat.
#sortable {
margin: 0;
padding: 0;
}
.ui-state-highlight {
background: yellow;
margin: 0 5px 5px 5px;
color: black;
float: left;
width: 100px;
height: 100px;
}
.ui-state-default {
background: green;
margin: 0 5px 5px 5px;
color: black;
float: left;
width: 100px;
height: 100px;
}
Update .ui-state-default to change the initial colour and update .ui-state-highlight to change the placeholder colour.

Adding DIVs step-by-step...1, 2, 3, 4, 8, 16

I'm trying to create div boxes step by step and animate them for several times when a button is pressed. I have a running code, and everything is going well. It goes right to the endhost, then it goes left again to its original place. This is mainly what I do, and also the demo is found here: http://jsfiddle.net/LSegC/1/
Now what I want to do is to increase the number of whole animated DIVs one-by-one (as it is now) up to 3 Divs, but then have exponential increase on the total number of DIVs. So the total number of animated DIVs will be like 1, 2, 3, and then 4, 8, 16, etc.
Remember, my problem is not with the number being shown inside the DIV, it's actually that how many DIVS are being created! So I want for instance 8 DIVs, numbered from 1 to 8 animated. Hope it is now clear.
$(document).ready(function(){
$("button").click(function() {
var d = $(".t").fadeIn();
var speed = +$("#number1").val();
d.animate({left:'+=230px'}, speed);
d.animate({left:'+=230px'}, speed);
d.animate({top:'+=20px', backgroundColor: "#f09090", text:'12'}, speed/4, "swing", function() {
$('.span', this).fadeOut(100, function() {
$(this).text(function() {
return 'a' + $(this).text().replace('a', '');
}).fadeIn(100);
});
});
d.delay(1000).animate({left:'-=230px'}, speed);
d.animate({left:'-=230px'}, speed);
d.fadeOut().promise().done(function() {
d.last().after(function() {
var top = +$(this).css('top').replace('px', ''),
number = +$(this).data('number') + 1,
$clone = $(this).clone();
$clone.data('number', number).css('top', top + 20);
$clone.find('.span').text(number);
return $clone;
});
d.find('.span').text(function() {
return $(this).text().replace('a', '');
});
})
});
EDIT
Your code was too hard to manipulate as it was, I recreated the whole thing:
HTML:
<img id="streamline1" src="https://cdn3.iconfinder.com/data/icons/streamline-icon-set-free-pack/48/Streamline-04-48.png" />
<img id="LAN" src="https://cdn1.iconfinder.com/data/icons/ecqlipse2/NETWORK%20-%20LAN.png" />
<img src="https://cdn3.iconfinder.com/data/icons/streamline-icon-set-free-pack/48/Streamline-04-48.png" id="streamline" />
<div id="mid"></div>
<div id="bottom"></div>
<div>Speed (mS):
<input value="500" id="speed" type="number" style="position: relative"></input>
<button>Apply!</button>
<!-- dynamic area -->
<div class="packets"></div>
</div>
JS:
$(document).ready(function () {
var count = 0;
var items = 0;
var packetNumber = 0;
var speed = 0;
$("button").click(function () {
if (count < 4) {
items = items + 1;
count++;
} else {
items = items * 2;
}
speed = $("#speed").val();
createDivs(items);
animateDivs();
});
function createDivs(divs) {
packetNumber = 1;
var left = 60;
for (var i = 0; i < divs; i++) {
var div = $("<div class='t'></div>");
div.appendTo(".packets");
$("<font class='span'>" + packetNumber + "</font>").appendTo(div);
packetNumber++;
div.css("left",left+"px");
div.hide();
left += 20;
}
}
function animateDivs() {
$(".t").each(function () {
var packet = $(this);
packet.show();
packet.animate({
left: '+=230px'
}, speed);
packet.animate({
left: '+=230px'
}, speed);
packet.animate({
top: '+=20px',
backgroundColor: "#f09090",
text: '12'
}, speed / 4, "swing", function () {
$('.span').fadeOut(100, function () {
$(this).text(function () {
return 'a' + $(this).text().replace('a', '');
}).fadeIn(100);
});
});
packet.delay(1000).animate({left:'-=230px'}, speed);
packet.animate({left:'-=230px'}, speed);
}).promise().done(function(){
$(".packets").empty();});
}
});
CSS:
#bottom {
border: 1px dashed gray;
position: absolute;
left: 55px;
height: 20px;
width: 500px;
opacity: 0.5;
top: 30px;
z-index=-1;
}
#mid {
border: 1px dashed gray;
position: absolute;
left: 55px;
height: 20px;
width: 500px;
opacity: 0.5;
top: 10px;
z-index=-1;
}
.t {
display: inline-block;
position: absolute;
top: 10px;
left: 60px;
text-align: center;
vertical-align: middle;
width: 20px;
height: 20px;
background-color: lightgreen
}
#streamline {
width: 50px;
height: 50px;
right: 0px;
position: fixed;
left: 548px;
}
#streamline1 {
left: 0px;
width: 50px;
height: 50px;
}
#LAN {
width: 50px;
height: 50px;
left: 275px;
position: fixed;
}
.packets {
display: inline;
}
FIDDLE: http://jsfiddle.net/54hqm/3/
It was tough for me to follow the code also, but I cut it back quite a bit, came up with a "one-way" "empiric" approach. FIDDLE
The speed can be adjusted by the change in the increment (inc), but there are a variety of methods that can be used.
Can you be more specific about what you mean by "exponential"? Do you mean an exponential speed increase across the div, or rather a speed increase until you get to 50%, then a decrement in speed.
JS
$("button").click(function() {
var speed = 1000;
var d = $('.mover');
d.show();
var inc = 1;
for (var i=0; i<290; i=i+inc)
{
d.animate({ left: i,
easing: 'linear'}, 1);
if (inc < 11)
{
inc = inc + 1;
} else {
inc = inc - 1;
}
}
});

Categories