jQuery UI Draggable: Custom Snap-To Method - javascript

I know the jQuery UI API has snap, snapMethod and snapTolerance built in, but these are not going to work in this case.
Here is the problem I face: when dragging within a container, I want the draggable to snap to the edges of the container when within a certain distance. Calculating distances and triggering this is not an issue. It is getting the draggable to snap which I cannot get to work.
I expected something like: $draggable.position().left = 0; might snap it to the left edge of the parent container but it doesn't make any difference.
Here is a fiddle to demonstrate: https://jsfiddle.net/jwxrevL2/1/
JS:
//set draggable
$('.drag').draggable({
drag: function(){ drag($(this)) },
containment: 'parent',
});
//drag
function drag( $draggable ){
var snap_tolerance = 10;
//Draggable
var d_top = $draggable.position().top;
var d_middle = ($draggable.position().top+($draggable.height()/2));
var d_bottom = ($draggable.position().top+$draggable.height());
var d_left = $draggable.position().left;
var d_center = ($draggable.position().left+($draggable.width()/2));
var d_right = ($draggable.position().left+$draggable.width());
//Wrapper
var $wrapper = $('.wrapper');
var w_top = 0;
var w_bottom = $wrapper.height();
var w_left = 0
var w_right = $wrapper.width();
//snap to left
if( d_left <= (w_left+snap_tolerance )){
console.log('snap left');
$draggable.position().left = w_left;
within_snap = true;
}
//snap to right
if( d_right >= (w_right-snap_tolerance)){
console.log('snap right');
$draggable.position().left = (w_right-$draggable.width());
within_snap = true;
}
//snap to top
if( d_top <= (w_top+snap_tolerance )){
console.log('snap top');
$draggable.position().top = w_top;
within_snap = true;
}
//snap to bottom
if( d_bottom >= (w_bottom-snap_tolerance )){
console.log('snap bottom');
$draggable.position().top = (w_bottom-$draggable.height());
within_snap = true;
}
}//end fn drag

I've have managed to get it working. Although I must say I do not fully understand what is going on. These are the changes I've made (updated fiddle):
//set draggable
$('.drag').draggable({
drag: function(e, ui){ drag($(this), ui) },
containment: 'parent',
});
So on the drag event I pass the ui object as well as the jQuery object (I think, please correct me if I am wrong about the ui object) into the drag function.
//snap to left
if( d_left <= (w_left+snap_tolerance )){
console.log('snap left');
ui.position.left = w_left;
within_snap = true;
}
Then by updating the ui objects position.left property I can snap it into position.
Can anyone explain why it is different using ui rather than the jQuery object?

Here's an example.
You need to specify a grid like so:
<script>
$(function() {
$( "#draggable4" ).draggable({ grid: [ 20, 20 ] });
$( "#draggable5" ).draggable({ grid: [ 80, 80 ] });
});
</script>
https://jsfiddle.net/m3r2xra3/

Related

JavaScript function is preventing link working

This javascript function is used to animate a number of selection boxes, however, I want to be able to use these selection boxes as A HREF links but i believe that something in this javascript is preventing it from acting as a href link.
Please could someone help point me in the right direction
Any help much appreciated thank you
(function() {
var $container = $('#portfolio-items');
if( $container.length ) {
var $itemsFilter = $('#portfolio-items-filter'),
mouseOver;
// Copy categories to item classes
$('article', $container).each(function(i) {
var $this = $(this);
$this.addClass( $this.attr('data-categories') );
});
// Run Isotope when all images are fully loaded
$(window).on('load', function() {
$container.isotope({
itemSelector : 'article',
layoutMode : 'fitRows'
});
});
// Filter projects
$itemsFilter.on('click', 'a', function(e) {
var $this = $(this),
currentOption = $this.attr('data-categories');
$itemsFilter.find('a').removeClass('active');
$this.addClass('active');
if( currentOption ) {
if( currentOption !== '*' ) currentOption = currentOption.replace(currentOption, '.' + currentOption)
$container.isotope({ filter : currentOption });
}
e.preventDefault();
});
$itemsFilter.find('a').first().addClass('active');
$itemsFilter.find('a').not('.active').hide();
// On mouseover (hover)
$itemsFilter.on('mouseenter', function() {
var $this = $(this);
clearTimeout( mouseOver );
// Wait 100ms before animating to prevent unnecessary flickering
mouseOver = setTimeout( function() {
if( $(window).width() >= 960 )
$this.find('li a').stop(true, true).slideHorzShow(300);
}, 100);
}).on('mouseleave', function() {
clearTimeout( mouseOver );
if( $(window).width() >= 960 )
$(this).find('li a').not('.active').stop(true, true).slideHorzHide(150);
});
}
})();
Just remove:
e.preventDefault();

