Writings, notes, and work of a Boston based designer

Combining Selectables And Draggables Using jQuery UI Nov 23 + 2 comments

written by: ryan coughlin

There are two powerful and fun elements that the jQuery UI provides. When both of them work together you will be able to come up with some creative web application uses for them. The two I am going to be covering are:

I am sure you all want to see what the final product will look like, check it out.

To start working with the jQuery UI you are going to need to add the following files to the head of your document:

  • jquery-1.2.6.min.jsthe main core jquery framework
  • ui.core.jsthe UI core javascript
  • ui.draggable.js - the draggable javascript
  • ui.selectable.jsthe selectable javascript

Before we get started let me just say, thanks a ton to Richard Worth who has helped develop the jQuery UI and provided a ton of help to me last year trying to get this to work, and boom here is the working copy, you can find Richards blog here.

The jQuery UI adds classes for different mouse events, for instance:

  • jQuery UI Draggable
    • .ui-draggablethe class it adds you when create the draggable(); object
    • .ui-draggable-dragging - the class it adds when you are dragging the object around the page
  • jQuery UI Selectable
    • .ui-selectee - the class it adds when you create the selectable(); object
    • .ui-selectablethe class it adds when you select the object
    • .ui-selecting – the class it adds when you click and start to create the bounding box around the elements you wish to select, once you let go, it becomes .ui-selectable

That is the basic structure how how the jQuery UI draggable and selectable generates classes to manipulate change for each action.

I am going to be using console.log(); to report what the code is doing after different steps throughout the page. Make sure you have Firebug installed if you are on Firefox. If you are unsure what console.log() does view the documentation. Be sure that if you use this technique in a real world example, remove the console.log() functions to decrease page loading time. This neat little function allows us to see what our code is doing, we can call:

console.log(variable);

When we open up the page and open Firebug, it will print out what the variable contains, it is a great tool for debugging, keep it handy.

Now that we have the understanding on how it works with different classes, lets start looking at the code that makes it work. I have commented it to make it easier for you to translate on your own time.

The first bits of the code

// creates the selected variable
// we are going to be storing the selected objects in here
var selected = $([]), offset = {top:0, left:0};
console.log("This is the value of selected when it is created " +selected + "");

As you can see from above, our first line creates the variable selected and is able to store numerous values because we are setting up an array for this variable. And the offset, resets the top and left values. If you use console.log it will output: Object length=0 prevObject=Object jquery=1.2.6 notice the length is 0 that is fine we haven’t selected any items yet.

// initiate the selectable id to be recognized by UI
$("#selectable1").selectable();

This part above, calls the selectable to generate the selectable region. NOTE: that #selectable is container and is the box not each element we are selecting.

The following part is important because, this is the where we start to hook up selectables with the draggables.

