keep helper under mouse when dragging - javascript

I'm trying to implement something very close to what the 'Sortable Widget' would do, though I can't use it because of other things that doesn't work with the premade widget.
So I'm trying to recreate it's functionality with draggable and droppable elements:
$(".Element").draggable({
helper: 'original',
drag: function(event, ui) {
ElementWidth = $(this).outerWidth(true);
if($(this).prev().length){
LeftElementWidth = $(this).prev().outerWidth(true);
LeftElementLeftOffset = $(this).prev().offset().left;
if(parseFloat(ui.offset.left+(ElementWidth/2)) < parseFloat(LeftElementLeftOffset+(LeftElementWidth/2))){
$(this).prev().before($(this));
}
}
if($(this).next().length){
RightElementWidth = $(this).next().outerWidth(true);
RightElementLeftOffset = $(this).next().offset().left;
if(parseFloat(ui.offset.left+(ElementWidth/2)) > parseFloat(RightElementLeftOffset+(RightElementWidth/2))){
$(this).next().after($(this));
}
}
}
});
$("#Container").droppable({ accept: '.Element' });
It works fine, except for that the draggable-helper doesn't stay underneath the mouse-cursor when I move it's element to the next position.
Check out this fiddle:
http://jsfiddle.net/5qFhg/15/
You'll see what happens when you try to sort the green boxes. How can I keep the helper in position?

http://jsfiddle.net/margaret_/XM6f8/1/
Is this what you are looking for? Are you okay with using knockout? I can't add comments 'cause I don't have 50 reputation.
<input data-bind="value: name, visibleAndSelect: $data === viewModel.selectedTask(), event: { blur: function() { viewModel.selectTask(''); } }" />
Use parent and previous location to mimic the function.
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
$(element).data("sortList", valueAccessor()); //attach meta-data
$(element).sortable({
start: function(event, ui) {
//track the original position of the element
var parent = ui.item.parent();
var prev = ui.item.prev();
//create a function to move it back (if it has a prev sibling, insert after it, otherwise put it at the beginning)
ui.item.moveItemBack = prev.length ? function() { ui.item.insertAfter(prev); } : function() { parent.prepend(ui.item); };
},
update: function(event, ui) {
var item = ui.item.data("sortItem");
if (item) {
//identify parents
var originalParent = ui.item.data("parentList");
var newParent = ui.item.parent().data("sortList");
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (position >= 0) {
//move the element back to its original position and let KO handle adding it to the new parent
if (originalParent !== newParent) {
ui.item.moveItemBack();
}
//place item in the proper position
newParent.remove(item);
newParent.splice(position, 0, item);
}
}
},
connectWith: '.container'
});
}
Do you want the divs to appear side by side?

Related

Jquery - appending after re-registering