Customize dragging a div in JQUERY is not working fine when mouse moves fast

I am trying to design a custom scrollbar, I was able to do it only when mouse moves slow, If mouse moves fast the 'bar' gets out of the scroll Region.
Here is my fiddle:
http://jsfiddle.net/ghufranne/ydz2hpm0/22/
var $body = $('body');
var $target = null;
var isDraggEnabled = false;
var posY=0;
$body.on("mousedown", "#bar", function(e) {
$this = $(this);
if(e.offsetX==undefined){
y = e.pageY-$(this).offset().top;
}else{
y = e.offsetY;
};
$('#scrollRegion').offset({
top:currTop+y});
$target = $(e.target);
e.preventDefault();
});
$body.on("mouseup", function(e) {
$target = null;
});
var bottomScrollLock=false;
var topScrollLock=false;
$body.find("#scroll").on("mousemove", function(e) {
if ($target) {
var pos= $target.offset();
if(bottomScrollLock && e.pageY<posY){
bottomScrollLock=false;
}
if(topScrollLock && e.pageY>posY){
topScrollLock=false;
}
if(pos.top<scrollPos.top )
{
posY=e.pageY;
topScrollLock=true;
}
if((pos.top+heightOfBar-scrollHeight)> scrollPos.top){
posY=e.pageY;
bottomScrollLock=true;
}
noOfElToScroll = parseInt(pos.top-e.pageY+y);
$("#drag").text(noOfElToScroll);
if(!bottomScrollLock && noOfElToScroll<0 ){
$target.offset({
top: e.pageY -y
});
for(var i=0;i<1;i++){
$drop.find('li:last').after("<li>value "+(lastVisible++)+"</li>");
$drop.find('li:first').remove();
firstVisible++;}
}
if(noOfElToScroll>0 &&!topScrollLock){
$target.offset({
top: e.pageY -y
});
$("#drag").text($('#bar').offset().top);
$drop.find('li:first').before("<li>value "+(firstVisible--)+"</li>");
$drop('li:last').remove();
lastVisible--;
}
};
});
If I had used Draggable working, It would work fine.
But I don't want to use it, I want to learn it.
Look on first
and second chart. That factor is called a Jquery

JqueryUI Selectable direction

