Detecting which list items are dragged into using jquery

I want artists to be able to move images between galleries and set the order in which the images are displayed in each gallery by dragging the items within and between lists.

I am using jquery to allow the user to sort the order of items in a list as well as move items between lists.

The user will then submit a form that will be processed by a php script which will store the the gallery and order information for each image in a mysql database.

Here is my html script:


<h2>Connected Sortable Lists</h2>
		<ul class="connected list">
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 1</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 2</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 3</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 4</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 5</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 6</li>
		</ul>
		<ul class="connected list no2">
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-1</li>
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-2</li>
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-3</li>
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-4</li>
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-5</li>
			<li><input type="hidden" name="order[2][]" value="Item #" readonly>Item 2-6</li>
		</ul>
		<ul class="connected list no3">
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-1</li>
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-2</li>
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-3</li>
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-4</li>
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-5</li>
			<li><input type="hidden" name="order[3][]" value="Item #" readonly>Item 3-6</li>
		</ul>
		<ul class="connected list no4">
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-1</li>
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-2</li>
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-3</li>
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-4</li>
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-5</li>
			<li><input type="hidden" name="order[4][]" value="Item #" readonly>Item 4-6</li>
		</ul>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<script src="jquery.sortable.js"></script>
	<script>
		$(function() {
			$('.sortable').sortable();
			$('.handles').sortable({
				handle: 'span'
			});
			$('.connected').sortable({
				connectWith: '.connected'
			});
		});
	</script>

The jquery based scripts let the user sort the order of items in each list as well as move them between lists.

When I submit the form and access the $_POST array in php, the items show up in the new sort order in each list determined by the second key in the order array. This is the correct order of the items after they have been dragged into the desired order.

Of course, however, they are still organized into groups according to the first key in the order array because this key was hard coded into the code when the html code was generated.

Thus, even when I drag an item from one list to another, the first order key remains the same and the $_POST array organizes the items into the lists they were in before being dragged between lists.

Is there some way I can use jquery to change the first order key when it is dragged to a different list so that the key places it in the $_POST array in the list it was dragged to?

Is there another way someone can suggest for me to achieve my goal here?

Thanks,

–Kenoli

I realize that maybe I’m thinking about this all wrong.

It’s confusing me working with jquery that operates on the file before (I guess) anything, like a post action, related to the server occurs and, on top of that, is invisible (or so it seems to me) and makes changes to the page that are not picked up in a traditional post.

I guess what I want to do is to extract an array from the DOM as it is after all the dragging has been done that is constructed something like this:

items_array(lists) [[list1][item1, item2, item3…], [list2]] [[item1, item2, item3…]]

I would then send this to my php script that will process the data.

I do need to know how that array is constructed in order to set up my query code in my php script correctly to modify my database.

It looks like the default on this is to use AJAX, though I would just as soon wait until all the dragging and dropping has been done and then send the data on to the server. Otherwise I am not sure what would have to happen, updating the page (or part of the page) every time an AJAX request is sent? I guess maybe the update isn’t needed if the page keeps looking the same after the AJAX request is sent. It just seems simpler to wait until all the changes are made and post them all at once.

What’s probably going on here is that I just don’t know how to think javascript.

What was behind my first request was a thought that I could get query to replace the first key of the order() array to correspond to the number of the list into which the item was dragged.

Help!

–Kenoli

Hi there,

Essentially you have four lists which can have zero or more elements when the user has finished dragging and dropping them.

When the user then presses submit, I would use JavaScript to construct an array representing each list and the elements it contains, then submit that via AJAX to your PHP script.

I could give you a hand with that if you fancy.

I would really appreciate that. I spent a bunch of time reading up on jquery last night and just sat down to see if I could make something work.

I guess one confusion I have is how to create a multi-dimensional array from a number of different elements in jquery. I presume it will have to be multidimensional as it will have to indicate which list each item is a member of and the order of the items in the list.

I’m going to work on creating an array and would be grateful to see how you might do it.

It is interesting to me that this forum which is about query has a spell checker that removes the “J” from in front of “query.”

Thanks,

–Kenoli

PS – I’m also a bit confused about where to insert a script in an html file. People seem to insert them everywhere, in the head, in various places in the file and, commonly, at the end. I know there is some kind of significance to this, but I haven’t been able to ferret out the underlying logic completely.

I had a sense that the jquery .ready method somehow made sure a method was not called before the page is loaded (excluding images), though I’m sure there may be times one would want a function called before the entire page is loaded. This is all confusion to me at this point.

–Kenoli

