JQuery UI.sortable, how to change helper offset dynamically? - javascript

I have a list of items each with heavy content. When I apply jQuery sortable, what I wanted to achieve is this, when an item is dragged away and starts sorting, all the item contents will be hidden automatically.
From all my testtings, jQuery doesn't accommodate such changes very well, for example, if I do the following,
$("#sortable").sortable({
start: function(event, ui) {
$(".hidden").addClass("hide");
}
});
jQuery can't automatically figure out the sizes of hidden items and the sorting will be a mess.
So I solved it using an indirection, I firstly check mousedown, mouseup and mousemove event, and once dragging is detected (say when mousedown/mousemove for more than 50ms), I'll hide the items immediately.
And in jQuery, I used some delay to start sorting.
var mouse_down = false;
var mouse_down_time;
var content_hidden;
$(".drag-handle").bind("mousedown", function(e) {
mouse_down = true;
mouse_down_time = e.timeStamp;
$(".drag-handle").bind("mousemove", function(e) {
if (mouse_down && !content_hidden) {
if (e.timeStamp - mouse_down_time > 10) {
$(".hidden").addClass("hide");
content_hidden = true;
}
}
});
}).bind("mouseup", function(e){
$(".drag-handle").unbind("mousemove");
mouse_down = false;
if (content_hidden) {
$(".hidden").removeClass("hide");
content_hidden = false;
}
});
$("#sortable").sortable({
delay:100,
stop: function(event, ui) {
if (content_hidden) {
$(".hidden").removeClass("hide");
$(".drag-handle").unbind("mousemove");
content_hidden = false;
}
}
});
So jQuery will start sorting after item contents are hidden.
Everything works fine except the helper offset. The helper offset seems to bump up for exactly the amount of spaces occupied by the all the item contents above the dragged item (which are hidden now during dragging).
So my question is,
is there a way to dynamically calculate all the item heights before and after hidden, and set the helper offset accordingly?
Thanks.

Well, problem solved.
What I did is dynamically calculate all the items height above the dragged item (before and after hiding contents), and then set the helper offset to the corresponding value by setting css:({'top-margin':offset_value+'px'})

Related

Kendo ListBox: Return dropped items in order

