Posts and Pages Tagged ‘jQuery’

Drag and drop with jQuery UI Sortable Last updated:9 December 2012

Last September I wrote a bespoke e-commerce solution for my wife’s website, including all the accompanying admin screens. One admin screen allows the amendment of the display order of items in a particular category. It was, if I’m honest, a bit clunky, as you had to type in the display order as a number, and then refresh the screen to see what the resultant re-order looked like. In response to repeated requests of “Can’t you make it drag and drop…?” I switched from responding with hollow laughter and actually had a look at how this might be done. And surprisingly, it was pretty straightforward. Here’s how

Setting up the database

I already had a database with a table called items, each of which had a unique ID field and another field called Display_Order. The images for each item are all named in the format [ID]-x.jpg to make life easier.

Displaying the items on the “Maintain Display Order” admin page

I started off just setting up the page to display all the items. Straightforward php, and pretty much what I already had. I’m retrieving the items from the database and displaying them in an unordered list, which I’ll use later for the drag and drop.

$result = mysql_query("SELECT * FROM items ORDER BY Display_Order ASC");
echo "<ul id='categoryorder'>";
while ($item = mysql_fetch_array($result))
{
echo "<li id='ID_".$item['ID']."'><img src='.$item['ID']."-1.jpg' 
height='90' width='60' alt='No file on server' title='Preview'></li>";
}
echo "</ul>";

A couple of things to note here – I’ve assigned an id categoryorder to my list, and each item in the list has its html id set as “ID_” followed by the database ID. This is important later on, as we’ll see – don’t miss the underscores. Your resultant html should look like this:

<li id="ID_1">...</li>
<li id="ID_2">...</li>

Then, I added a bit of css to make my list into two columns of images to match the real page layout.

Adding the drag and drop

I used the jQuery UI sortable plugin. You can download jQuery, jQuery UI base and the plugin from http://jqueryui.com/download. Having done this, I then need to add links to these javascript files in the head of my page

<script type="text/javascript" src="/jquery-1.4.2.min.js"></script><br>
<script type="text/javascript" src="/jquery-ui-1.8.1.custom.min.js"></script>

In order to make my list items draggable then I need to add just the following code:

<script type="text/javascript">
$(document).ready(function(){

  $("ul#categoryorder").sortable({ 
         opacity: 0.6, 
         cursor: 'move'  
         });

});
</script>

All I’m doing is invoking the “sortable” plugin when the page loads and assigning it to items in my #categoryorder list. I’m also setting the opacity of the dragged item to be 0.6, so it’s a bit faded when it’s actually being dragged. Finally, I’m setting the cursor to change when I’m dragging an item. And that’s it for the draggable bit – my items can now be dragged and dropped into whatever new order I want.

Giving a visible indication that the order has changed

Once I’ve changed the order, it’d be nice to display a message to say that the are changes that haven’t yet been saved, and I can do this by using the update Event on sortable:

<script type="text/javascript">
$(document).ready(function(){

  $("ul#categoryorder").sortable({ 
         opacity: 0.6, 
         cursor: 'move'<span>,  
         update: function(){
                $('#categorysavemessage').html('Changes not saved');
                $('#categorysavemessage').css("color","red");
                }</span>
         });

});
</script>

