On dragging one jqueryUI draggable element trigger drag on other multiple elements - javascript

I have for example three draggable elements like follows:
<div class="draggable main">Main</div>
<div class="draggable followers">Follower</div>
<div class="draggable followers">Follower2</div>
If .main is dragged, We need .followers to be dragged too. Not just positions but also triggering all the draggable events in them like start, drag, stop. Note, the followers will be normal draggable elements. Just when .main is dragged they should be moving with it as if they are in the same draggable element triggering all the .follower draggable elements' events too.
Here is the JS
jQuery('.draggable.followers').draggable();
jQuery('.draggable.main').draggable({
// If .main is dragged, We need .followers to be dragged too. Not just positions but also triggering all the draggable events in them like start, drag, stop.
});
Here is the jsfiddle
http://jsfiddle.net/9x56E/1/

Just "cheat" a little by shuffling the HTML around a little like this:
<div class="special-draggable">
<div class="main">Main</div>
<div class="draggable followers">Follower</div>
<div class="draggable followers">Follower2</div>
</div>
Adjust the CSS a little:
.draggable{
border: 1px solid red;
width: 60px;
height: 60px;
}
.main{
width: 300px;
border: 1px solid red;
height: 60px;
}
And then the jQuery:
jQuery('.draggable.followers').draggable();
jQuery('.special-draggable').draggable();
http://jsfiddle.net/Niffler/9x56E/2/
EDIT: Sorry, missed the part about it having to trigger the draggable events...
How about this:
$(function() {
$('.draggable.followers').draggable();
$('.draggable.main').draggable();
/* trigger dragstart on followers when main is dragged */
$('.draggable.main').on('dragstart', function() {
$('.draggable.followers').each(function() {
$(this).trigger('dragstart');
});
});
/* trigger drag on followers when main is dragged and adjust position */
$('.draggable.main').on('drag', function() {
var maintop = $(this).css('top');
var mainleft = $(this).css('left');
$('.draggable.followers').each(function() {
$(this).trigger('drag');
$(this).addClass('ui-draggable-dragging');
$(this).css('margin-left', mainleft);
});
$('.draggable.followers:first').css('margin-top', maintop);
});
/* trigger dragstop on followers when main is dragged and adjust position */
$('.draggable.main').on('dragstop', function() {
var maintop = $(this).css('top');
var mainleft = $(this).css('left');
$('.draggable.followers').each(function() {
$(this).trigger('dragstop');
$(this).removeClass('ui-draggable-dragging');
$(this).css('margin-left', mainleft);
});
$('.draggable.followers:first').css('margin-top', maintop);
});
/* test different actions on .followers */
$('.draggable.followers').on('dragstart', function() {
// do something
});
$('.draggable.followers').on('drag', function() {
// do something
});
$('.draggable.followers').on('dragstop', function() {
// do something
});
});
Here's a fiddle: http://jsfiddle.net/Niffler/6TZp3/

Related

How can I make the parent div draggable

I have a div containing three buttons. The div needs to be draggable, so that you can drag all three buttons around the screen together. That works fine, but the problem is that when I click on of the individual buttons it inherits the draggable id and it is draggable on it's own. I do not want that to happen. So my question is: how do I make my buttons draggable, but make them always stay together and keep them clickable. I added the code below, but here is a JSFiddle: http://jsfiddle.net/2ga50vvt/
So to be clear: the div also needs to be draggable through dragging one of the individual buttons, but then the rest of the div needs to stick with it. Now dragging an individual button only moves the button.
P.S. I do not want to use JQuery UI
HTML:
<div id="draggable" class="ui-widget-content">
<button ng-click="menu.shown = !menu.shown">MENU</button>
<br>
<button ng-click="disconnect()">CLOSE</button>
<br>
<button ng-click="">KEYS</button>
</div>
JS
$(document).ready(function() {
var $dragging = null;
$('body').on("mousedown", "#draggable", function(e) {
$(this).attr('unselectable', 'on').addClass('dragged');
var el_w = $('.dragged').outerWidth(),
el_h = $('.dragged').outerHeight();
$('body').on("mousemove", function(e) {
if ($dragging) {
$dragging.offset({
top: e.pageY - el_h / 2,
left: e.pageX - el_w / 2
});
}
});
$dragging = $(e.target);
}).on("mouseup", ".dragged", function(e) {
$dragging = null;
$(this).removeAttr('unselectable').removeClass('dragged');
});
});
CSS:
body {
padding: 50px;
}
.dragged {
background-color: yellow;
}
#draggable {
position: fixed;
width: 150px;
height 150px;
padding: 0.5em;
background: red;
background-color: black;
z-index: 1000;
cursor: move;
float: left;
}
Update 1
This is a working solution: http://jsfiddle.net/2ga50vvt/3/
However when I click on the div and start dragging the center of the div jumps to my cursor. It works great, but it looks a bit wonky. Is there a way to prevent the div from moving to my cursor?
Your help is most welcome.
You can read the target property of the event and return false to avoid all not #draggable to be draggable.
if(e.target.id !== "draggable") {
return false;
}
The edited fiddle:
http://jsfiddle.net/2ga50vvt/1/
It works perfectly, but one suggestion: don't target with ids because with this code you can't drag more of one element (ids must be unique), so the workaround is to write an attribute or a classname and play with it.
Good luck.
Use $dragging = $('#draggable'); instead of $dragging = $('e.target');
It will drag div if you try to drag using cursor on button. It will drag #draggable instead of target.
Working Fiddle
Presuming you're opposed to JQueryUI for it's file size, I'd still recommend a prebuilt solution because why reinvent the wheel?
Draggabilly is a really nifty library that I've used when resource size has been an issue. It's 20k minified (obviously even smaller gzipped) and available on a CDN - which in itself has lots of benefits e.g. caching.
$(function() {
$( "#draggable" ).draggabilly();
});
There's a few CSS hooks, different options, events etc.
JSFiddle here