Not really. You could, for example, use data attributes to store arbitrary data on DOM elements. E.g.:

<ul class="connected list">
  <li data-list="1">Image 1</li>
  <li data-list="1">Image 2</li>
</ul>
<ul class="connected list">
  <li data-list="2">Image 1</li>
  <li data-list="2">Image 2</li>
</ul>

The question I would be most interested is answering now is what does your PHP script have to do when the user presses submit?
What exactly should it store in the database?

Once we have answered that question, we can worry about making the JS do what we want in a second step.

Never seen that before.
I’ll give it a go:

jQuery, jquery, JQUERY, Jquery

Edit:

Seems ok to me, or did you mean something else?

In 99.9% of all cases it is the best practise to insert the your scripts directly before the closing </body> tag.
This is so that the browser doesn’t block the UI while it downloads stuff, giving your page a snappier and more responsive feel.
It also ensures that any elements that your scripts might operate on are present in the DOM.

$(document).ready(function(){ ... }); does indeed wait for the DOM to load before executing any commands.
If you place your scripts at the bottom of the page as I describe above, it is almost never necessary to use this, as by the time your script comes to load, the DOM is present already.

Let me know if you would like any more clarification on this.

You Asked:

The question I would be most interested is answering now is what does your PHP script have to do when the user presses submit?
What exactly should it store in the database?

Once we have answered that question, we can worry about making the JS do what we want in a second step.

My answer:

OK. Given the following list:


<ul class="connected list">
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 1</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 2</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 3</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 4</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 5</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 6</li>
		</ul>

<ul class="connected list2">
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 1</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 2</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 3</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 4</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 5</li>
			<li><input type="hidden" name="order[1][]" value="Item #" readonly>Image 6</li>
		</ul>

etc…

The information my php script needs is the item #, the order of the items in the list and which list each item is in. The other gack in that code was just my attempt to capture the data using POST/PHP methods.

The $_POST array my script would need would look something like:

$_POST
(
[list1] => Array
(
[0] => item1
[1] => item2
[2] => item3
[3] => item4
)

[list2] =&gt; Array
    (
        [0] =&gt; item1
        [1] =&gt; item2
        [2] =&gt; item3
        [3] =&gt; item4
    )

[list3] =&gt; Array
    (
        [0] =&gt; item1
        [1] =&gt; item2
        [2] =&gt; item3
        [3] =&gt; item4
    )

[list4] =&gt; Array
    (
        [0] =&gt; item1
        [1] =&gt; item2
        [2] =&gt; item3
        [3] =&gt; item4
    )

)

I could loop through the first level (list1, list2, list3, list4) to place the items in the proper gallery based on the list they are part of and then use the keys in each list array to assign an order to each group of items.

Does this answer your questions?

Never seen that before.
I’ll give it a go:

jQuery, jquery, JQUERY, Jquery

Edit:

Seems ok to me, or did you mean something else?

No, this is what I meant. Maybe it is my computer intervening with a spell checker.

Thanks so much for your help. I’m going to be gone all day and will check in this evening (we’re probably in totally different time zones.)

–Kenoli

Hi there,

As I mentioned before, I’m really not a big fan of those hidden inputs.
You should really use data attributes instead.

This would be my first attempt at implementing what you are asking.
Try rearranging the elements and check if the output that would be sent to the server is what you need.
Lists 1, 2, 3 etc are all arrays.

Let me know if this is correct and we can implement the AJAX bit.

Demo

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sortable list</title>
    <style>
      #results { margin: 5px; padding: 5px; }
      ul > li { cursor: pointer }
    </style>
  </head>
  
  <body>
    <ul id="list_1" class="sortable connected">
      <li data-item="1">Image 1</li>
      <li data-item="2">Image 2</li>
      <li data-item="3">Image 3</li>
      <li data-item="4">Image 4</li>
      <li data-item="5">Image 5</li>
      <li data-item="6">Image 6</li>
    </ul>

    <ul id="list_2" class="sortable connected">
      <li data-item="1">Image 1</li>
      <li data-item="2">Image 2</li>
      <li data-item="3">Image 3</li>
      <li data-item="4">Image 4</li>
      <li data-item="5">Image 5</li>
      <li data-item="6">Image 6</li>
    </ul>

    <ul id="list_3" class="sortable connected">
      <li data-item="1">Image 1</li>
      <li data-item="2">Image 2</li>
      <li data-item="3">Image 3</li>
      <li data-item="4">Image 4</li>
      <li data-item="5">Image 5</li>
      <li data-item="6">Image 6</li>
    </ul>

    <button>Submit</button>
    <div id="results"></div>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
    <script>
      $('.sortable').sortable();
      $('.connected').sortable({
        connectWith: '.connected'
      });

      function getListOrder(){
        var params = [];
        $(".connected").each(function(){
          var items = [];
          $(this).children().each(function(){
            items.push("Item " + $(this).data("item"));
          })
          params.push(items);
        })

        return params;
      }

      function prettify(params){
        output = "";
        $.each(params, function( index, value ) {
          output += "<strong>List " + (index +1) + "</strong>: " + value + "<br>";
        });

        return output.replace(/,/g, ", ");
      }

      $("button").on("click", function(){
        var params = getListOrder();
        $("#results").html("<p>This is what would be sent to the server:</p>" + "<p>" + prettify(params) + "</p>");
      })
    </script>
  </body>