Here I’m using the update option to populate a predefined message field (#categorysavemessage) with some red text to say “Changes not saved” whenever the sort order is changed.

Updating the database

I don’t want to update the database every time I move an item, just at the end when I’m happy, so I’ve added a “Save now” button. I now need to create a javascript function to fire when that button is clicked, and here’s the javascript I need:

function saveDisplayChanges()
{
var order = $("ul#categoryorder").sortable("serialize");
$('#categorysavemessage').html('Saving changes..');
$.post("update_displayorder.php",order,function(theResponse){
$("#categorysavemessage").html(theResponse);
$('#categorysavemessage').css("color","green");
});
}
</script>

Here I’m first populating a variable called order with the “new” order of my items, using the “serialize” method of the sortable plugin. This looks at the html id of each list item which refers to the ID in my database, and builds a query string. I’m then passing the resultant string via AJAX to a separate php file called update_displayorder.php which updates the database. Here I’m using the jQuery form of AJAX call. Finally, when I get a message back from the php file I’m updating the message field.

Here’s the php code which will actually update the database:

$newOrder = $_POST['ID'];
$counter = 1;

  foreach ($newOrder as $recordIDValue) {
  $query = "UPDATE items SET Display_Order = " . $counter;  
  $query .= "WHERE ID = " . (int)$recordIDValue;
  mysql_query($query) or die('Error, insert query failed');
  $counter ++;
  }

echo 'Changes saved';

So here I’m just extracting the data from the post, and then stepping through each item, updating the display order for each one with a variable which I’m incrementing for each item.

And that’s it – it works, with very little code, thanks to the jQuery UI plugin. Nice. I’ve tested in IE6, IE7, IE8, Chrome, FF3, Opera 9, Opera 10 and Safari 4, all on Win/XP, and Safari 5 on Mac OS X/Snow Leopard.

Here’s a little demo – it doesn’t talk to a real database, but you can see the drag and drop and the resultant code that would be passed back to drive the update.

jQuery UI Autocomplete with a remote database and PHP generating JSON data Last updated:23 October 2012

I spent some time last night struggling with jQuery UI Autocomplete. I was trying to use a remote data source, and was calling a php script to interrogate the database, no ajax. During the course of getting it to work properly, I discovered a couple of things that aren’t very clear in the documentation.

The basic javascript, lifted straight from the jQuery UI demo is:

$("#autocomplete").autocomplete({
source: "search.php",
minLength: 2,//search after two characters
select: function(event,ui){
    //do something
    }
});

Fine so far. There was one major thing that fooled me. If, instead of using a php script, you use a local source, something like:

source: [{"value":"Some Name","id":1},{"value":"Some Othername","id":2}]

then this local data is queried each time a character is entered in the required field. I assumed, therefore, that all I had to replicate in my search.php script, was a query to return all the values from my database. Wrong! I need to pass a search term to my php script in order to return only the correct subset of data. I further discovered that jQuery UI Autocomplete passes this data as a GET entitled ‘term’ (didn’t see this anywhere in the examples). So, armed with this knowledge, my php script looks like this:

//connect to your database

$term = trim(strip_tags($_GET['term']));//retrieve the search term that autocomplete sends

$qstring = "SELECT description as value,id FROM test WHERE description LIKE '%".$term."%'";
$result = mysql_query($qstring);//query the database for entries containing the term

while ($row = mysql_fetch_array($result,MYSQL_ASSOC))//loop through the retrieved values
{
		$row['value']=htmlentities(stripslashes($row['value']));
		$row['id']=(int)$row['id'];
		$row_set[] = $row;//build an array
}
echo json_encode($row_set);//format the array into json data

Hope this explanation is useful. No demo this time, because it doesn’t seem very interesting. You can have a look at an integration of jQuery UI Autocomplete with a vertical slider here and see a demo here.

jQuery fadeOut in Firefox 10 Last updated:20 June 2012

Having an odd week this week.

While viewing a client’s site today in Firefox 10 I noticed that the crossfade on the site, loaded just before Christmas, wasn’t quite right – the images should crossfade into one another (using this script), but what happened was the image faded to black and then the next image appeared. I observed the same effect on the malsup cycle plugin, used elsewhere on the site.

Now the same script works fine in Opera 11 and Chrome, and it works fine in Firefox 10 on my demo page listed above. So what’s the difference? The only difference is that one image has a border, and one doesn’t. Add the border and the crossfade works fine, take it away and it doesn’t. Here’s a cut down illustration – the left hand image has a border and fades to transparent, the right hand image has no border and fades to black then transparent.

Having checked a couple of other sites I’ve built, if the image is wrapped in div, all seems to be OK as well, it’s just an issue with images.

This is only an issue in Firefox 10 as far as I can see. FF9 is fine.

I’ve logged this as a bug with jQuery. If you’ve come across the same issue please leave a comment below, or, if you’ve got a spare second and Firefox 10, have a look at the test page and let me know if you see the same thing. Apparently it’s OK on Mac/OS X. I’m on W7 Pro 64-bit.

Update 10 February Thanks to Oblik for the updates below – this is reported as a Firefox bug as well. It also seems that applying background-color to the image also solves the problem. I’ve further noted that this does not affect a different machine running W7 Home with FF10 – the links suggest it may be related to the graphics card.

Update 18 February This looks like it’s mostly likely to get resolved by a Firefox update to me. I’ve noticed the problem today on another couple of sites. It’s now being logged under this ticket.

Update 24 April Still not fixed in FF11…I’ve noticed the problem a lot on a wide variety of sites. Looks like it might be scheduled for a fix in FF13.

Update 20 June Fixed in FF13!!

Preventing doubleclick firing two clicks with jQuery Last updated:14 November 2010

A user of my wife’s workshop page reported that they couldn’t click to expand a div – when they did it opened and then immediately closed again. Experimentation showed that in fact they were double clicking rather than single clicking – registering two click events in fact. My original code was:

$('h3 span a').click(function() {
    $(this).parent().parent().siblings(".event_hidden").slideToggle();
    return false;
});

When I started thinking about it, I couldn’t immediately see how to separate the two events – after all, a doubleclick is just two clicks, isn’t it? Fortunately jQuery provides an easy solution by using the event properties:

$('h3 span a').click(function(event) {
    if(event.detail==1){//activate on first click only to avoid hiding again on double clicks
        $(this).parent().parent().siblings(".event_hidden").slideToggle();
    }
    return false;
});

Edit 14 November 2010 Actually, problem not quite solved, since I found that IE8 and under (I don’t know about IE9) don’t recognise event.detail, and hence the code above prevents the slideToggle working entirely. Fortunately, neither does the doubleclick problem seem to affect these browsers, and hence I amended my code to:

$('h3 span a').click(function(event) {
    if(!event.detail || event.detail==1){//activate on first click only to avoid hiding again on double clicks
        $(this).parent().parent().siblings(".event_hidden").slideToggle();
    }
    return false;
});

jQuery fadeIn and fadeOut problems with IE8 Last updated:29 October 2010

I’ve just written a jQuery plugin to allow a user to simply configure some dropdown menu animations. It tests fine in IE6 and IE7, but the fadeIn/fadeOut didn’t work in IE8. Unbelievable. It astonishes me the things that fail in IE8 but were fine in IE7.

The specific instance here was fading a drop down ul element, which was positioned absolutely with respect to an li element with position:relative. You know, standard stuff. Turns out IE8, position:relative and fade don’t play nicely. The solution in the end turned out to be applying:

filter:inherit

to the li element. Applying this to the ul has no effect. This doesn’t seem to cause any issues with IE6 or IE7, fortunately.

I also found that in IE8 as the fadeIn completes, the faded text appears to jump a pixel to the left. This isn’t tragic, but it’s a bit irritating. I found that this only happened if I used the fadeIn method. If I fade in by using .animate() to change the opacity, then the jump goes away.

You can see the final result here if you’re interested.