I am looking to create a sortable (via drag and drop) grid, similar to what JQuery's Sortable grid does ( http://jqueryui.com/demos/sortable/#display-grid ). However, Sortable requires that you use only divs with identical dimensions. For my purposes, each block is allowed to be different widths and heights.
The functionality I am looking for is the snap-to-grid capabilities while "shoving" the other elements out of the way. Draggable does everything except for preventing them from overlapping and shoving the other elements out of the way.
Oh, it doesn't have to be Jquery either. I'm open to using other methods if it is easier.
Jquery sortable does not require the items to be the same dimensions, as you can see in my prototype here: http://brettlyman.net/dashboard/index3.html
Most people use <ul> as the sortable container and <li>'s as the sortable items, and they can contain any content you want. Just make sure you put "list-style-type: none; margin: 0; padding: 0;" in the style of the <ul> so it looks/behaves like a container.
Unfortunately with sortable you cannot do a free-form grid -- the items must be next to each other in proximity. In my prototype I float everything left, so they fill to the right and then drop to the next line. I have the grid option set on the resize of my containers, however, so I can enforce a grid-type layout.
Thought I'd post my solution in case anyone else is having the same type of problem.
The short answer:
use "stop" event to re-calculate all grid elements according their new positions.
General comments:
sortable grid with variable element's dimensions - it is a visual mess, as elements of different size moving multiple times, jumping up and down, while you're dragging one of them; and layout completely different after each movement.
Working on the similar feature I developed jQuery plugin "Swappable"
http://www.eslinstructor.net/demo/swappable/swappable_home.html
and recently I answered the same question regarding this plugin:
to Swap elemnts(with className too) when Draged & Dropped
please take a look at the discussion and maybe it help you.
best regards,
-Vadim
I came across this question trying to solve the problem for myself.
I'm on my first iteration at trying a workaround: use the parent container as a droppable. Will let you know if it works:
For the following HTML:
<div id="sortable">
<div><div class="handle"></div>1</div>
<div><div class="handle"></div>2</div>
<div><div class="handle"></div>3</div>
...
</div>
Using the following JavaScript:
$('#sortable')
.sortable({
handle: '.handle',
tolerance: 'pointer'
})
.droppable({
drop: function(e, ui) {
var previousElement = ui.draggable.parent().children().filter(function() {
var $this = $(this);
var pos = $this.position();
return (this != ui.draggable[0]) && (pos.left <= ui.position.left) && (pos.top <= ui.position.top);
}).last();
if( previousElement.length ) {
previousElement.after(ui.draggable[0]);
ui.draggable.parent().data().sortable._noFinalSort = true;
}
}
});
Related
jQuery UI sortable plugin is intended to enable a group of DOM elements to be sortable. A nice demo is at official website here
The API documentation provides tolerance option, and the description says:
Specifies which mode to use for testing whether the item being moved is hovering over another item. Possible values: intersect, pointer
Furthermore, the description of intersect (which is default) states:
intersect: The item overlaps the other item by at least 50%.
I expected, that if I drag one item, and move it over another item, it will detect that I'm reordering the items as soon as 50% of the height overlaps. But it doesn't seem to work this way :(. Even if you check the official demo, and you try to drag the 1st item over the 2nd item, you'll see that the 1st item has to be dragged for entire height, like 100% of its height, over the 2nd element, and only then the order of items is swapped.
Am I missing something? Is there any way for me as a programmer to force the plugin to work as I expect it to work? I wish the user to move the 1st item only 50% of its height down, in order for the plugin to detect overlapping and perform reordering.
The short answer is:
There's a bug ticket for this, so it seems like the only option is some form of workaround.
Here's a workaround example that uses a custom sort function, which seemed to answer your question better. I'll keep the below example as well for another approach to the problem.
...
That covers the case with a single direction, but what if you want to implement a grid?
Here's a workaround fiddle that I edited (Grid example w/ insert): fiddle
Note: This doesn't swap blocks, it inserts them and pushes the rest back.
Here's a snip of the javascript / jQuery code involved that mocks 50% coverage:
var height = $(".tab").height();
var height = $(".tab").width();
$('#pointer').sortable({
cursorAt: { top: height/2, left: width/2 },
containment: 'parent',
tolerance: 'pointer'
});
Looking at the widget's code you'll find:
if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
this._rearrange(event, item);
} else {
break;
}
So, due you specifie tolerance: "intersect", this._intersectsWithSides(item) is take it.
And function _intersectsWithSides(item) it's defined here:
_intersectsWithSides: function(item) {
var isOverBottomHalf = this._isOverAxis(this.positionAbs.top +
this.offset.click.top, item.top + (item.height/2), item.height),
isOverRightHalf = this._isOverAxis(this.positionAbs.left +
this.offset.click.left, item.left + (item.width/2), item.width),
verticalDirection = this._getDragVerticalDirection(),
horizontalDirection = this._getDragHorizontalDirection();
it take "item.height/2" and "item.width/2"
You can go with your on function and redefine this behaviour:
_myIntersectsWithSides: function(item) {
var isOverBottomHalf = this._isOverAxis(this.positionAbs.top +
this.offset.click.top, item.top + (item.height/5), item.height),
isOverRightHalf = this._isOverAxis(this.positionAbs.left +
this.offset.click.left, item.left + (item.width/5), item.width),
verticalDirection = this._getDragVerticalDirection(),
horizontalDirection = this._getDragHorizontalDirection();
And that give you 20% (100/5 instead of 100/2) of width and height.
The "50%" it's defined by code.-
Hope this help
I am trying to create a full page interface using the excellent jQuery UI Layout plugin.
I want the central pane to hold multiple dialogs and allow them to be contained within that pane. So far so good. However, I also want to be able to drag the dialogs "out of the way", and move them to the right or bottom so that the central pane develops scroll bars and allows the central pane to act as a scrollable desktop for dialog boxes. I want the other pane(s) to be always there for other UI purposes.
HTML:
<div class="ui-layout-center">
<div id="dialog1">dialog 1</div>
<div id="dialog2">dialog 2</div>
</div>
<div class="ui-layout-north">North</div>
<div class="ui-layout-south">South</div>
<div class="ui-layout-west">West</div>
jQuery:
$('body').layout(
{
applyDemoStyles: true,
livePaneResizing : true
});
var dialog1 = $("#dialog1")
.dialog({})
.parent().appendTo( $(".ui-layout-center") );
dialog1.draggable( "option", "containment", $(".ui-layout-center") );
$("#dialog2")
.dialog({})
.parent().appendTo( $(".ui-layout-center") );
As you can see, it almost works, but the scrolling doesn't work horizontally. I've experimented with containing dialog1 but this makes things worse! Perhaps it's just a CSS issue or that combined with a setting. Any ideas?
jsFiddle
The solution turned out to me a case of manipulating the underlying draggable of the dialog box:
$("#dialog2").dialog("widget").draggable(
{
containment : [ 0, 0, 10000, 10000 ],
scroll: true,
scrollSensitivity : 100
});
Obviously, these values can be played with to achieve different results. I hope this helps anyone else in the same position!
jsFiddle
I looked over the documentation and apparently you are able to achieve this with using CSS and changing the overflow value.
http://jsfiddle.net/vnVhE/1/embedded/result/
As you can see the CSS applied is:
// disable scrolling in the other panes
.ui-layout-pane-north ,
.ui-layout-pane-west,
.ui-layout-pane-south {
overflow: hidden !important;
}
.ui-layout-layout-center {
overflow: auto
}
NOTE: Please keep in mind while this allows horizontal scrolling it is a bit tricky and hackish at best in my opinion. Under Chrome I could scroll just fine if I held the mouse near the edge of the vertical scroll bar and it moved properly.
I know vertical center in CSS is a pain to begin with, but I've just made it a bit more complicated. On my page, I have:
<ul id="complete">
</ul>
<form id="new_item_form">
<input type="text" id="add_item" placeholder="Type some tasks here"/>
</form>
<ul id="incomplete">
</ul>
It's for a basic task list. Tasks are added to the incomplete ul, and when completed move to the complete ul. What I want to do via css is have the text field vertically centered on the page and stay there, with the two lists butted up against it. I've been looking at all sorts of vertical alignment (a summary of forms found here: http://blog.themeforest.net/tutorials/vertical-centering-with-css/ ) but I can't seem to find a way that I can figure out how to adapt to allow what I need. How would I accomplish this style of fixed position centering?
EDIT:
Here's an image of what I'm looking for: https://www.dropbox.com/s/i0oit3v84j93b5g/Screen%20Shot%202012-10-11%20at%204.27.16%20PM.png
Is this what you want to obtain?
Of course, my code is a bit sketchy (use of height attribute on tds! Don't scold me to much). But you get the point.
If the height of the table is not known nor fix, but its parent height is known, it won't work (see this example) and you'll have to break it down.
If you just don't know any height at all, it's kind of hard to align...
Further reading on vertical-align
I can't think of any way to do this with CSS, but it's fairly easy to do with JavaScript/jQuery. Here is a working jsFiddle that does what you want on document load. You'd call the code again if you changed the lists, of course.
First, you enclose your lists and form in a div. I called this id="cmiddle". Then you use CSS to set the cmiddle div as position: relative. Then you use JavaScript code to get the enclosing window or frame height, calculate the center for the form, and then, subtract the upper list height to get the correct div position:
$(document).ready(function(e) {
// To work with frames, too
function getParentDocHeight($ele) {
for (;;) {
if (!$ele || !$ele.length) {
return $(window).height();
}
$ele = $ele.parent();
if ($ele.is("frame") || $ele.is("window")) {
return $ele.height();
}
}
}
var $cm = $("#cmiddle");
var formHeight = $("#new_item_form").outerHeight();
var viewHeight = getParentDocHeight($cm)
var formTop = (viewHeight - formHeight) / 2;
var divTop = formTop - $("#complete").outerHeight();
$cm.css("top", divTop);
});
Edit: Kraz was nice enough to add a simulation of adding list items to both lists and calling the code again to recalc. His jsFiddle here.
Well, I'm not sure what you are talking about
But generally,
put the line-height = the div's height. I will create a div around it if necessary
if some very particular situations, i do some math to manually center it
So if you want to centering 1 thing, create a div go around it with line-height = the div's height
And then make the div position: absolute, width, height, ....
Hope this helps
In my app I have 2 divs, one with a long list of products that can be dragged into another div (shopping cart). The product div has the overflow but it breaks prototype draggable elements. The prototype hacks are very obtrusive and not compatible with all browsers.
So I am taking a different approach, is it possible to have a scrollable div without using CSS overflow:auto?
Theres a css property to control that.
<div style="width:100px;height:100px;overflow:scroll">
</div>
http://www.w3schools.com/Css/pr_pos_overflow.asp
You can use a frame with content larger than its window. Might make it hard to pass JS events though.
Here is what I wrote to have it running under IE 8.0.6 & Firefox 3.6.3:
Make draggable the elements (with border) in the "width:100px;scrollable:auto" container:
function makeDraggable(container,tag) {
if(!container || !tag) { return false; }
$(container).select(tag).each( function(o) {
new Draggable(o,{
starteffect: function(e){makeDragVisible(container,e);},
endeffect: function(e){e.setStyle({'position':'','width':'','cursor':''});},
zindex: 1000
// , revert: ... // the other options
});
});
}
function makeDragVisible(container,element) {
if(!container || !element) { return false; }
var i=$(container).getStyle('width');
i=i.replace('px','');
i=Math.round(i-20)+'px';
element.setStyle({'width':i,'z-index':1000,'position':'absolute','cursor':'move'});
//
$(container).setStyle({});
}
Important notes:
the z-index is repeated
notice the container loss of style at the end of 'starteffect'. Cursor and width are simply there to keep the drag user friendly.
I hope it helps.
I am trying to use jCarousel plugin for jQuery in order to provide my website users with scrollable (horizontal) content.
The content I am mentioning is basically user defined <li> elements, styled so that they have a feel and look of a tab. so basically I am trying to achieve the same effect of the tabs in pageflakes.com. As you may imagine, the users are creating tabs and providing tab names by themselves..
jCarousel needs you to specify a fixed width for the content e.g., all their examples are based upon images that have fixed height and width. but in my case I have not control over what the user will name his/her tab...making it impossible for me to guess the width of the total container div.
I have tried using a silly method such as guessing the width programatically assuming each letter being approx 5 pixels and multiplying 5 with the length of the word they have given as a name for a tab. even in this case, i needed to manipulate the css file dynamically which I am not sure how to do and even if it is feasable to do so..
Any solutions appreciated...
<lu>
<li class='MyTab' id='578'>This is my tab</li>
<li class='MyTab' id='579'>of which I can</li>
<li class='MyTab' id='580'>never guess</li>
<li class='MyTab' id='581'><img src='img/bullet_key.png' /> The length of</li>
</lu>
The above html is produced programatically through ajax_tabs_output.aspx, loaded into a javascript array and the jCarousel takes care of the rest..
function outputTabsArray() {
$.ajax({
url: 'ajax_tabs_output.aspx',
type: 'get',
data: 'q=array',
async: false,
success: function(out)
{
tabs_array = out;
}
});
}// end outputTabsArray
function mycarousel_itemLoadCallback(carousel, state)
{
for (var i = carousel.first; i <= carousel.last; i++) {
if (carousel.has(i)) {
continue;
}
if (i > tabs_array.length) {
break;
}
carousel.add(i, mycarousel_getItemHTML(tabs_array[i-1]));
}
};
/**
* Item html creation helper.
*/
function mycarousel_getItemHTML(item)
{
return '<div class="MyTab" id="' + item.tab_id + "'>" + item.tab_name + '</div>';
};
$('#mycarousel').jcarousel({
size: tabs_array.length,
itemLoadCallback: {onBeforeAnimation: mycarousel_itemLoadCallback}
});
The closest thing to what you want is probably jscrollhorizontalpane. I've never used it so I can't vouch for it's effectiveness as a solution to this specific problem.
This sort of widget shouldn't be to hard to make if you want to attempt it. I'll break down the approximate method I would use:
Make sure to use plenty of wrappers.
<div class="scrollable">
<div class="window">
<div class="space">
<ul class="tabs">
<li class="tab">...</li>
<li class="tab">...</li>
<li class="tab">...</li>
</ul>
</div>
</div>
←
→
</div>
What we'll be doing is shifting the "space" element back and forth inside "window" element. This can be done by setting position:relative to "window" and position:absolute to "space" and then shift it about using left:-??px, but I'll use the scrollLeft property.
Add some CSS.
.window {
overflow : hidden;
width : 100%;
}
.space {
width : 999em; /* lots of space for tabs */
overflow : hidden; /* clear floats */
}
.tabs {
float : left; /* shrink so we can determine tabs width */
}
.tab {
float : left; /* line tabs up */
}
This is just the basic stuff that the technique needs. Some of this stuff could be applied by jQuery,
Add events to window resize.
$(window)
.resize(function () {
var sz = $('.window');
var ul = sz.find('ul');
if ( sz.width() < ul.width() ) {
$('.scrollable a.left, .scrollable a.right').show();
}
else {
$('.scrollable a.left, .scrollable a.right').hide();
}
})
.trigger('resize');
Add events to scroll buttons.
$('.scrollable a.left').hover(
function (e) {
var sz = $('.window');
sz.animate({ scrollLeft : 0 }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
$('.scrollable a.right').hover(
function (e) {
var sz = $('.window');
var margin = sz.find('ul').width() - sz.width();
sz.animate({ scrollLeft : margin }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
Done!
The widths could also be calculated by looping through the "tab" elements and summing upp outerWidth. This is unnecessary if you have full control but might be a better choice for a full standalone plugin.
From what I can tell, you're trying make JCarousel do something it was never designed to do. Based on what I read on the JCarousel website it appears to be an image rotator.
What it sounds like you want is a tabbed interface. See JQuery UI Tabs for a demo and documentation on how to implement it.
If I'm totally wrong and all you're looking for is a tutorial on how to do proper CSS tabs, have a look at:
http://unraveled.com/projects/css_tabs/
Soviut,
You are actually quite right! I am trying to make JCarousel do something it wasn't designed for.
However, I also wouldn't like to use a tab plugin or any similar stuf as I NEED FULL CONTROL over my output. Such as injecting more elements into the tabs when needed such as double clicking, loading and many other etc. etc.
Actually what I am looking for a way to scroll horizontally the content within a div with arrows on the left and right.. To be more precise, I need the exact same structure of the tabs seen in www.pageflakes.com
The user will be able to create tabs by clicking on a link (or any other element for that matter), they will be able to inline edit its name, whenever they have more tabs then the length of the div, the buttons will be visible allowing them to slide inside the div, I will have events bound to their load, click and double click events.
To summarize I have everything ready and working except for the sliding/scrolling part. Its only I have to get help from you guys regarding how to achieve this functionality..
Kind regards,
What you're looking for isn't tabs, but draggable panels. One example can be found here, called JQuery Portlets. The related blog entry about Portlets can be found here.
You may also want to look into JQuery UI's Draggable, Droppable, and Sortable plugins.