</html>

Pullo – Just got in. Thanks so much; I think this will do perfectly, though I will look at it closely in the am and get back to you.

The hidden inputs were there as I thought I was going to do an html post and needed to sent the data. They needed to be hidden or it interfered with the drag and drop as the items couldn’t be dragged if they were grabbed from within a visible input tag.

I was thinking I needed form tags to get data into the post. Doing it from jquery is a totally new animal.

I need to learn about data-items. Very new to javascript/jquery.

–Kenoli

Hi there,

Glad that we seem to be on the right track :slight_smile:

Just to be clear, nothing is currently being submitted to anything.
We need to submit the data via AJAX to your PHP script as a next step.

Just let me know if you need any help doing this.

Great. I’m going to take some time understanding the jquery as my own learning. I will also see if I can figure out how to submit it. I presume this will be done in jquery with .post or .ajax

A couple of Questions:

Did you extract the data as one multi-level array or three arrays?

Regardless, I presume if it is submitted as a post, each array will show up in the php script as an array element in the post, i.e. $_POST[‘List 1’], $_POST['List 2], etc. each an array with its own ‘image’ elements.

–Kenoli

I would attach an event listener to the button that catches any clicks (as it currently does), constructs the appropriate arrays, then as a second step posts them to the PHP script using jQuery’s $.ajax() method.

Currently the getListOrder() returns an array of arrays corresponding to list one, two etc.
I would simply post this one array to the PHP script, then extract what you need from there.
Try logging it to the console to get a better idea of what you are dealing with:

$("button").on("click", function(){
  var params = getListOrder();
  console.log(params);
  makeAjaxRequest(params);
});

You will obviously need to write the function makeAjaxRequest().

See above.

Let us know how you get on :slight_smile:

I have gone through your code and want to see if I understand it and ask some questions. I will go through it step by step.


<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

Links to jquery.

$('.sortable').sortable();
$('.connected').sortable({
        connectWith: '.connected'
      });

.sortable makes each tag with “sortable” in the class attribute sortable.

connectWith makes each tag with “connected” in the class attribute sortable and connected so that objects can be moved between those lists.

I’m not sure of the syntax here and why “connectWith: ‘.connected’” is within both brackets and parentheses i.e. ({ in here }).

 function getListOrder(){
        var params = [];
        $(".connected").each(function(){
          var items = [];
          $(this).children().each(function(){
            items.push("Item " + $(this).data("item"));
          })
          params.push(items);
        })

        return params;
      }

Ok. This is what I think happens:

The array params is declared.

We loop through each <ul> with class “connected” and at each stop get the content of “data-item” from the <li>, add the word "Item before it and add all of that as a new element to the array “items”.

When each loop is completed, we add the array “items” as a new element of params (params[0] first time around).

This loops until all <ul>s are scanned and assigned as elements params[1] and params[2] of the params array.

 function prettify(params){
        output = "";
        $.each(params, function( index, value ) {
          output += "<strong>List " + (index +1) + "</strong>: " + value + "<br>";
        });

        return output.replace(/,/g, ", ");
      }

This code adds html and text to each element of params and stores the “prettified” elements in the array “output”. I don’t get what the .replace function is replacing.

 $("button").on("click", function(){
        var params = getListOrder();
        $("#results").html("<p>This is what would be sent to the server:</p>" + "<p>" + prettify(params) + "</p>");
      })

This attaches an event trigger to the content of the tag with id “button” and populates the array params with the output of getListOrder.

It then inserts the text derived by looping through the “params” array with some added text into the “html” section of the div with id “#results”.

Next Step:

So, if the array “params” (without the prettify stuff) can be sent to my php script so it shows up as is as a $_POST array, I can take it from there.

For some reason, I have had more trouble making sense out of javascript than I ever did of php. In the process of playing around with the above script, I discovered that single quotes don’t seem to work where double quotes do. In php, the difference is that with single quotes, variables are not interpreted but taken as literal text, but both as far as sending text.

Another question: if I am simply looping through a tag and doing nothing to content, can I do something like:

$(this).children().each.html

and pick up the stuff between the div tags or is a function() always needed? Can I put another selector there like:

$(this).children().each.data(“item”)

Thanks again for your help.

–Kenoli

Hey Kenoli,

Yes, and jQueryUI, which is responsible for the drag and drop functionality.

Correct.

You are passing the sortable method an object literal (i.e. the {})
This object literal is a great way of passing a variable amount of parameters to a method.

Correct

Well, each element, but essentially yes. The code that does that is this:

$(".connected").each(function(){ ... });

Kind of.
1st we declare an items array.
2nd we iterate over the children of the element with the class “.connected”. In this case, that is a bunch of <li> items.
3rd for every child element, we construct a string consisting of “Item” and the value of the element’s data-item attribute.
4th we push this to the items array

Yes.

Yes.

Yes. This function is pretty superfluous. It just formats the contents of params nicely, so that you have a better idea of what params contains.

It is replacing all commas with a comma followed by a space.
Thus 1,2,3 becomes 1, 2, 3
Again, quite superfluous.

Yes.

It creates a string:
“<p>This is what would be sent to the server: </p>” +
“<p>” +
the return value of calling the method “prettify” with the parameter “params” (in this case a nicely formatted list) +
“</p>”

and sets the HTML of <div id="results></div> to the value of that string.

Exactly.

No, you can’t do this.

$.each accepts a callback which that will be executed on every object it iterates over.
http://api.jquery.com/jQuery.each/

Hope that helps.
I find it great that you are trying to get your head around this instead of just saying “Thanks, that worked”
:slight_smile:

Great. I’m looking at some references for using jquery to send the array.

Any thought would be appreciated.

Thanks,

–Kenoli

Well, I would attach an event listener to the button that catches any clicks (as it currently does), constructs the appropriate arrays, then as a second step posts them to the PHP script using jQuery’s $.ajax() method.

Is there a particular part of that you are having trouble with?

Feelings clueless.

I added this to your script (which works perfectly) and it did nothing, at least as far as I can tell.

var params = getListOrder();   
      $.ajax({
   type: "POST",
   data: {info:params},
   url: "process_ajax.php",
   }
});