// declare draggable UI and what we are going to be doing on start
$("#selectable1 div").draggable({
     start: function(ev, ui) {
          $(this).is(".ui-selected") || $(".ui-selected").removeClass("ui-selected");
	  console.log("The value of 'this' currently is: "+this);
          selected = $(".ui-selected").each(function() {
               var el = $(this);
	       el.data("offset", el.offset());
	       $(this).text("Selected and dragging object(s)");
	  });
console.log(selected);
offset = $(this).offset();
},

The part below, tells the draggable UI to create every element within to have dragging.

$("#selectable1 div").draggable({
$(this).is(".ui-selected") || $(".ui-selected").removeClass("ui-selected");
console.log("The value of 'this' currently is: "+this);

This next section above resets the select and drag, by removing the ui-selected class, when we output using console.log, to see the value of this is The value of ‘this’ currently is: [object HTMLDivElement].

selected = $(".ui-selected").each(function() {
     var el = $(this);
     console.log("The value of el is: "+el);
     el.data("offset", el.offset());
     $(this).text("Selected and dragging object(s)");
});

Now we are finally working with the selected variable we created at the start of this, we are to run through each element with the class of ui-selected. We are going to be storing the value of $(this) in the variable el, to make it easier to call and to work with the offset(); property later on.

el.data("offset", el.offset());
$(this).text("Selected and dragging object(s)");

We are going back to out el variable and working with the data object and the offset object. The offset object returns two integer values: top and right. The definition of the offset(); from the jQuery documentation is: Get the current offset of the first matched element relative to the view port.

The other line Viagra, changes the text of the elements to let us know that we have selected and are now dragging the objects stored in the variable selected.

Now that we have called offset we want to store it in the variable offset, for each DOM elements selected. The code that follows this part is below:

console.log("The new value of selected is now "+selected);
offset = $(this).offset();
console.log("The value of top value is "+offset.top+" and the left value is "+offset.left);

We have another console.log() to print out the selected value and it is storing the DOM element. Next we create the variable offset and store the offset() of the elements, I have printed the top and left values using console.log() above, the output is similar to: The value of top value is 207 and the left value is 672.

The next section below is the section of code that handles what is going to go on while we are dragging our selected elements. This part is also very important because we are bringing back the values of offset with the current values of the draggable ui, it stores the ui properties in ui.position.top and ui.position.left. Further down the code we will use the offset values to update the CSS top and left properties of the selected elements.

drag: function(ev, ui) {
     var dt = ui.position.top - offset.top, dl = ui.position.left - offset.left;
     console.log("The value of dt is "+dt);
     selected.not(this).each(function() {
          var el = $(this), off = el.data("offset");
          el.css({top: off.top + dt, left: off.left + dl});
     });
},

We are creating the variable dt on the second line there and this value is equal to ui.position.top – offset.top and we are also creating the variable dl that is dl = ui.position.left – offset.left. So we have:

  • var dlworking with the x axis or the left coordinates
  • var dtworking with the y axis or the top coordinates

Lets add a console.log to see what these values are before we do the math. Add the following code to see what our variables and math are doing:

console.log("The value of dt is "+dt+" and is equal to "+ui.position.top+" - "+offset.top);
console.log("The value of dl is "+dl+" and is equal to "+ui.position.left+" - "+offset.left);
console.log("The value of ui.position.top is "+ui.position.top);
console.log("The value of ui.position.left is "+ui.position.left);
console.log("The value of offset.left is "+offset.top);
console.log("The value of offset.left is "+offset.left);

When you run the code the console.log() outputs the following:

The value of dt is 37 and is equal to 237 - 200
The value of dl is 152 and is equal to 352 - 200
The value of ui.position.top is 237
The value of ui.position.left is 352
The value of offset.top is 200
The value of offset.left is 200

Notice what the code is printing out, it is taking the ui.position.top and subtracting the value of offset.top and giving us the difference to equal variable dt. We do this math so we can update the top and left CSS properties in the next section of code, if we didn’t do this math, we would select and drag our elements and they would move sporadically on the page.

Next we are going to cover the other each function that loops through each of the selected elements expect $(“this”):

// take all the elements that are selected expect $("this"), which is the element being dragged and loop through each.
selected.not(this).each(function() {
     // create the variable for we don't need to keep calling $("this")
     // el = current element we are on
     // off = what position was this element at when it was selected, before drag
     var el = $(this), off = el.data("offset");
     el.css({top: off.top + dt, left: off.left + dl});
});

So we have the last big piece of code to make this script work. Like I said, you have the each function that runs through each element expect $(“this”), then we create two variables:

  • elwhich is the current element we are on
  • offwhich is the position of the elements when they were selected, before any type of movement and drag

The last segment: we are going to be adding or subtracting (we will subtract if the dt or dl is negative). The dl stands for delta left and dt stands for delta top. For instance lets set something up:

The current element we are on start at the coordinates: 10, 30, now we drag that to: 8, 15. Now lets run the math, the new position of the element is: 2, 15. Say, you drag it again, it runs the loop once again, and the off will be 2, 15 and the new value will be wherever the new coordinates are subtract the total delta from when the drag started until now, not just the last mouse movement.

Now that we did the math and we have captured the new value, update it is the css property and plug in the top and left coordinates.

So what did you think of it? Pretty interesting effect when you combine both of those UI elements to create select and drag. Share your thoughts and make sure to bookmark using the button below. Any improvements? Contact me.

Enjoy!

Take a peak at some more?

  • http://pixdev.cowfight.com/ Wes

    I guess now the task is getting a droppable() to accept them simultaneously… :-]

  • http://www.ryancoughlin.com Ryan Coughlin

    @Wes its true! it would make using those two UI elements flawless. I have been talking to Richard Worth one of the developers on the UI and that is their goal to get those two elements playing together as one function, so hopefully all in good time.

  • ReTox

    I don’t understand this article -

    this demo doesn’t work either in FFox 3, Opera 9.64 or IE 7….

  • http://www.ryancoughlin.com Ryan Coughlin

    @ReTox I just gave it a shot and seemed to work fine for me. Did you get any errors in Firebug?

  • http://www.standupspace.com Slackjaw47

    In IE 7 the javascript error is “‘console’ is undefined.” It doesn’t work for me either in FF or IE 7. Firebug doesn’t report an error but IE 7 does. Looking forward to seeing the end product for this functionality especially if its included for 2 connected sortables lists.

  • Pingback: Combing Selectables And Draggables - Select/Drag - Using jQuery UI - Tutorial Collection

  • http://sinanyasar.com Sinan

    It was hard to get it work from the code snippets above but thanks for sharing.

    // creates the selected variable
    // we are going to be storing the selected objects in here
    var selected = $([]), offset = {top:0, left:0};
    console.log(“This is the value of selected when it is created ” +selected + “”);

    // initiate the selectable id to be recognized by UI
    $(“#selectable1″).selectable({
    filter: ‘div’,
    });

    // declare draggable UI and what we are going to be doing on start
    $(“#selectable1 div”).draggable({
    start: function(ev, ui) {
    selected = $(“.ui-selected”).each(function() {
    var el = $(this);
    el.data(“offset”, el.offset());
    //$(this).text(“Selected and dragging object(s)”);
    });

    if( !$(this).hasClass(“ui-selected”)) $(this).addClass(“ui-selected”);
    //console.log(“The value of ‘this’ currently is: “+this);
    console.log(selected);
    offset = $(this).offset();
    },
    drag: function(ev, ui) {
    var dt = ui.position.top – offset.top, dl = ui.position.left – offset.left;
    console.log(ui.position.top,offset.top);
    //console.log(“The value of dt is “+dt);

    // take all the elements that are selected expect $(“this”), which is the element being dragged and loop through each.
    selected.not(this).each(function() {
    // create the variable for we don’t need to keep calling $(“this”)
    // el = current element we are on
    // off = what position was this element at when it was selected, before drag
    var el = $(this), off = el.data(“offset”);
    el.css({top: off.top + dt, left: off.left + dl});
    });
    }
    });

    This is how i could get it running.

  • http://www.ryancoughlin.com Ryan Coughlin

    Did the code in the demo file help you at all? Also, thanks for posting your working code. Glad you got it working.

  • http://www.mikesmullin.com/ Mike Smullin

    Ryan, the code works great–but only if Firebug is enabled. I almost passed it up but since you are ranking #1 on Google for this term I gave it a few shots and eventually discovered the reason when I enabled Firebug to see if I could figure out where it was broken.

    It’s probably because you’re using console.log().

    Hope this helps you help the other readers! :)

  • Jim

    Can you at least provide a working demo?

    If it doesn’t work with FF, IE, or Opera, I don’t see much point in even bothering with the code.

  • Pingback: 25 Tutorials and Resources for Learning jQuery UI - Speckyboy Design Magazine

  • Pingback: 25 Tutorials and Resources for Learning jQuery UI · rogdykker

  • Pingback: 25 Tutorials and Resources for Learning jQuery UI | Programming Blog

  • Pingback: 25 Tutorials and Resources for Learning jQuery UI | EMDMA

  • http://www.irrenhausag.com Madman

    Hey, thank you! Helped me a lot! *Thumbs Up*

  • http://Yourwebsite... Mircea

    Great demo!
    Is there any way to make it work with jQuery 1.4?
    It does not work with 1.4 and I can’t figure it out why.

    Thank you.

  • http://ateneo-socialista.org/articles/?article=7864824 Melissa Domenico

    Great post, thank you. I signed to your blog RSS.

  • http://Yourwebsite... ichbindiegute

    This is exactly what I was searching for, thank you!
    Do you have any clue why the script doesn’t work with JQuery 1.4.X?

  • http://Yourwebsite... sachin

    Hi Ryan

    Thanks for this useful article.
    its works perfect to me but i also want the droppable functionality.

    the draggable elements should be returned back to original place if i dropped them in a non droppable place.

    Please help me out

  • http://Yourwebsite... aj_dev

    The demo isn’t working… Not in Chrome, IE, or FF with firebug installed. Firebug errors:

    Unknown property ‘font-color’. Declaration dropped.
    [Break On This Error] $(function() {
    index.html (line 54)

    reference to undefined property fbs.breakpoints[url]
    [Break On This Error] return fbs.breakpoints[url];
    firebu…vice.js
    (line 3236)

    $ is not defined
    [Break On This Error] $(function() {
    index.html (line 54)

    reference to undefined property persistedState.panelState[panel.name]
    [Break On This Error] return persistedState.panelState[panel.name];
    firebug.js
    (line 1274)

    IE gives this error:

    Webpage error details

    User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; InfoPath.3)
    Timestamp: Sat, 20 Aug 2011 12:13:31 UTC

    Message: Object expected
    Line: 54
    Char: 2
    Code: 0
    URI: http://ryancoughlin.com/demos/dragselect/index.html

    Would be really great if it worked

  • http://www.facebook.com/ericbmorton Eric Morton

    The complete demo (linked at start of article) does not seem to work (broken link to jquery lib).
    Copypasted the code from that page to a jsfiddle which works nicely: http://jsfiddle.net/PDPZx/

  • SomeOne

    When was this post written? (Noticed now it has “2008″ in URL, but still… this is useful info, please don’t omit it). Nice post!

  • Konga Raju

    it is only selecting multiple items not useful to drag multiple items
     

  • eric socolofsky

    even though this post is old, bits and pieces of it can still be found all over the tubes. unfortunately, they all take some digging to make them actually work as expected, and with modern versions of jQuery/UI, but the basic concept remains the same:

    - use selectable() to enable marquee (lasso) selection and click-container-to-deselect;

    - use a click handler to manually set jQueryUI selectable classes (‘.ui-selectable’, etc)

    - hook into draggable() to copy the offset to all selected elements.

    i forked a jsfiddle from a stackoverflow conversation about this (http://stackoverflow.com/questions/705250/is-there-a-jquery-plugin-which-combines-draggable-and-selectable#8643716) with clearer comments and updated jQuery/UI libs. you can find it here:

    http://jsfiddle.net/6f9zW/

    hope this helps others get up and running more quickly!

  • Annonymous

    The fiddle doesn’t work… lol :D (guess modernizer or something is missing..)

  • freshfelicio

    thanks man this really helped me!