On this script I have drag and drop fieldsets (left menu - top). Drag multiple fieldsets into the right "working area". In those fieldsets is an area for dropping Fields (of which I have one, left menu - bottom). Drop "Field One" into any of the dropped Fieldset (under the line in the "Drop Fields Here" area) in the working area.
I want to then move that Field One from the Fieldset I dropped it into and into the other Fieldset in the working area. I have got the Field One draggable but I cannot get it to append to the new Fieldset.
I am not wanting to delete the Field and replace it with another clone from the left menu. I am new to jquery so my code may be a bit messy. Any help is greatly appreciated. Jsfiddle and code below.
I believe the code issue is between line 21-29...but I am not sure.
JsFiddle Link
$(document).ready(function() {
var fs_count = 0;
$("#drop-area").droppable({
accept: '.ui-draggable:not(.draggableField , .activeField)',
drop: function(event, ui) {
fs_count++;
var clone = $(ui.draggable).clone()
clone.addClass('.connectedSortable')
// clone.removeClass('.ui-draggable');
if (clone.hasClass('dropped')) {
return false;
}
clone.addClass('.connectedSortable').addClass('dropped').attr('id', 'fs_' + fs_count);
$(this).append(clone);
var fs_class = clone.attr('class');
alert('You added a field with class ' + fs_class);
var fieldsDroppable = $('#drop-area .ui-draggable:last-child .fieldDroppable');
fieldsDroppable.droppable({
accept: '.draggableField , .activeField',
drop: function(event, ui) {
var clone = $(ui.draggable).clone();
if (clone.hasClass('draggableField')) { // If else to prevent clones reproducing in Fieldsets when moving from original Fieldset.
clone.removeClass('ui-draggable , draggableField').addClass('activeField');
$(this).append(clone);
}
else {
// Append the div here?
}
var cloneClass = clone.attr('class'); // Temporary varialble for develpoment alert below
alert('you dropped a field' + cloneClass); // Temporary for development only
// Below re-register the "field" with jquery....not sure this is entirely correct.
var fieldsDraggable = $('#drop-area .ui-draggable .fieldDroppable .activeField');
fieldsDraggable.draggable();
}});
} });
$(".fieldDroppable").droppable({
accept: '.draggableField , .activeField',
drop: function(event, ui) {
var clone = $(ui.draggable).clone()
$(this).append(clone);
}
});
$(".ui-draggable").draggable({
opacity: 1.0,
helper: 'clone',
revert: 'invalid'
});
$(".draggableField").draggable({
opacity: 1.0,
helper: 'clone',
revert: 'false'
});
$(".activeField").draggable();
$("#drop-area").sortable({
handle: '.drag-handle',
update: function () { //triggered when sorting stopped
var dataAuto = $("#drop-area").sortable("serialize", {
key: "za",
attribute: "id",
});
alert(dataAuto);
}
});
$("#drop-area").disableSelection();
});
There will be a lot of work to do here, yet you can get over this hurdle like so:
https://jsfiddle.net/Twisty/6trmrfoo/32/
Basically, you want to assign a new .droppable() to a specific element in the field set when it is added to the #drop-area. I would advise using a function as you will do this a multitude of times.
You can also make use of .data() to store a source value along with the draggable elements. I only did this for fields since that was the only pace it seemed like it was needed. You can updated this once it's dropped into a fieldset so that future drag-n-drop operations can be handled properly, we don't want to clone the object we move it.
To move it, consider using .detach(). It'll detach the element from the current parent and can be used in chain to append or move into memory as a variable. Similar to .clone() just we're manipulating the actual object. .clone() is to "Copy & Paste" as .detach() is to "Cut & Paste".
JavaScript
$(function() {
var fs_count = 0;
function makeFieldTarget($fs) {
$fs.droppable({
accept: '.draggableField, .activeField',
drop: function(event, ui) {
var clone, cloneClass;
if (ui.draggable.data("source") == "sidebar") {
clone = $(ui.draggable).clone();
clone.removeClass('draggableField').addClass('activeField');
$(this).append(clone);
cloneClass = clone.attr('class');
console.log('DROPFIELD - you dropped a field from the side bar: ' + cloneClass);
clone.data("source", "fieldset").draggable({
zIndex: 1000
});
}
if (ui.draggable.data("source") == "fieldset") {
clone = ui.draggable;
clone.detach().attr("style", "").appendTo($(this));
cloneClass = clone.attr('class');
console.log('DROPFIELD - you dropped a field from a Field set: ' + cloneClass);
}
}
});
}
$("#drop-area").droppable({
accept: '.ui-draggable:not(.draggableField, .activeField)',
drop: function(event, ui) {
fs_count++;
var clone = $(ui.draggable).clone()
clone.addClass('connectedSortable');
if (clone.hasClass('dropped')) {
return false;
}
clone.addClass('connectedSortable dropped').attr('id', 'fs_' + fs_count);
$(this).append(clone);
var fs_class = clone.attr('class');
console.log('DROPAREA - You added a field with class ' + fs_class);
makeFieldTarget(clone.find(".fieldDroppable"));
$("#drop-area").sortable("refresh");
}
});
$(".ui-draggable").draggable({
opacity: 1.0,
helper: 'clone',
revert: 'invalid'
});
$(".draggableField").data("source", "sidebar").draggable({
opacity: 1.0,
helper: 'clone',
revert: 'false',
zIndex: 1000
});
$("#drop-area").sortable({
handle: '.drag-handle',
placeholder: "drop-place-holder",
items: ">div.dropped",
update: function() { //triggered when sorting stopped
var dataAuto = $("#drop-area").sortable("serialize", {
key: "za",
attribute: "id",
});
alert(dataAuto);
}
});
$("#drop-area").disableSelection();
});

Resizable jQuery - detect where scroll starts