“process_ajax.php” is in the same directory as the file we are working with and has a simple php script that will output the contents of the $_POST array. So,I figured when it arrived, I should see the output of the php file.

When I stick it in above your output scripts, it seems to keep them from functioning, like it killed the script.

Obviously missing something critical here.

Just to save the order of your images, this would actually be enough.

It is however probably better to assure yourself that something is getting through to your PHP script.

Change the PHP script to this

<?php
  print_r($_POST);
?>

Then add a success callback to your AJAX routine:

var params = getListOrder();   
$.ajax({
  type: "POST",
  data: {info:params},
  url: "process_ajax.php",
  success: function(res) {
    console.log(res);
  }
});

This will log the output that the server received to the console.

Here’s my code.

I get nothing back.

Not sure how to write the path, so I gave it a complete URI.

I also tried some other combinations.

This is the path where the file I am working with is served. “process_ajax.php” is in the same directory as this file.

So, If it did work, I could just write my database code and it will update the database?

Is there any way I can send the post array and the run the script so it show up in the browser?

   <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
    <script>
      $('.sortable').sortable();
      $('.connected').sortable({
        connectWith: '.connected'
      });

      function getListOrder(){
        var params = [];
        $(".connected").each(function(){
          var items = [];
          $(this).children().each(function(){
            items.push("Item " + $(this).data("item"));
          })
          params.push(items);
        })

        return params;
      }

		var params = getListOrder();
		$.ajax({
		  type: "POST",
		  data: {info:params},
		  url: "localhost/Sites/Drag_and_Drop/process_ajax.php",
		  success: function(res) {
		    console.log(res);
		  }
		});

I added some code to the first php file:

<?php


echo '<pre>';
print_r ($_POST);
echo '</pre>';

$jdata = $_POST

include("process_ajax_2"); //Include functions definition file

?>

Then created the file this script includes:

<?php

echo '<pre>';
print_r ($jdata);
echo '</pre>';

?>

Thought this would show up in the browser as well.

I think somehow the array is not being sent.

–Kenoli