I am using two listboxes, connected with each other, that allow drag and drop between the two. While using the reorder code found in another thread fixes the issue with reordering items within the same list, it doesn't address the issue when dragging and dropping an item from one listbox to the other. I understand that is because this isn't a "reorder" action, it is a "drop" action. I've tried many different angles, like using the "add" event, but there is no "offset" property in the (e) object.
How can I accomplish the same thing, that is, keep the dropped item in the order it is visually dropped?
Here is my widget function with the reorder event that properly orders the items as they are displayed in the listBox (why this doesn't happen automatically is beyond me!). Remember, the reorder event only occurs when moving items within the same listBox, not when dragging an item from one listBox to another.
$("#myMenus").kendoListBox({
draggable: true,
connectWith: "baseMenus",
dropSources: ["baseMenus"],
add: function(e) {
console.log('ADD');
console.log(e);
},
reorder: function(e) {
e.preventDefault();
var dataSource = e.sender.dataSource;
var dataItem = e.dataItems[0];
var index = dataSource.indexOf(dataItem) + e.offset;
dataSource.remove(dataItem);
dataSource.insert(index, dataItem);
},
});
I've managed to accomplish the same task, depending on the html hint that appears while dropping the item in the second listbox. I've modified your code with the solution.
$("#myMenus").kendoListBox({
draggable: true,
connectWith: "baseMenus",
dropSources: ["baseMenus"],
add: function(e) {
e.preventDefault();
var dataSource = e.sender.dataSource;
var dataItem = e.dataItems[0];
var index = $('li.k-drop-hint').index();
dataSource.insert(index, dataItem);
},
reorder: function(e) {
e.preventDefault();
var dataSource = e.sender.dataSource;
var dataItem = e.dataItems[0];
var index = dataSource.indexOf(dataItem) + e.offset;
dataSource.remove(dataItem);
dataSource.insert(index, dataItem);
},
});

Cycle through classes on Jquery to animate

enter image description here
I'm trying to create an animation where if you click the button the circles animate around the path and changes size. I'm not sure how i would cycle the classes on the next click ?
http://bluemoontesting.co.uk/bluemoon2016/people.html
I'm using an svg and have targeted the elements with this so far:
<script>
$(".animate-slider").click(function() {
$('.st7').toggleClass("top-left");
$('#XMLID_292_').toggleClass("left");
$('#XMLID_293_').toggleClass("center-right");
$('#XMLID_297_').toggleClass("top-right");
$('#XMLID_301_').toggleClass("top");
$('#XMLID_283_').toggleClass("top-center");
});
</script>
If anyone could help me i'd be very grateful :)
Thanks
I would take a little different approach. Instead of toggling classes, to get it to move to more than two positions, you will need to cycle the classes assigned to each element instead. Storing the class names in an array would allow you to move them in the array to cycle the position that each element moves to next. I created a simplified example.
$(document).ready(function () {
var steps = ['right', 'bottom-right', 'bottom-left', 'left', 'top'],
allClasses = steps.join(' ');
$('#go').click(function() {
$('#a').removeClass(allClasses).addClass(steps[0]);
$('#b').removeClass(allClasses).addClass(steps[1]);
$('#c').removeClass(allClasses).addClass(steps[2]);
$('#d').removeClass(allClasses).addClass(steps[3]);
$('#e').removeClass(allClasses).addClass(steps[4]);
steps.push(steps.shift()); // move first element to the end
// to cycle in the other direction you would pop and unshift instead
// steps.unshift(steps.pop()); // move last element to the beginning
});
});
You could just use setInterval like so:
var $st7 = $('.st7'); //class selectors can be expensive, so cache them
function rotate() {
$st7.toggleClass("top-left");
$('#XMLID_292_').toggleClass("left");
$('#XMLID_293_').toggleClass("center-right");
$('#XMLID_297_').toggleClass("top-right");
$('#XMLID_301_').toggleClass("top");
$('#XMLID_283_').toggleClass("top-center");
}
//2000 is milliseconds, so that's two seconds
var rotateIntervalId = setInterval(rotate, 2000);
//optionally consider stopping/starting the effect on mouse hover/exit
$('#Layer_1').on('hover', function() {
clearInterval(rotateIntervalId);
}).on('blur', function() {
rotateIntervalId = setInterval(rotate, 2000);
});

jQuery UI Sortable containment unable to detect placeholder

So I was working on this application where I want people to be able to drag items from one table data to the other table data, which must be contained within the parent table row.
But whenever I drag it around it seems to stick to the containment excluding the height of any placeholders.
Try it yourself: http://jsfiddle.net/2wy8R/
Any idea of how I can make it select the parent of the parent? Of not, then, how can I make the placeholders count?
Greetings
.
Update: YouTube video of the problem http://youtu.be/PMXcQvJmRGw
OK, here you go. Overridden the default containment as it seems buggy with your scenario. Let me know if this is not a good idea but it seems to work:
http://jsfiddle.net/2wy8R/6/
$('#first, #second').sortable({
connectWith: '.sortable',
placeholder: 'placeholder',
start: function(event, ui) {
ui.placeholder.height(ui.item.height() - 4);
var p = $(ui.helper);
var tr = p.closest("tr");
$(document).mousemove(function(e) {
var pOffset = p.offset();
var trOffset = tr.offset();
if (pOffset.left < trOffset.left) {
p.css({left: trOffset.left});
}
if (pOffset.left + p.width() > trOffset.left + tr.width()) {
p.css({left: trOffset.left + tr.width() - p.width()});
}
if (pOffset.top < trOffset.top) {
p.css({top: trOffset.top});
}
if (pOffset.top + p.height() > trOffset.top + tr.height()) {
p.css({top: trOffset.top + tr.height() - p.height()});
}
});
}
}).disableSelection();
just be careful with it though, as this keeps adding mousemove events to the document. you may want to unbind mousemove before binding it...

Creating multiple droppable siblings that at positioned on top of eachother

I am trying to create multiple jquery droppables next to eachother where some parts might overlap, in these cases I want the one that is on top (z-index wise) to be greedy.
I have tried setting the greedy: true option in the droppable, but this does not seem to help. I have also tried to return false on the drop event and used event.stopPropagation();.
Here is a jsfiddle based on the demo page of jquery.
Is there any way to stop the drop event from propagating if there is another droppable triggering it, preferably the one that has the highest z-index?
Use document.elementFromPoint to check the element directly under the cursor. If it’s not your droppable, don’t do anything.
Add the following to the top of your drop: handler:
var droppable = $(this);
ui.helper.css({pointerEvents: 'none'});
var onto = document.elementFromPoint(event.clientX, event.clientY);
ui.helper.css({pointerEvents: ''});
if(!droppable.is(onto) && droppable.has(onto).length === 0) {
return;
}
Disabling pointer events on the helper is necessary for document.elementFromPoint to ignore the thing your dragging and only checking underneath. I’ve updated your jsFiddle for a quick demonstration. Note that the highlight still affects both elements. You should be able to change that by not letting jQuery UI do the highlighting but instead write it yourself on the draggables drag: event. I have, however, found this to be unusable in practice as this check (and disabling pointer events) is quite slow and may also cause flickering.
You need a function to check if the element is the highest visible element. Something is the highest visible when
The item is later in the dom list
It has a higher z-index set
The below function can be used to see if the current element (either in over method or drop method in the droppable setting) is in fact the highest visible element.
function isHighestVisible(cEl) {
cEl = $(this); //use this if you want to use it in the over method for draggable, else just pass it to the method
//get all active droppable elements, based on the classes you provided
var $list = $('.ui-widget-header.ui-state-active');
var listCount = $list.length;
var HighestEl = null;
var HighestZI = -1;
//one element is always highest.
if (listCount<=1) {
console.log(cEl.attr('id'));
return true;
}
//check each element
$list.each(function(i) {
var $el = $(this);
var id = $el.attr('id');
//try to parse the z-index. If its 'auto', return 0.
var zi = isNaN(parseInt($el.css('z-index'))) ? 0 : parseInt($el.css('z-index'));
if (zi>0) {
//z-index is set, use it.
//I add the listCount to prevent errors when a low z-index is used (eg z-index=1) and there are multiple elements.
//Adding the listCount assures that elements with a z-index are always later in the list then elements without it.
zi = zi + listCount;
} else {
//z-index is not set, check for z-index on parents
$el.parents().each(function() {
$par = $(this);
var ziParent = isNaN(parseInt($par.css('z-index'))) ? 0 : parseInt($par.css('z-index'));
if (ziParent>0) {
zi = ziParent;
return false;
}
});
}
//add the current index of the element to the zi
zi += i;
//if the calculated z-index is highest, change the highest element.
if (HighestEl==null || HighestZI<zi) {
HighestEl=$el;
HighestZI = zi;
}
});
//if the highest found active element, return true;
if (HighestEl.attr('id')==cEl.attr('id')) {
console.log(cEl.attr('id'));
return true;
}
//if all else fails, return false
return false;
}
If i had this issue i would use jQuery's .addclass
.droppablewidgetforce {
z-index: 1000 !important;
}
make a class as such so the layer stays on top no matter what.. this should fix the issue.
The greedy options is just for nested elements that have a droppable parent.
In your code the 2 droppable elements are siblings so the greedy option will not work:
<div id="droppable3" class="ui-widget-header">
<p>Outer droppable</p>
</div>
<div id="droppable3-inner" class="ui-widget-header">
<p>Inner droppable (greedy)</p>
</div>
Here is a messy workaround if you still want to use siblings instead of parents and children.
Live code example.
function normalDraggedOver() {
$( this ).addClass( "ui-state-highlight" )
.find( "> p" )
.html( "Droppeeeed!" );
}
var options = {
activeClass: "ui-state-hover",
hoverClass: "ui-state-active",
greedy: true,
drop: normalDraggedOver
};
var options2 = {
activeClass: "ui-state-hover",
hoverClass: "ui-state-active",
disabledClass: "ui-state-disabled",
hoverStateClass: "ui-state-hover",
greedy: true,
greedyWithSibligs: true,
drop: normalDraggedOver,
over: function () {
/* if we have previous siblings of the inner element we handle them as parents */
if(options2.greedyWithSibligs && $(this).prev().length > 0) {
$(this).siblings().slice(0, $(this).index())
.removeClass(options2.hoverClass)
.droppable('disable')
.removeClass(options2.disabledClass)
.removeClass(options2.hoverStateClass);
}
},
out: function () {
/* all the siblings of the inner element that were handled as parents act as one*/
if(options2.greedyWithSibligs && $(this).prev().length > 0) {
$(this).siblings().slice(0, $(this).index())
.droppable('enable')
.addClass(options2.hoverClass);
console.log($(this).attr('id'));
}
}
};
If just want it to look the same you can set the #droppable3 to position: relative and set the child #droppable3-inner to position: absolute.
HTML:
<div id="droppable3" class="ui-widget-header">
<p>Outer droppable</p>
</div>
<div id="droppable3-inner" class="ui-widget-header">
<p>Inner droppable (greedy)</p>
</div>
Here is the live example

Help me move from prototype to jquery

window.onload = function() {
$A($('draggables').getElementsByTagName('p')).each(
function(item) {
new Draggable(
item,
{
revert: true
}
);
}
);
Droppables.add(
'droparea0',
{
hoverclass: 'hoverActive',
onDrop: moveItem
}
);
// Set drop area by default non cleared.
$('droparea0').cleared = false;
}
function moveItem( draggable,droparea){
$(droparea).highlight({startcolor: '#999999', endcolor: '#f3f0ca' });
if (!droparea.cleared) {
droparea.innerHTML = '';
droparea.cleared = true;
}
draggable.parentNode.removeChild(draggable);
droparea.appendChild(draggable);
}
Hi, I'm moving from prototype to Jquery and right now I've being unsuccessfuly able to do the transition and finally need some help. can some pne please help me to translate the above prototype js code to jquery put some comments to it so I can follow? I will really appreciate. Yes, prototype is a bit hard work but until I get my head into jquery completely it will be hard to get that move out of my head.
As already mentioned, jQueryUI is your friend. Given the following HTML:
<div class='draggables'>
<p>Drag1</p>
<p>Drag2</p>
<p>Drag3</p>
</div>
<div id='droparea0'>Drop Zone</div>
You can use the following jQuery and jQueryUI to get something close to what you are doing.
$(document).ready(function() {
$('.draggables p').draggable();
$('#droparea0').droppable({
drop: function(event, ui) {
ui.draggable.detach(); // detach the dragged element from the DOM
$(this).css({'background-color': '#999999'}) // start colour for drop area
.animate({'background-color': '#f3f0ca'}) // animate to final colour
.empty() // clear the contents of the dropzone
.append(ui.draggable); // append the dragged element
ui.draggable.css({top: 0, left: 0}); // reset top/left since it was changed during dragging
}
});
});
Working jsFiddle here: http://jsfiddle.net/2F8YE/
first of all in jQuery you should use $(function(){...}) instead of window.onload (jquery starts here ;D)
just look at the jQueryUI sample http://jqueryui.com/demos/droppable/

Categories