I am building custom calendar and I have the following issue: since I have to create different events on that calendar, that start and end on certain dates, and I need to resize them (left and right), I would need to know if I am starting that resize from the left side or from the right side, so I know how to set those dates.
Basically, it doesn't select the proper dates when I resize from start and when I resize from the end.
EDIT: My draggable elements also get some sort of top/left values that mess up everything, and I cannot resize it to left more than their start width (to the right side it does what it should).. It is reducing width (- 47px) instead of adding that width.
This is the code I have for resizable now:
$(".draggable").resizable({
classes: {
"ui-resizable": "highlight"
},
containment: '.calendar-row',
grid: [47, 10],
handles: 'e, w',
resize: function (event, ui) {
lastEvent = event;
},
start: function (event, ui) {
uiEvent = true;
$( event.originalEvent.target ).one('click', function(e){ e.stopImmediatePropagation(); } );
},
stop: function (event, ui) {
setTimeout(function(){
uiEvent = false;
}, 300);
$( event.originalEvent.target ).one('click', function(e){ e.stopImmediatePropagation(); } );
if(isChrome || isEdge)
{
var resizedDate = new Date($(lastEvent.toElement).attr('data-date'));
// left to right
if(ui.size.width > ui.originalSize.width)
{
console.log($(lastEvent.toElement).attr('id'));
console.log($(lastEvent.toElement).attr('data-date'));
}
else{
var elemFromPoint = document.elementFromPoint(lastEvent.pageX, lastEvent.pageY).previousElementSibling;
console.log($(elemFromPoint).attr('id'));
console.log($(elemFromPoint).attr('data-date'));
}
}
if(isFirefox)
{
var resizedDate = new Date($(lastEvent.target).attr('data-date'));
$(lastEvent.target).hide();
// left to right
if(ui.size.width > ui.originalSize.width)
{
var elemFromPoint = document.elementFromPoint(lastEvent.pageX, lastEvent.pageY);
console.log($(elemFromPoint).attr('id'));
console.log($(elemFromPoint).attr('data-date'));
}
else{
var elemFromPoint = document.elementFromPoint(lastEvent.pageX, lastEvent.pageY).previousElementSibling;
console.log($(elemFromPoint).attr('id'));
console.log($(elemFromPoint).attr('data-date'));
}
$(lastEvent.target).show();
}
}
});

Alerting Order of Dropped Elements WITHOUT using jQuery sortable

https://jsfiddle.net/ianderso222/y9ynouxs/36/
I hope this makes sense, If I need to elaborate I will do so.
Right now the code is working pretty well, doing everything I need it to except this one issue. I just need to alert the order that the squares have been dropped in, after all 4 have been placed.
So, if square4 is placed in the large container, that would show up first in the alert. If square2 is in the last, smallest box it would be last in the list, and so on.
I would use sortable but I am afraid it would not work with the current setup. The resizing to different sized containers would not work, or at least I was not able to get it to work. If there is a way to keep the current structure of resizing to fill container and sliding into place I would say do that, but from everything I have seen I feel I would have to essentially start from scratch.
Here is the JavaScript, pardon the messy code:
$('.holderList li').droppable({
drop: function(event, ui) {
var droppable = $(this);
var draggable = ui.draggable;
console.log(draggable.attr('id') + ' is ' + droppable.attr('id'));
var $this = $(this);
ui.draggable.position({
my: "center",
at: "center",
of: $this,
using: function(pos) {
$(this).animate(pos, 200, "linear");
}
});
ui.draggable.addClass('dropped');
ui.draggable.data('droppedin', $(this));
$(this).droppable('disable');
setTimeout(function() {
var dragID = ui.draggable;
if (!$(".ui-droppable").not(".ui-droppable-disabled").length) {
alert(draggable.attr('id'));
}
}, 400);
},
});
$(".square").draggable({
stack: ".square",
revert: function(event, ui) {
//overwrite original position
$(this).data("ui-draggable").originalPosition = {
width: 50,
height: 50
}
//return boolean
return !event;
},
drag: function(event, ui) {
var draggable = $(this).data("ui-draggable");
$.each(draggable.snapElements, function(index, element) {
ui = $.extend({}, ui, {
snapElement: $(element.item),
snapping: element.snapping
});
if (element.snapping) {
if (!element.snappingKnown) {
element.snappingKnown = true;
draggable._trigger("snapped", event, ui);
}
} else if (element.snappingKnown) {
element.snappingKnown = false;
draggable._trigger("snapped", event, ui);
}
});
if ($(this).data('droppedin')) {
$(this).data('droppedin').droppable('enable');
$(this).data('droppedin', null)
$(this).removeClass('dropped')
}
},
snap: ".holder",
snapMode: "inner",
snapTolerance: 8,
snapped: function(event, ui) {
var squareWidth = ui.snapElement.width();
var squareHeight = ui.snapElement.height();
ui.helper.css({
width: squareWidth,
height: squareHeight
});
}
});
Take a look at my solution:
Demo
First it assigns a data-current attribute to the droppable holder on every drop and sets it to the id of the draggable.
Then it itterates trough all the .holder elements and prints their data-current
Simple but works.
// On single drop
drop: function(event,ui){
...
droppable.attr('data-current', draggable.attr('id') );
}
//On all dropped
$('.holder').each(function(el){
console.log($(this).attr('data-current'));
});

Make draggable sortable with the droppable