i'm trying to build some JQuery UI Selectable demo.
Is it possible to know if the user is selecting from left to right or right to left ?
I have tried this solution but it's not working
var last_selected_id="first";
$( ".selectable" ).selectable({
selecting: function(event, ui) {
if(last_selected_id=="first")
{
//first, do nothing
}
else
{
if(ui.selecting.id>last_selected_id)
{
//left to right
console.log('left to right');
}
else
{
console.log('right to left');
}
last_selected_id = ui.selecting.id;
},....
The ids are sequentially bigger from left to right
You should use the coordinates of mouse pointer provided by JQuery UI event object.
var startX;
var direction;
$( ".selectable" ).selectable({
start: function( event, ui ) {
startX = event.pageX;
direction = "";
},
selecting: function( event, ui ) {
direction = (event.pageX >= startX) ? "right" : "left";
}
});
you're missing 2 statments, one at the beggining saying the ui.selecting.id>0, and the the else if when the ui.selecting.id is lower the the last_element_id this code should work`
if(ui.selecting.id>0)
{
if(last_selected_id=="first")
{
console.log(last_selected_id);
}
else
{
if(ui.selecting.id>last_selected_id)
{
//left to right
direction = "ltr";
}else if(ui.selecting.id<last_selected_id){
//right to left
direction = "rtl";
}
}
if(ui.selecting.id>0){
last_selected_id = ui.selecting.id;
}
}
},

jQuery Dragging With Collision Detection

I have the following HTML:
<div class="list" id="list">
<div class="item" id="i1">Item 1</div>
<div class="item" id="i2">Item 2</div>
<div class="item" id="i3">Item 3</div>
</div>
<div class="timeline" id="timeline">
</div>
What I want to be able to do, with jQuery, is:
Be able to drag .items from the #list into the #timeline
.items can be dropped into the timeline as many times as required, eg. there could be 4 of item #i1 in the timeline.
.items in the timeline must not overlap each other
.items can be positioned at any place along the timeline so long as they do not overlap any other items on the timeline
So Ive gone for jQueryUI's Draggable and Droppable, and also gone for the jQueryUI Draggable Collision Plugin.
Here is the jQuery I have started with:
$('#list .item').draggable({
helper: 'clone',
revert: 'invalid',
//the following are for the jquery-ui-dragggable-collision plugin
obstacle: '#timeline .item',
preventCollision: true
});
$('#timeline').droppable({
accept: '.item'
});
My problem is that the jQueryUI Draggable Collision Plugin only works when you are dragging the original Div itself, and not dragging a helper. I need helpers so that I can achieve #2 (adding multiple copies of one item). But I need something like the Collision Plugin so I can achieve #3 (items not overlapping).
Does anybody know of a solution to this problem? Is there another plugin that does collision detection on the helper of a draggable object? Is there another approach I can try to get what I want to achieve?
If you prefer a jsfiddle to that uses the jQueryUI Draggable Collision Plugin as you suggested, here is something to play around with: Link to jsfiddle
The approach uses the original helper in order to make use of collision functionality.
The clone is generated in the start event function (and removed again in the stop event in case the dragging did not result in a successful drop):
$(function(){
var draggableSelector = ".list .item:not(.dropped)";
var init = function() {
$(draggableSelector).each(function(i){
$(this)
.draggable({
//helper: 'clone',
revert: 'invalid',
start: function(event,ui) {
var $clone = ui.helper.clone();
$clone
.removeClass("ui-draggable ui-draggable-dragging")
.insertAfter(ui.helper)
;
$(this).data("clone",$clone);
},
stop: function(event,ui) {
if( $(".ui-draggable-dragging.dropped").length == 0) {
$(this).data("clone").remove();
};
},
//the following are for the jquery-ui-draggable-collision plugin
refreshPositions: true,
obstacle: '.item.dropped',
preventCollision: true,
})
.css("left", ( ($(this).width() + 5) * i) + "px")
;
});
$('.timeline').droppable({
accept: '.item'
,drop: function(event,ui) {
ui.draggable
.addClass("dropped")
;
setTimeout(reinit, 500);
}
});
};
var reinit = function() {
$(".list .item.ui-draggable").draggable("destroy");
init();
}
init();
});
Hope that this will be useful.
Here is an example i wrote for this question showing a simple drag and drop plugin with collision detection.
It allows you to drop items onto a timeline as long as there is space for the item to exist without overlapping.
It is by no means a finished product but hopefully will show that code like this is not incredibly complex to write and trying to hack together massive conflicting plugins are not always the best option. Sometimes its best to start from scratch. Its fun, and a really good way to learn.
/*----------ON DOCUMENT READY----------*/
$(document).ready(function() {
$("#timeline").timeline({
items: ".item"
});
});
/*----------THE TIMELINE PLUGIN----------*/
$.fn.timeline = function(options) {
var defaults = {
items: "div"
}
var options = $.extend(defaults, options)
return this.each(function() {
//-----SETUP-----//
//define all the vars we will need later
var el = $(this);
var items = $(options.items);
var mousedown = false;
var dragging = false;
var activeItem = false;
var placedItems = new Array();
//make everything unselectable so it dosne interfere with dragging
$("html").find("*").css({
"user-select": "none",
"-moz-user-select": "none",
"-webkit-user-select": "none",
"-ms-user-select": "none",
"-o-user-select": "none",
}).attr("unselectable", "true").unbind("onselectstart");
//-----EVENTS-----//
//log when the mouse is down anywhere on the doc
$(document).mousedown(function() {
mousedown = true;
});
//when the mouse is released
$(document).mouseup(function(e) {
//if was dragging an item attempt to place it
if (mousedown && dragging) {
placeItem(e);
}
//log that dragging has stopped
mousedown = false;
dragging = false;
});
//log when the mouse is pressed over an item
items.mousedown(function() {
dragging = true;
//clone the active item and hide it ready for dragging
activeItem = $(this).clone().appendTo("body").hide();
});
//when the mouse movers over the doc
$(document).mousemove(function(e) {
//if mouse was pressed over item attempt to drag
if (mousedown && dragging) {
dragItem(e);
}
});
//-----FUNCTIONS-----//
//drag the item around the screen
function dragItem(e) {
//if no active item done do owt
if (!activeItem) {
return false;
}
//work out where the drag anchor is
var x = e.pageX - (activeItem.height() / 2);
var y = e.pageY - (activeItem.width() / 2);
//save the original position in case we cant place the item
if (!activeItem.origPos) {
activeItem.origPos = {
x: x,
y: y
}
}
//drag the item
activeItem.css({
"position": "absolute",
"top": y,
"left": x,
"z-index": "999",
"opacity": 0.6,
"display": "block"
});
}
//attempt to place the item
function placeItem(e) {
//if no active item dont do owt
if (!activeItem) {
return false;
}
//define som vars needed later on
var onTargetY = false;
var onTargetX = false;
var remove = false;
var collision = false;
//check if item is being relesed withing the timeline bounds
if (e.pageY > el.position().top && e.pageY < el.position().top + el.height()) {
onTargetY = true;
}
if (e.pageX > el.position().left && e.pageX < el.position().left + el.width()) {
onTargetX = true;
}
//if on target attempt to drop on timeline
if (onTargetX && onTargetY) {
//snap to the left or right if dropped at the left or right edges
var maxLeft = el.position().left;
var maxRight = el.position().left + el.width() - activeItem.width();
x = e.pageX - (activeItem.width() / 2);
if (x < maxLeft) {
x = maxLeft;
} else if (x > maxRight) {
x = maxRight;
}
//loop the items already on the timeline and check for collisions
$.each(placedItems, function(i, item) {
var itemMin = item.position().left;
var itemMax = item.position().left + item.width();
if (x + activeItem.width() > itemMin && x < itemMax) {
collision = true;
}
});
y = el.position().top;
}
//if there is a collision or the item is dropped outside the timeline
//set x and y back to original position and set removal flag to true
if (collision || !onTargetX || !onTargetY) {
x = activeItem.origPos.x;
y = activeItem.origPos.y;
remove = true;
//if dropped inside the timeline and no collisions add item to the
//array of items inside the timeline
} else {
placedItems.push(activeItem);
}
//finally either animate the item back to where it started then remove it
//or snap it into the timeline in the space found
activeItem.animate({
top: y,
left: x
}, {
duration: 300,
queue: false,
complete: function() {
//if remove flag set remove the item from the dom
if (remove) {
$(this).remove();
}
//some tidying up
activeItem.css("opacity", 1);
activeItem = false;
}
});
}
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list" id="list">
<div class="item item1">Item 1</div>
<div class="item item2">Item 2</div>
<div class="item item3">Item 3</div>
</div>
<div class="timeline" id="timeline"></div>
Enjoy :).

JQuery Resizable - Update element absolute position while resizing

I have many divs distributed in rows and columns, (generated using jQuery). Each one of these divs is re-sizable, using JQuery UI Resizable. In order to define the requirements, all divs must use absolute positioning. So, when I re-size any of these divs, the script should update the top attribute of all divs below this, (located in the same column and one row after the current resizing div).
This is the code I have now:
updatePositions: function() {
var update = 0;
$(".element").resizable({
handles: 's',
start: function(event, ui) {
event.stopPropagation();
var el = $(this);
var el_row = parseInt(el.attr("row"));
var el_rel = parseInt(el.attr("rel"));
var el_col = parseInt(el.attr("col"));
update = $(".element").filter(function() {
return(
$(this).attr("row") > el_row &&
$(this).attr("col") == el_col &&
$(this).attr("rel") != el_rel
);
});
},
resize: function(event, ui) {
update.each(function(event){
var top = $(this).position().top + $(this).height() + 20;
$(this).css("top", top )
});
}
})
}
And here is an example: JSFiddle
As you can see, all the divs are found just right and I can update the top position. But for some reason it's getting crazy when resizing! It seems like I'm doing the update as many times as found tiles on each selected div.
finally I solved this problem. JSFiddle
this is the js code:
function CacheType(){
corrtop = 0;
rel = 0;
}
$.extend({
updatePositions: function() {
var update = 0;
var arr = new Array();
$(".element").resizable({
handles: 's',
start: function(event, ui) {
event.stopPropagation();
var el = $(this);
var el_row = parseInt(el.attr("row"));
var el_rel = parseInt(el.attr("rel"));
var el_col = parseInt(el.attr("col"));
var ex = el.position().top;
var ey = el.height();
update = $(".element").filter(function() {
if(
$(this).attr("row") > el_row &&
$(this).attr("col") == el_col &&
$(this).attr("rel") != el_rel
){
var tmp = new CacheType();
tmp.corrtop = $(this).position().top - ex - ey;
tmp.rel = $(this).attr('rel');
arr.push(tmp);
return true;
}else{
return false;
}
});
},
resize: function(event, ui) {
var x = $(this).height() + $(this).position().top;
update.each(function(event){
for(var i=0;i<arr.length; i++){
var tmp = arr[i];
var rel = $(this).attr('rel');
if(rel == tmp.rel)
$(this).css("top", x + tmp.corrtop);
}
});
}
})
}
});
$(document).ready(function(){
$.updatePositions();
});

Categories

Resources