Sunday, August 8, 2010

swapable - jQuery plugin


The jQuery UI Swapable plugin makes selected elements swapable by dragging with the mouse.

Introduction

Enable a group of DOM elements to be swapable. Click on and drag an element to a new spot within the list; drag element and drop one will swap and the other items will not affected. By default, swapable items share sortable properties.

DETAILS

Similar to "Sortable", but only two elements of the selected group are affected: dragged and dropped which are swapped. All other elements stay at their current positions. This plugin is built based on existing "Sortable" jQuery plugin and inherits all sortable options except "cursorAt" one which is hard-coded.
Based on jquery.ui.sortable.js
Depends:
  • jquery.ui.core.js
  • jquery.ui.mouse.js
  • jquery.ui.widget.js
  • jquery.ui.sortable.js
To add the feature to a group of elements:
Minimal configuration:

$("#swappable").swappable({items: '.itemClass',cursorAt: {top:-10} });

Option's specific: 


  • Always set option "cursorAt: {top: -nn} " as a negative Integer.
  • Always set option "items" with items' class name, not element or filter.
Example:


 

  <ul id="foo">
     <li class="bar"><li>
     <li class="bar"><li>
     <li class="bar"><li>
  </ul>


$("#foo").swappable({
   items:'.bar', // Mandatory option, class only.
   cursorAt: {top:-20}, // MUST be set to negative. Default doesn't work!

76 comments:

  1. great plugin, this is what i needed :)

    ReplyDelete
  2. Thanks,
    I'm glad to hear it :))

    ReplyDelete
  3. This looks like what I need but I cannot figure out how to get it all to work. There are a lot of dependencies, which don't seem to download, and other options. Thanks anyway.

    ReplyDelete
  4. This plugin is built on jquery.ui.sortable.js and has the same dependencies. Try sortable first. If it works in your environment, add link to this plugin. ( must be set after all dependencies)

    jquery.ui.core.js
    jquery.ui.mouse.js
    jquery.ui.widget.js
    jquery.ui.sortable.js
    jquery.ui.swappable.js <- last one

    please let me know if you have any questions.

    regards,

    -Vadim

    ReplyDelete
  5. Thats exactly what I was looking for. Very helpful. Thanks a lot.

    ReplyDelete
  6. Something must have changed. Demo not working in either Chrome or FF in both Windows and OSX.

    ReplyDelete
  7. probable it was server maintenance.

    ReplyDelete
  8. Вадим, онлайн-примеры не работают.
    Ссылка на http://jqueryui.com/jquery-1.4.2.js нерабочая, возможно, стоит её заменить на http://jqueryui.com/jquery-1.4.3.js

    Локально, с правильно подсунутыми .js, всё ок.

    ReplyDelete
  9. thanks Previden :)
    I'm glad to know that it was environment problem, not plugin's one.

    ReplyDelete
  10. hello
    nice work
    i wonder if is it possible to send the order of grids to php??

    ReplyDelete
  11. for sure it's possible. It's a regular DOM structure and can be processed as a regular DOM. There is nothing plugin-specific rules to send the order to php. What's the problem???

    ReplyDelete
  12. This is a really nicely done plugin, thanks!

    Just wandering, is there any way to add animation to the swap?

    I've had a look through the plugin but i can't figure out how to do it

    ReplyDelete
  13. Sorry for delay, Sam.
    I'm afraid it's not easy. I don't plan to implement animation in the near feature :(

    ReplyDelete
  14. This doesn't seem to work with jQuery.noConflict(true);

    ReplyDelete
  15. I apologize this does work with jQuery.noConflict(true). However it is a bit buggy when using it in a grid layout. Thank you.

    ReplyDelete
  16. Thank you for your comment.

    Could you please give me any details of the bug? Is it reproducible?

    thanks,

    -Vadim

    ReplyDelete
  17. You're welcome.

    I took a look at it deeper and it looks like you have to be exact on where you drag the item to. If you are semi-touching and still not at the right spot, it will revert. So probably this is not a bug. But rather the jQuery needs a way to inform the users that it has registered where you are swapping to. Thank you.

    ReplyDelete
  18. Sorry for the double post. Here is the steps to illustrate what I mean.

    http://www.eslinstructor.net/demo/swappable/swappable_grid.html

    On the grid layout, if you move Grid 8 to the right of Grid 9 and not have Grid 8 touch the center of Grid 9, Grid 8 will revert back to its original position. I hope this explains what I saw. Thank you.

    ReplyDelete
  19. Well, it's not a bug.

    Pointer must be inside a target block (not necessary in the center however, just inside). Frankly, I don't see any problem with such behavior.
    And thank you for using this plugin :)

    ReplyDelete
  20. I understand. Great plugin! Thank you very much!

    ReplyDelete
  21. Great!
    I have a question.
    I made a table and each cell has an id. I used this plugin and did the swap of the contents of cells. Turns out the id has not changed and I needed to change the id of the cells with the images. Is it possible?

    ReplyDelete
  22. I'm not sure I completely understand your request, but technically speaking this plugin is built on "Sortable" and inherits all "sortable" functions. So, you can add function Stop() and implement whatever you want there.

    $("#foo").swappable({
    ...
    stop: function(event, ui) {
    //modify id here
    }
    ...
    });

    hope this helps.

    ReplyDelete
  23. Very good plugin! Just what i need.
    And thanks for the last reply, it helped me a lot.

    ReplyDelete
  24. Hello Vadimk, Thanks a ton man. Very appreciated solution..... It's wonderful plugin and made my work so easy.
    thanks again...

    I have few Question,
    1. Can't we Swap with animation (slow move of elements)
    2. "cursorAt: {top: -nn} ?what exactly "nn" does?

    ReplyDelete
  25. 1) It's not easy to implement animation as internally it's not a simple position exchange between 2 elements. Elements (not only these two) make several relocations before they set their final position.

    2) negative cursorAt{} set cursor above the element you are drugging (the more nn, the more distance between cursor and top border of the element), so when you drop, event object can refer to the target element.

    and thank you for using this plagin :)

    ReplyDelete
  26. Hi,

    Swappable "cancel" -method is not working like it should. One of swappable elements gets reverted back to place it was before, but other element won't.

    Is it bug?

    ReplyDelete
  27. Sorry, I don't quite understand: are you talking about my demo or your application? I've just checked demo and it works in all major browsers under Windows/Linux.
    Please check how you implemented this plugin in your application. Thanks.

    ReplyDelete
  28. @vadimk, Thanks for answering my previous question.

    How to swap the ClassName including Elements if it's not similar (Drag_Element_Class & Target_Element_Class)

    In your given example, There is only one class used for both DragElement and TargetElement.

    Example : http://jsfiddle.net/bibhaw/vtBVR/47/

    http://stackoverflow.com/questions/5659917/to-swap-elemntswith-classname-too-when-draged-dropped

    ReplyDelete
  29. Bibhaw,
    in your example elements behave exactly as they should behave based on the css rules you apply to them. You can add or remove classes or recalculate height/width with javascript on stop event:

    $("#foo").swappable({
    ...
    stop: function(event, ui) {
    //modify elements here
    }
    ...
    });

    ReplyDelete
  30. Vadimk, that's correct but at least i need to know the className of both elements. I only want to know which function stores the Object's className/ID in your plugin

    $("#foo, #zoo").swappable({
    items:('#foo,#zoo'),
    cursorAt:-10,
    stop: function(event, ui) {
    Here I need to know the Drag_ID and Target_ID of elements.
    if(dargElement.ID == TargetElement.ID)
    { Here No need to swap the className.
    }
    if (dragElement.ID != TargetElement.ID)
    {
    var current_ID = dragElement.ID;
    dargElement.ID = TargetElement.ID;
    TargetElement.ID = current_ID;
    OR
    can use toggle
    }
    }

    ReplyDelete
  31. Vadimk, I only want to know which function stores the Object's className/ID in your plugin

    $("#foo, #zoo").swappable({
    ......
    stop: function(event, ui) {

    if (dragElement.ID != TargetElement.ID)
    {
    var current_ID = dragElement.ID;

    dargElement.ID = TargetElement.ID;

    TargetElement.ID = current_ID;

    OR can use toggle
    }
    }

    ReplyDelete
  32. =================================
    stop: function(event, ui) {
    alert("drug: "+ ui.item[0].id + ", drop: "+ event.originalEvent.target.id);
    }
    ===================================
    In your demo add ids to elements and copy/paste this fragment.

    ReplyDelete
  33. ah thanks man. resolved now.

    really appreciated. Thanks a ton

    ReplyDelete
  34. You are welcome,Bibhaw.
    And thank you for using my plugin.

    ReplyDelete
  35. Hi vadmik, Once again i need your help. Please take a look
    http://stackoverflow.com/questions/5932379/does-anybody-know-about-vadims-jquery-plugin-swappable

    ReplyDelete
  36. Bibhow, I'm not sure I completely understand your question and your goal. It looks like you assign the same set of elements to the 3 different parent elements (foo,zoo,soo), right? And what do you need to compare? You already have drug_id/drop_id. What exactly do you want to have as a result?

    ReplyDelete
  37. Please see the question again, i have been updated it.
    YOU?? It looks like you assign the same set of elements to the 3 different parent elements (foo,zoo,soo), right?

    #foo {width: 100px, height:200px etc..}
    #zoo {width: 150px, height:250px etc..}
    #soo {width: 125px, height:225px etc..}
    NO, There are different set of elements (width, height etc...), when i drag (element of #foo) over #zoo's element so therefor i need to change the height & width of both the elements.

    in case of #soo is not swappable : $("#foo,#zoo").swappable({ ...}); #soo elements can't be drag but anyone can drop the #foo OR #zoo elements over #soo elements so in that case, we need to protect.

    ReplyDelete
  38. Bibhaw,
    I don't see anything specific to this plagin in your question. From plugin you already have drug and drop ids; I don't think you need help to find parent elements, right?

    ReplyDelete
  39. This comment has been removed by the author.

    ReplyDelete
  40. oops, it seems you didn't understand my question. BTW thanks for your time.

    ID : #foo, #zoo, #soo

    ("#foo, #zoo").swappable( { ....
    ......
    stop : ......
    .............
    How to authenticate...
    drag_id,drop_id is elements of #foo, #zoo ???
    OR
    drag_id_parentId, drop_id_parentId is swappable and exist in [ ("#foo, #zoo")] ???

    ReplyDelete
  41. hi, Vadimk. thank you for your plugin. very nice!

    but i've found a bug in it. reproduced in IE8.

    see swappable list.

    swap 1st and 3d elements such way that 1st element will be between 3d and 4th.

    see screenshot:

    http://xmages.net/storage/10/1/0/3/1/upload/086a9fe2.png

    than when you drop the item, the items 1 and 3 will be swapped. But you will see no margins between 2, 1 and 4.

    see screenshot:

    http://xmages.net/storage/10/1/0/6/5/upload/7a61e2d9.png

    it is not very critical when you have margin-bottom: 3px (as in your online example).
    but when you have 10px or even more it could be look not very good.

    could you please provide solution for this issue?

    Thank you!

    Vasily

    ReplyDelete
  42. Hi Vasily,
    thank you for using this plagin.
    I see your point, but I'm afraid I wan't be able to look into the problem in the near future: I'm too busy with my current project. But if I find time I'll try to help you. Sorry, can't help you right now.

    Again, thank you,

    -Vadim

    ReplyDelete
  43. Hi,

    I've found a solution for defect with disappearing margins.
    It is hack, not very nice, but it works: we need to call any event before calling this._clear(event, noPropagation) function inside _mouseStop function:

    see new code inside the comments:

    } else {
    /***************************************/
    if ($.browser.msie) {
    target.click();
    }
    /***************************************/

    this._clear(event, noPropagation);
    }

    --

    Vasily

    ReplyDelete
  44. Hi Vasiliy.

    Thank you for sharing your solution. I really appreciate it. Thank you.

    ReplyDelete
  45. Hi, great plugin by the way, just what I needed!

    How can I style the target element during dragging, e.g. when the pointer is over the target element?

    ReplyDelete
  46. Hi Richard.
    Thank you for using my plugin.
    The simplest way to style target element is to use CSS :hover selector. Something like this:

    li:hover
    {
    border:1px solid red;
    color:blue;
    }

    ReplyDelete
  47. I am trying to use swappable plugin to develop a web based iphone app. It doesnt work on iphone browser, though sortable works perfectly on it.
    can you suggest any changes

    ReplyDelete
  48. Never tested it on iphone. No idea, sorry.

    ReplyDelete
  49. Every time an item is moved, I would like to trigger a call to a function. How can I implement that using Swappable? I tried adding the "change: " detection found for "sortable" but no luck. Any advice/suggestions?

    ReplyDelete
  50. NM.... I got it.

    I should have used "UPDATE" not "CHANGE".. As follows...

    $("#swappable").swappable({
    items: '.ui-state-default',
    cursorAt: {top:-5},
    update: function(sorted){
    ...(Run your functions here)
    }
    });
    $("#swappable").disableSelection();

    ReplyDelete
  51. Hi,

    Have you tried to get this to work on a tablet or mobile? cant seem to drag and drop this on touch enabled devices.

    ReplyDelete
  52. Hello.
    I have been using this great plugin for a while but now trying it on iPad it did not work. Do you know if I am doing something wrong or if this plugin just doesn't work with iPad?

    Many thanks,
    Starki

    ReplyDelete
  53. Sorry, didn't check this branch for a while.

    Frankly, I've never tested this plugin on mobiles or tablets. So I simply don't know if it works on these platforms or not.
    Sorry.

    ReplyDelete
    Replies
    1. Hi Vadim,

      Would you be interested in getting this to work with tablets? It could be a job i'm willing to pay you for.

      Delete
    2. Hi Moiz,

      Thank you for your request. I'm afraid I don't have time to work on this plugin now as I am busy with other projects. Sorry.

      Delete
  54. I've never tested this feature. I think the best way to discover is to try it. Please let us know the result.
    Thank you,

    -Vadim.

    ReplyDelete
  55. Hi, how can I get the plugin to work with touch events. I need it to work on tablets and mobile devices. Can you help ?

    ReplyDelete
    Replies
    1. Hi Moiz,

      this plugin works with mouse events only. If you look into the code you see _mouseStart() and _mouseStop() functions, plus _clear() function to cleanup. All 3 functions are taken from "Sortable" plugin, customized and than added to the original one so that they overwrite original functions. If jQuery-mobile UI has "Sortable" plugin, you can customize it the same way.
      But I'm afraid I can't help you with moving this plugin to mobile platform as I'm busy with other projects and don't have time for this development. Sorry.

      Delete
  56. Vadim, great plugin thanks! I've fixed the requirement to set the cursorAt property so now it has the same L&F as the sortable plugin. I posted my version to GitHub and gave you attribution for authorship. https://github.com/agoodno/jquery.swappable.js. I committed the original and then made my modification. I was hoping to get your approval on the change so you could assign it an official new version. Once again, thanks for the plugin, it saved me a lot of time.

    ReplyDelete
  57. Nice job, Andy!

    I created github repo
    https://github.com/vkiryukhin/swappable.
    You can fork the source, modify it and then (if you want) pull request to merge your version.
    1. Add your name in the comment area at the top.
    2. Get rid of "cursorAt" attribute in the examples and other pages.
    3. I would include source of "jQuery Nearest plugin v1.2.0" directly into the swappable.js file to avoid one more dependency link.

    Please test your changes in major browsers.

    And thank you for making you code public!

    Best regards,

    -Vadim

    ReplyDelete
  58. This is great!
    Q: How to update this to use with mysql? (in case that we want to save our ordering)

    ReplyDelete
    Replies
    1. Aleksandar,

      I already answered this question here, in this branch. Please take a look at my reply from April 21, 2011 (http://vadimkir.blogspot.com/2010/08/swappable-jquery-plugin.html?showComment=1303372364279#c4715000216053849224)

      Simply use "Stop()" member function, where you can get elements' order and save it in your DB.

      Best regards,

      -Vadim

      Delete
    2. This comment has been removed by the author.

      Delete
  59. looks like if we use it like:
    $(".sortable").swappable({
    placeholder: "ui-state-highlight",
    items: '.ui-state-default',
    cursorAt: {top:-1},
    revert: true
    });
    it has some kind of animation but it doesn.t work properly...do you have any ideea why?

    ReplyDelete
  60. Vadim, great work !
    works perfectly and the database!

    But, when i want to put betwen li tag, div or table html tags, code stop to work..

    How to fix this problem?

    ReplyDelete
    Replies
    1. http://ebchost.info/drag/swappable_list.php
      At this link you will see what I mean, but also, on the right side of example, which shows the formations, you can see the new problem that i met when modifying the code (try to drag right side player and the GK) and for that reason more experienced please help ?

      "ui-state-highlightt" css code is very difficult to menage for me.

      Thanks forward

      Delete
  61. Hi vadimk,
    Your plugin works really awesome. I just need to know how to make the ul swappable on a click and make it disable on another click. What I did here is:


    /***********************************************************************************/
    function swap_elements()
    {
    if($("#swappable > li").hasClass("ui-state-default"))
    {
    alert("Remove");
    $("#swappable").removeClass("ui-sortable");
    $("#swappable > li").removeClass("ui-state-default");
    $("#swappable > li").css({"border":"none"});
    $("#plan_list_id > ul").attr("id", "unswap");

    }
    else
    {
    alert('Add');
    $("#plan_list_id > ul").attr("id", "swappable");
    $("#swappable").addClass("ui-sortable");
    $("#swappable > li").addClass("ui-state-default");
    $("#swappable > li").css({"border":"1px solid #780030"});

    $("#swappable").swappable({
    items: '.ui-state-default',
    cursorAt: {top:-5}
    });
    $("#swappable").disableSelection();
    }
    }

    /***********************************************************************************/

    This code doesn't let the li to be swappable after removing the id(swappable) and class(ui-sortable) from ul and removing class(ui-state-default) from all li elements. But all the li elements are still draggable. How to make them un-draggable too after removing the class? Any help regarding this would be really appreciated.

    Thanks for the plugin though. Its really great. :)

    ReplyDelete
    Replies
    1. Hi Anonymous,

      I don't think you need to manipulate with classes. Enable / Disable functionality can be done this way:

      --------------------------------------------------------

      function toggleIt(){

      var isDisabled = $( "#swappable" ).swappable( "option", "disabled" );

      if( isDisabled ) {
      $( "#swappable" ).swappable( "enable" );
      } else {
      $( "#swappable" ).swappable( "disable" );
      }
      }

      -------------------------------------------------------------------

      Best regards,

      --Vadim

      Delete
  62. Just want to say thank you for this. This is exactly what I was looking for so I could implement a drag and drop combat button interface similar to those seen in many MMOs. I am implementing this in my web game at nevergrind.com. Thanks a ton!

    ReplyDelete
  63. Hi Vadimk,
    I need to know the new location of the moved item, can I?
    For example: a list of 10 elements as follows (100,500,200,040,999,22,88,11,10,5) I move the first element to the seventh position, how do I know that it was moved to the seventh position?

    Thank you

    ReplyDelete
  64. Hi Vadimk. Thanks for the plugin! I was faced with some unexpected behaviour when trying to implement it. Everytime I swap two list items, an extra list item is appended to my unordered list. What am I missing?

    ReplyDelete
  65. Hi Vadimk, Thanks for the plugin :), I have one issue - i am creating tables dynamically and applying the swappable to the td's it is working fine, but when i am creating multiple tables the swapping is happening between the two table (or more) td's. I need to happen the swapping only in one table and not across tables. Any help would be appreciated.

    ReplyDelete