I made a drag & drop system. The draggable items can be dropped in placeholders. Those placeholders are added dynamically based on the amount of draggable items.
But when I drag a draggable item between those placeholders, it doesn't get appended to it. I've tried many many things but I'm still stuck on it.
I'm afraid that I'll need to change a lot of the code, but maybe one of you has a genius idea on how to solve this.
Here's the jsfiddle
Is it possible to check wether it has been placed on a placeholder, or not?
This is my droppable function:
function dropping () {
$("li.placeholder").droppable({
accept: ".to-drag",
hoverClass: correct_placeholder,
activeClass: active_placeholder,
revert: false,
tolerance: "pointer",
drop: function (event, ui) {
var dragging = ui.draggable.clone().find("img").remove();
$(this).append(dragging).addClass("dropped");
$(this).droppable('disable');
var day = $(ui.draggable).attr('data-id');
var index = $(event.target).index();
$(this).attr("data-id", day);
$(this).attr("date-order", index);
$(this).addClass("drop-"+ index);
$(this).attr("data-content", "");
$(this).find("h1, p").remove();
var dropped = $(".dropped").length;
var original = $(".placeholder").length;
if (dropped === original) {
$("li.placeholder").removeClass(blank_placeholder);
$("#dropzone").append('<li class="placeholder ' + blank_placeholder +' " data-id="" data-order=""></li>');
init();
}
$(".remove").click(function() {
var removable = $(this).parent();
var modal = $(this).attr("data-modal");
$(".modal-"+modal).remove();
if (dropped > original) {
$(this).parent().droppable('enable')
removable.remove();
removable.removeClass("dropped");
removable.removeClass("ui-droppable-disabled");
} else {
$(this).parent().droppable('enable');
removable.empty();
removable.removeClass("dropped");
removable.removeClass("ui-droppable-disabled");
}
init();
});
}
});
$("li.dropped").droppable({
disabled: function (event, ui) {
disabled: true
}
});
}

Jquery UI Selectable - Selecting multiple elements

I'm trying to implement a function to select multiple elements on my application here, but since I'm new to front end development I'm having some trouble controlling JavaScript's events and CSS. I found this post so I'm using It as my guide.
Before using the Selectable() function, I was adding a class (with some CSS) to give my element the appearance that It is selected (clicking one at time), and If I wanted It to be deselected, I just clicked the canvas (triggering a function to remove the class "selectedEffect").
But now I want to select multiple elements (in future I want to be able to group them), so I started using Selectable() and as I said, I found that post, that helped me partially. I was able to copy this piece of code to my project:
// this creates the selected variable
// we are going to store the selected objects in here
var selected = $([]), offset = {top:0, left:0};
$( "#canvas > div" ).draggable({
start: function(ev, ui) {
editorContent.addClass("hidden");
if ($(this).hasClass("ui-selected")){
selected = $(".ui-selected").each(function() {
var el = $(this);
el.data("offset", el.offset());
});
}
else {
selected = $([]);
$("#canvas > div").removeClass("ui-selected");
}
offset = $(this).offset();
},
drag: function(ev, ui) {
$(currentElement).removeClass("selectedEffect");
$(currentArrow).removeClass("selectedArrow");
var dt = ui.style.top - offset.top, dl = ui.style.left - offset.left;
// take all the elements that are selected expect $("this"), which is the element being dragged and loop through each.
selected.not(this).each(function() {
// create the variable for we don't need to keep calling $("this")
// el = current element we are on
// off = what position was this element at when it was selected, before drag
var el = $(this), off = el.data("offset");
el.css({top: off.top + dt, left: off.left + dl});
});
}
});
$( "#canvas" ).selectable();
// manually trigger the "select" of clicked elements
$( "#canvas > div" ).click( function(e){
if (e.metaKey == false) {
// if command key is pressed don't deselect existing elements
$( "#canvas > div" ).removeClass("ui-selected");
$(this).addClass("ui-selecting");
}
else {
if ($(this).hasClass("ui-selected")) {
// remove selected class from element if already selected
$(this).removeClass("ui-selected");
}
else {
// add selecting class if not
$(this).addClass("ui-selecting");
}
}
$( "#canvas" ).data("canvas")._mouseStop(null);
});
But, after implementing this function, I still having some bugs:
Not able to drag multiple selected objects at same time
If I click one element and then click the canvas(to deselect It), the "selectedEffect" don't disappear.
I just figured It out. The problem was that since I was adding dynamically elements to my canvas, I had to bind the following function to my draggable elements in the moment the where created:
$( "#myNewlyCreatedElement" ).draggable({
start: function (event, ui) {
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 = $([]);
$( "#canvas > div" ).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});
});
}
});

Categories