How to change image side of cursor on drag event using JQuery

When you drag an file on browser screen, an image appear side of mouse cursor that is windows default image. This images is various like Copy, Move and Forbide. See its at bottom.
How can i change image side of mouse cursor to this images using javascript or JQuery? For example when i drag a file and move mouse in undragable area, forbiden image display side of cursor.
You can use the dataTransfer.dropEffect property of the dragover event to set the small image besides the cursor:
$(".targetDiv").on("dragover", function (event) {
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = "none"; // Shows the "forbidden" image
});
The values for that property are copy, move, link and none. You can test these values in the code snippet below. Please note that the originalEvent must be used. According to my tests, it works in Firefox and Chrome but not in IE.
$(function () {
$(".targetDiv").on("dragover", function (event) {
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = event.target.getAttribute("data-effect");
});
});
.targetDiv
{
display: inline-block;
border: solid 1px black;
width: 80px;
height: 50px;
margin: 4px;
text-align: center;
line-height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Drag a file over each block </p>
<div>
<div data-effect="link" class="targetDiv">Link</div>
<div data-effect="move" class="targetDiv">Move</div>
</div>
<div>
<div data-effect="copy" class="targetDiv">Copy</div>
<div data-effect="none" class="targetDiv">None</div>
</div>
You can change the cursor image by changing the property of the cursor by css using jquery.
function ondrag(event) {
$('body').css('cursor', 'wait');
}
You can check the various cursor property here.
http://www.w3schools.com/cssref/pr_class_cursor.asp
If you want to replace the cursor with a custom image you can use this:
https://github.com/Webbrother/jquery.change-cursor
If you want to limit draggables to a certain area,
Try using "containment" option:
http://docs.jquery.com/UI/Draggable#option-containment
You can do it with jquery draggable
Here is the preview what i have done
$( ".your_image" ).draggable({
drag: function() {
$(".your_image").css("cursor","url(https://cdn1.iconfinder.com/data/icons/CrystalClear/16x16/actions/move.png), auto");
},
stop: function() {
$(".your_image").css("cursor","url(https://cdn4.iconfinder.com/data/icons/32x32-free-design-icons/32/Copy.png), auto");
}
});
.your_image{
height:100px;
width:100px;
background-color:red;
cursor:url(https://cdn4.iconfinder.com/data/icons/32x32-free-design-icons/32/Copy.png), auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div>
<div class="your_image">
</div>
</div>
<div class="log">
</div>

New class keeps old class behaviour

I'm taking by first babysteps in jQuery and stumbled upon a problem I can't seem to get around.
I couldn't find an article that quite described what my problem was, so I would like to try to get an answer this way.
I don't understand why my objects keep behaving like their former class.
When I setup a hover action for a class, and change the class of the object by clicking, jQuery keeps doing the animation for the new class.
I used toggleClass() and removeClass/ addClasswithout any result:
https://jsfiddle.net/biest9160/f0na6sro/
var wide = function() {
$(this).animate({ 'width': '120px' }, 200);
}
var normal = function() {
$(this).animate({ 'width': '100px' }, 200);
}
$(document).ready(function() {
$('.class1').hover(wide, normal);
$('.class1').click(function() {
$(this).toggleClass('class1 class2');
})
})
div {
width: 100px;
height: 100px;
border: 1px solid #000;
margin: auto;
}
.class2 {
background: #555;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box1" class="class1">box1</div>
<div id="box1" class="class1">box2</div>
<div id="box1" class="class1">box3</div>
<div id="box1" class="class1">box4</div>
I don't understand why the the hover action is triggered while the object has a new class.
Initialy you attach the event to the element with the class name. After the class is changed the event remains on the element.
To remove the event you can use .unbind. To remove .hover event you can check this answer.
A working example using .unbind to remove the event and after to reattach it will look like in the snippet (basically is toggle hover event):
var wide = function(){
$(this).animate({'width':'120px'},200);
}
var normal = function(){
$(this).animate({'width' : '100px'},200);
}
$(document).ready(function(){
$('.class1').hover(wide,normal);
$('.class1').click(function(event){
var $this = $(this);
$this.unbind('mouseenter mouseleave'); // remove hover
if( $this.hasClass('class2'))
{
$this.hover(wide, normal); // reattach hover
}
$this.toggleClass('class1 class2');
})
})
div{
width:100px;
height:100px;
border: 1px solid #000;
margin: auto;
}
.class2{
background: #555;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box1" class="class1">box1</div>
<div id="box1" class="class1">box2</div>
<div id="box1" class="class1">box3</div>
<div id="box1" class="class1">box4</div>
Use .on() menthod to bind the event which will actually bind the event on the parent of the class.
Here is the example:
$(document).on("click", '.class1', function(){
$(this).toggleClass('class1 class2');
});
This will defiantly work...

jQueryUI: how to restrict droppable area to multiple divs?

It feels like the question is already answered:
How to select multiple areas in containment using jQuery UI draggable
Set more than one containment in jQuery Draggable
I have 3 timelines and some draggable elements to place there. For some reason I cannot get it to work. This is the syntax I'm using: containment: ".timeline1, .timeline2, .timeline3"
$(function() {
$(".timeline1, .timeline2, .timeline3").droppable();
$(".event").draggable({
containment: ".timeline1, .timeline2, .timeline3"
});
});
.timeline1, .timeline2, .timeline3 {
width: 500px;
height: 40px;
border: 3px dashed gray;
margin-bottom: 10px;
}
.event {
height: 40px;
background: gray;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css" />
<div class="timeline1"></div>
<div class="timeline2"></div>
<div class="timeline3"></div>
<div class="event">dance</div>
<div class="event">sleep</div>
<div class="event">eat</div>
Update found in the docs: http://api.jqueryui.com/draggable/#option-containment
The draggable element will be contained to the bounding box of the first element found by the selector
Question is still valid, I'd really like to know how to restrict droppable area to multiple divs?
I'm not sure if you can do this with the containment option, but you can use snap, snapMode and revert to accomplish what you're trying to do.
I'd add a common class like timeline-droppable to your timeline elements to make it easier:
<div class="timeline1 timeline-droppable"></div>
<div class="timeline2 timeline-droppable"></div>
<div class="timeline3 timeline-droppable"></div>
Use the snap option to snap to your droppable timelines. Use the revert option to determine whether the draggable was dropped in a valid droppable:
$(function() {
$(".timeline-droppable").droppable();
$(".event").draggable({
snap: ".timeline-droppable",
snapMode: "inner",
revert: function(droppedElement) {
var validDrop = droppedElement && droppedElement.hasClass("timeline-droppable");
return !validDrop;
}
});
});
Of course you'll need to tweak the CSS to make your event elements fit nicely inside your timeline elements.

jQuery ui sortable inaccurate placeholder placement in nested sortables

I've a sortable element inside another sortable element. Both are connected to each other.
Following the the sample markup:
<div id='container'>
<div class='parent'>
<div class='child'>1</div>
<div class='child'>2</div>
<div class='child'>3</div>
<div class='child'>4</div>
</div>
<div class='parent'>
<div class='child'>5</div>
<div class='child'>6</div>
<div class='child'>7</div>
<div class='child'>8</div>
</div>
</div>
What i'm trying to do is to drag multiple items between .parent's and #container.
Following is the JS:
$("#container").on('click', '.child', function () {
$(this).toggleClass('selected');
});
$('.parent').sortable({
appendTo: "#container",
connectWith: '.parent, #container',
revert: 0,
helper: function (e, item) {
if (!item.hasClass('selected')) {
item.addClass('selected').siblings().removeClass('selected');
}
var elements = $('#container').find('.selected').clone();
$('#container').find('.selected').not(item).addClass('hidden');
var helper = $('<div/>', {
class: 'parent'
});
return helper.append(elements);
},
start: function (e, ui) {
var len = ui.helper.children().length;
var width = ui.item.width() + 2;
ui.helper.width((len * width));
ui.placeholder.width((len * width));
},
stop: function (e, ui) {
$('.selected').removeClass('selected');
}
});
$('#container').sortable({
connectWith: '.parent',
tolerance: "pointer"
});
The following
JSFiddle
with the whole code (I removed the code for appending items to minimize the code since that is not relevant to the placeholder position) demonstrates the issue i'm facing:
I'm able to drag items from .parent to the #container as follows:
By dragging the items to the left of #container as shown below:
By dragging items above #container through the space between .parents (This is a bit tricky but it works when the center is exactly hovered over the space) as shown below:
But Once i drag an item outside, the placeholder does not appear to the right side of #container even when i directly hover the item over it.
Expected output:
Current result:
As shown in the image, the placeholder is inside the .parent even though the item is away from it, inside #container.
From what i tried, the issue is with the following:
$('#container').find('.selected').not(item).addClass('hidden');
Where i hide the selected items using display:none, Possibly causing miscalculations with jQuery UI.
So i tried refresh and refreshPositions in the start event, as in this JSFiddle, Which is produces the expected output, but it no longer properly displays the placeholder in #container in the other two scenario.
What i want to do is to be able to drag items from .parent to #container by
Dragging items to the left of #container
Dragging items above #container through the space between .parents
Dragging items to the right of #container
Side note: Most of the sortable options are added while trying to fix the issue, and the css dimensions are for demo purpose only, these can be added or removed if necessary.
Update: this seems like a bug, which i reported here. The jQuery UI team replied that they are in the process of rewriting all interactions. So hopefully this will be fixed in the next release.
In my code, it has almost become a convention to just apply this poorly documented hack to Sortable’s over and out callback functions:
if($.ui.ddmanager.current)
$.ui.ddmanager.prepareOffsets($.ui.ddmanager.current, null);
This will make sure positions are refreshed, and in contrast to the built-in refresh and refreshPositions methods, it seems to work.
Could you try this approach, and see if it improves your Sortable’s behavior?
Edit:
Never mind the third-party plugin, check this out let me know if this is what you were after: http://jsfiddle.net/6opxyvkp/7/ - old
http://jsfiddle.net/6opxyvkp/9/ - new
HTML
<div id='container'>
<div class='parent'>
<div class='child'>1</div>
<div class='child'>2</div>
<div class='child'>3</div>
<div class='child'>4</div>
</div>
<div class='parent'>
<div class='child'>5</div>
<div class='child'>6</div>
<div class='child'>7</div>
<div class='child'>8</div>
</div>
</div>
CSS
#container {
display: inline-block;
height:60px;
background:dodgerblue;
}
.parent {
float:left;
height:58px;
margin:0 15px;
border:0px solid red;
background:silver;
}
.child {
float:left;
width:50px;
height:60px;
text-align: left;
background:#fff;
border:1px solid;
margin:0px;
}
.ui-sortable-placeholder {
visibility: visible !important;
border: none;
padding:1px;
background:rgba(0, 0, 0, 0.5) !important;
}
.selected {
background:red;
}
.hidden {
display:none !important;
}
JS
$("#container").on('click', '.child', function () {
$(this).toggleClass('selected');
});
$('.parent').sortable({
connectWith: '.parent, #container',
appendTo: '#container',
revert: 0,
helper: 'clone',
helper: function (e, item) {
var helper = $('<div/>');
if (!item.hasClass('selected')) {
item.addClass('selected').siblings().removeClass('selected');
}
var elements = item.parent().children('.selected').clone();
item.data('multidrag', elements).siblings('.selected').remove();
return helper.append(elements);
},
start: function (e, ui) {
var len = ui.helper.children().length;
var width = ui.item.width() + 2;
ui.helper.width((len * width));
ui.placeholder.width((len * width));
},
stop: function (e, info) {
info.item.after(info.item.data('multidrag')).remove();
}
});
$('#container').sortable({
connectWith: '.parent',
tolerance: 'pointer'
});
If third-party jQuery plugins are allowed, take a look at https://github.com/shvetsgroup/jquery.multisortable.

Categories