z-index stacking issue on draggable element (jQuery UI) - javascript

I'm having an issue in a jQuery UI app I'm writing (a klondike solitaire game) that happens when I'm attempting to drag a part of a flipped over stack from one stack to another. You can see this illustrated in the screenshot below:
The app in its entirety can be found in this Fiddle: http://jsfiddle.net/damo_s/hy2dvL1u/
I have an idea of what's going wrong but not sure how to engineer it to work as expected.
I initially tried to fix it by adding a class to the hovered over droppable but this only works to the extent that I have to have the draggable at least 50% over the droppable (because I'm using intersect mode on the tolerance option) before the draggable comes to the front. The relevant code:
$('.drop-area').droppable({
over: function (event, ui) {
$(this).closest('.main-stack').addClass('droppable-above');
},
out: function (event, ui) {
$('.main-stack').removeClass('droppable-above');
},
drop: function (event, ui) {
$('.main-stack').removeClass('droppable-above');
...
My guess of what's going on is that the stacks with class .main-stack are absolutely positioned layers with their own z-index values (though not explicitly set in my css) and thus the z-index of the draggable has no bearing on whether it will be above or below a particular .main-stack
Any further clarification on what's going on and what can be done to fix it would be much appreciated.

A solution came to me shortly after I posted the question.
I added this to the start: method of the draggable initialization:
if ($(this).closest('.main-stack').length) {
$(this).closest('.main-stack').addClass('dragging-card');
$(this).closest('.main-stack').siblings().removeClass('dragging-card');
}
So my code became:
$('.card').draggable({
start: function (event, ui) {
positionTop = $(this).css('top');
// On drag start check each stack for an empty stack
$('.main-stack').each(function () {
if (!$(this).find('.card').length) {
$(this).droppable('option', 'disabled', false);
return false;
}
});
// If dragging from a main stack
if ($(this).closest('.main-stack').length) {
// Set the class of the draggable's containing stack
$(this).closest('.main-stack').addClass('dragging-card');
$(this).closest('.main-stack').siblings().removeClass('dragging-card');
}
},
And my CSS changed to:
.under-card {
z-index: 1000!important;
}
.ui-front, .top-card {
z-index: 1001!important;
}
.dragging-card {
z-index: 1002!important;
}
.ui-draggable-dragging {
z-index: 1003!important;
}

Related

jQuery draggable bug (element not in line with cursor)

EDIT: JSFIDDLE: http://jsfiddle.net/h9yzsqfr/
--
Code:
elem.editor.find("div.ui-widget").draggable(
{
handle:"div.content",
containment:elem.editor,
snap:"*",
start: function(e,ui)
{
$(ui.helper).addClass("dragging");
},
drag: function(e,ui)
{
checkTop(ui);
if($(ui.helper).parents("div.lo-content").length > 0)
{
$(ui.helper).appendTo(elem.editor);
ui.position = { 'top': e.pageY, 'left': e.pageX };
}
},
stop: function(e,ui)
{
oldWidget.saveState($(ui.helper),1);
setTimeout(function() {
$(ui.helper).removeClass("dragging");
}, 300);
}
});
I have some draggable objects within a container defined by elem.editor; however I have some other draggable objects inside div.lo-content which is also inside elem.editor.
Whenever an object inside div.lo-content is dragged, it should reattach to the elem.editor container which is working fine; however as demonstrated in the following GIF (below), it is not setting the position of the draggable object to where the mouse cursor is.
What is a workaround for this problem?
The problem is your width: 100% and the containment option. As soon as its appended to body it stays 100%, but of the body, then it gets smaller but the containment has already been calculated.
One way to solve it is to reset containment. There are probably different ways to do it, one way could be like this:
start: function (e, ui) {
if ($(this).parents(".inner").length > 0) {
$(ui.helper).addClass("dragging");
ui.helper.css({
'width': '100px',
'left': '0px',
'top': '0px'
});
ui.helper.data()['ui-draggable'].containment = [0,0,$('body').width(), $('body').height()]
ui.helper.appendTo("body");
}
},
http://jsfiddle.net/puurqx8r/6/
connectToSortable 
It's a  Selector that Allows the draggable to be dropped onto the specified sortables. If this option is used, a draggable can be dropped onto a sortable list and then becomes part of it. Note: The helper option must be set to "clone" in order to work flawlessly. Requires the jQuery UI Sortable plugin to be included.
Code examples:
Initialize the draggable with the connectToSortableoption specified:
$( ".selector" ).draggable({
connectToSortable: "#my-sortable"
cursorAt:{top: 5, left: t}
});
Use cursorAt: top:, left: and appendTo: 'body'
See my answer with example here.
jQuery draggable shows helper in wrong place after page scrolled
I had the same problem. This is caused by the use of margins to center elements. Try using position absolute!
I removed position:absolute on dragable object

drag fancytree node to an external droppable

I have a fancytree populated with some json. I also have a droppable div. I want to be able to drag nodes within the tree (ie to move stuff about within the contained hierarchy) and I want to be able to drag stuff from the tree into my external droppable div.
How can I do this?
Here's what I have:
My html:
<div id="tree"></div>
<div id="droppable">
stuff
</div>
dnd initialisation options:
{
autoExpandMS: 400,
focusOnClick: true,
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
dragStart: function(node, data) {return true;},
dragEnter: function(node, data) {return true;},
dragDrop: function(node, data) {data.otherNode.moveTo(node, data.hitMode);},
draggable: {
containment: 'document',
scroll : false,
scrollSpeed: 7,
scrollSensitivity: 10,
}
},
droppable initialisation
$('#droppable').droppable({
onDrop : function(e,source){
alert('dropped: '+source.textContent);
window.dropped = source;
window.event = e;
},
});
Extensions:
I'm making use of ["dnd","edit","contextMenu"] extensions. I'm mentioning this in case there is some conflict I am not aware of... I did however disable the edit and contextMenu to no avail.
Behavior:
The contextMenu and edit extensions work fine.
I can drag and drop items within the tree to reorder nodes.
I cannot drag stuff out of the tree
with scroll: true the tree just gets some scrollbars that scroll to infinity as I drag over the edge
with scroll: false the tree scrollbars behave the same but no actual scrolling occurs
containment seems to have no effect
Included resources:
jquery.js jquery-ui-1.11.0.js
jquery.fancytree-all.css
jquery.contextMenu.css
jquery.contextMenu-1.6.5.js
jquery.fancytree.contextMenu.js
fancytree.min.css
To prevent scrolling inside the tree container:
$("#tree").fancytree({
...
dnd: {
...
draggable: {
scroll: false
},
and custom CSS
ul.fancytree-container {
position: inherit;
}
To make containment work:
$("#tree").fancytree({
dnd: {
...
draggable: {
revert: "invalid"
scroll: false,
appendTo: "body", // Helper parent (defaults to tree.$container)
helper: function(event) {
var $helper,
sourceNode = $.ui.fancytree.getNode(event.target),
$nodeTag = $(sourceNode.span);
$helper = $("<div class='fancytree-drag-helper'><span class='fancytree-drag-helper-img' /></div>")
.append($nodeTag.find("span.fancytree-title").clone());
// Attach node reference to helper object
$helper.data("ftSourceNode", sourceNode);
// we return an unconnected element, so `draggable` will add this
// to the parent specified as `appendTo` option
return $helper;
},
},
}
[...]
});
There is an example that can be found under the tests directory of the fancytree source that does the exact same thing. Take a look at it here: http://wwwendt.de/tech/fancytree/test/test-ext-dnd.html
Hope this helps.
I have faced the same issue with dragging elements out of the tree and ended up with some quick css hack for fancytree-container element:
ul.fancytree-container {
overflow: visible;
}
The problem is that FancyTree uses its own draggable helper which is attached to the tree element. See _onDragEvent in jquery.fancytree.dnd.js (line 389).
I got round this by hacking the code to append the helper to the body i.e.
$("body").append($helper);
...instead of...
$("ul.fancytree-container", node.tree.$div).append($helper);

Jquery Draggable - How do I make a dragged element snap in pixel increments

For my application, after an element is dragged and snapped to the droppable zone I need to continue to allows for that dragged item to be moved vertically only. I have already gone ahead and done this, but I also need for that item to only be dragged and snapped to pixel increments. How can this be done?
For example, I have a timeline and I need the elements to snap to 15 minute increments. I need to know if this is possible with the draggable function or do I need to come up with some slider hybrid? I'd rather not use some hybrid approach if possible.
Code Snippet:
$(".activity_link").draggable({
"snap": ".trek_slider",
"snapMode": "inner",
// "snapTolerance" : 30,
"revert": function (event, ui) {
// on older version of jQuery use "draggable"
// $(this).data("draggable")
// on 2.x versions of jQuery use "ui-draggable"
// $(this).data("ui-draggable")
$(this).data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
$(this).draggable("option", "axis", false);
// return boolean
return !event;
// that evaluate like this:
// return event !== false ? false : true;
}
});
$(".trek_slider").droppable({
"tolerance": "touch",
"over": function (event,
ui) {
var left_position = parseInt($(ui.draggable).css("left"));
$(ui.draggable).find(".activity_caret").show();
$(ui.draggable).draggable("option", "axis", "y");
}
});
Try this
$( ".your_div" ).draggable({ grid: [ 1,1 ] });
In above example it will be draggedx by only one pixel in each direction
Please Note: Do not forget to add your other options.

JQuery UI sortable in a scrollable container - scroll position "jumps" when sorting

Please check this almost identical question first: jQuery Sortable List - scroll bar jumps up when sorting
I have exactly the same problem, only that I tried all the suggested solutions there with no luck
Here is the way to reproduce
create a sortable list
have it scrollable
scroll down
reorder items
scroll position "jumps" up
Here is the code (see also in JSFiddle)
Html
<ul id="panel" class="scroll">
<li class="content" style="background-color:red">item1</li>
<li class="content" style="background-color:green">item2</li>
<li class="content" style="background-color:blue">item3</li>
<li class="content" style="background-color:gray">item4</li>
<li class="content" style="background-color:yellow">item5</li>
</ul>​
JavaScript
$(function() {
$("#panel").sortable({
items: ".content",
forcePlaceholderSize: true
}).disableSelection();
$(".content").disableSelection();
});​
CSS
.scroll{
overflow: scroll;
border:1px solid red;
height: 200px;
width: 150px;
}
.content{
height: 50px;
width: 100%;
}
​
Here is the code (in JSFiddle) after trying the notion of the accepted answer (with no luck)
I can try to understand why it happens (list get's "shortened" for a quick second), but all attempts to use placeholders or helpers to avoid it didn't work either. I feel I tried almost any permutation of the "scrollable" options with no luck
Browsers I tried
IE9, Firefox 10.0.1, and Chrome 17
JQuery versions
core 1.7.1, UI v 1.8.17
Am I doing something wrong? Is there a solution? Could it be a bug?
If you modifiy your CSS for the .scroll element by adding:
position:relative;
That should resolve this issue.
Adding overflow-y:scroll to the sortable list even without the height property solved it for me. It only shows a disabled scrollbar, but that's okay.
아래로 스크롤 할때 는 이런식으로 하면 됩니다.
var cY = -1;
var base = 0;
$("").sortable({
sort: function(event, ui) {
var nextCY = event.pageY;
if(cY - nextCY < -10){
if(event.clientY + ui.helper.outerHeight(true) - 20 > document.body.clientHeight) {
base = base === 0 ? event.clientY : base;
var move = event.clientY - base;
var curScrollY = $(document).scrollTop();
$(document).scrollTop(curScrollY + move+3);
base = event.clientY;
}
}
},
// .....
});
Seems like jQuery UI 1.9.2 solved the issue.
If you are unable to change the library, there's a workaround including a simple scroll bar operation. The idea is simple:
Keep the previous scroll position every time you scroll.
Set scroll back to the previous position when you start dragging your element.
Here you go;
var lastScrollPosition = 0; //variables defined in upper scope
var tempScrollPosition = 0;
window.onscroll = function () { // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function () {
tempScrollPosition = lastScrollPosition; // Scrolls don't change from position a to b. They cover some numbers between a and b during the change to create a smooth sliding visual. That's why we pick the previous scroll position with a timer of 250ms.
}, 250));
lastScrollPosition = $(document).scrollTop(); // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll
};
$("#YourSortablePanel").sortable({
start: function (event, ui) {
$(document).scrollTop(tempScrollPosition);
}
});
This is caused by the height of the container changing when sorting (just before the placeholder is created)
The problem is well described and answered in this stack overflow : https://stackoverflow.com/a/32477389/2604980
For me this sortable options were working if you don't want to remove overflow css from body:
start(e, ui) {
if (fixOffset) ui.item.css('transform', `translateY(${document.body.scrollTop}px)`);
fixOffset = true;
},
change(e, ui) {
ui.item.css('transform', `translateY(${document.body.scrollTop}px)`);
},
stop(e, ui) {
ui.item.css('transform', 'translateY(0)');
},

jQuery animate() to position issue

I'm just working on my personal website, giving it a bit of a revamp.
I've implemented a sort of 'accordion' menu feature, where if a menu button is clicked, the "non-clicked" buttons disappear, and then the clicked button is moved to the top of the list, where then a panel animates down in which I will be putting text content.
In Firefox this works perfectly, however in IE and Chrome the button jumps to the top of the page and then animates to position, instead of animating from where it started from.
Anyone any ideas how to fix this?
Offending code:
function Accordion(e)
{
var o =
{
init: function()
{
o.button = e;
o.addClickHandler();
},
addClickHandler: function()
{
o.button.click(function(){
o.button.removeClass('unselected');
o.button.addClass('selected');
o.fader();
});
},
fader: function()
{
$j('.unselected').animate({opacity:0}, 1000);
var wait = setInterval(function() {
if(!$j('.unselected').is(":animated") ) {
clearInterval(wait);
o.shifter();
}
}, 100);
},
shifter: function()
{
o.button.css({'position':'absolute'});
o.button.animate({top:91}, 500, o.createInfoBox);
},
createInfoBox: function()
{
var buttonParent = o.button.parent();
buttonParent.append("<div class='infoBox'></div>");
$j('.infoBox').animate({height:390});
}
}
o.init();
return o;
}
}
The issue lies within the shifter function, where I'm setting the position to absolute and then animating so the desired effect can be achieved. I understand why it's doing this (presume it's just resetting itself to top:0 and then animating to top:91) but does anyone have a quick solution? It's late and it's doing my head in.
Much appreciated,
Dave
HAve you tried using the current position of the element when you switch it to absolute... for example:
function() {
var currentp = $(this).offset();
o.button.css({'position':'absolute', top: currentp.top});
o.button.animate({top:91}, 500, o.createInfoBox);
}
Note there are two different offset functions and im not sure which one you want use here so you might want to review the docs on that.
Also you could always just re-theme the jQuery-ui accordian and save yourself the trouble ;-)

Categories