Animated Ajax Record Deletion Using MooTools

Written by David Walsh on Thursday, January 29, 2009


This article may feature code that is no longer best practice in MooTools.
Click here to learn what has changed to make your code framework-compatible.

I’m a huge fan of WordPress’ method of individual article deletion. You click the delete link, the menu item animates red, and the item disappears. Here’s how to achieve that functionality with MooTools javascript.

The PHP – Content & Header

The following snippet goes at the top of the page:

if(isset($_GET['delete']))
{
	$query = 'DELETE FROM my_table WHERE item_id = '.(int)$_GET['delete'];
	$result = mysql_query($query,$link);
}

The following is used to display the records:

$query = 'SELECT * FROM my_table ORDER BY title ASC';
$result = mysql_query($query,$link);
while($row = mysql_fetch_assoc($result))
{
	echo '<div class="record" id="record-',$row['item_id'],'">
				<a href="?delete=',$row['item_id'],'" class="delete">Delete</a>
				<strong>',$row['title'],'</strong>
			</div>';
}

The MooTools Javascript

window.addEvent('domready',function() {
	$$('a.delete').each(function(el) {
		el.addEvent('click',function(e) {
			e.stop();
			var parent = el.getParent('div');
			var request = new Request({
				url: 'mootools-record-delete.php',
				link: 'chain',
				method: 'get',
				data: {
					'delete': parent.get('id').replace('record-',''),
					ajax: 1
				},
				onRequest: function() {
					new Fx.Tween(parent,{
						duration:300
					}).start('background-color', '#fb6c6c');
				},
				onSuccess: function() {
					new Fx.Slide(parent,{
						duration:300,
						onComplete: function() {
							parent.dispose();
						}
					}).slideOut();
				}
			}).send();
		});
	});
});

For every link, we add a click event that triggers the Ajax request. During the request, we tween the containing element to a red background. When the Ajax request returns a “success” response, we slide the element off of the screen.

How would you use this? Share!


Epic Discussion

Commenter Avatar January 29 / #

I first saw this type of effect on Wordpress and immediately fell in love with it.

This is good JS. :)

Commenter Avatar January 29 / #

Very nice indeed! Great work.

Commenter Avatar January 29 / #

I haven’t done any PHP for a long time, but what will happen you I call:
?delete=1 or 1=1

Does mysql_query protect from sql injection?

Commenter Avatar January 29 / #

Hi David,
i’d like to have your answer to a question I posted on your article about binding… could you help me?
Nunzio Fiore

Commenter Avatar January 29 / #
Monkeytail says:

Nice!

Where in the MooTools docs can I find more about Fx.Slide ‘onComplete’ option ?

Commenter Avatar January 29 / #
cssProdigy says:

Another great post.

Commenter Avatar January 29 / #

This sounds great, but have you checked what kind of load it puts on the server? I know it sounds crazy, but I checked my logs recently and my #1 bandwidth hog was the admin-ajax.php file in the WordPress admin panel.

David Walsh January 29 / #
david says:

@Pedro: The “(int)” typecasting will make the $_GET['id'] become “0″, in your case.

@Agitationist: This puts very, very little load on the server. Basically none. Sure the WordPress file has issues, but the code above is easy on the server.

Commenter Avatar January 29 / #

Great, thanks – good to know.

Commenter Avatar January 30 / #
Salih Gedik says:

Very Nice. Thank you. I like mootools

Commenter Avatar January 30 / #
emehrkay says:

Whenever I see this, I always ask why it is – Why do you create a new request object with every click instead of creating it outside of the click and reusing it?

var my_req = new Request();

my_req.send(data); //inside of your click event

Commenter Avatar January 30 / #

Good combination between Class Request and Class Fx.

Thanks for sharing.

David Walsh January 31 / #
david says:

@emehrkay: Great catch — you could just use one Request.

Commenter Avatar February 02 / #

I always liked that effect in Wordpress, too. It gives the user proper feedback after clicking delete, making the experience more enjoyable and user-friendly.

Commenter Avatar February 02 / #

Hi all, I use this post for asking all of you a question about binding, I posted on another example of our David Walsh . The questio is here http://davidwalsh.name/simple-mootools-event-class-binding somebody can answer to this doubt?

thank you to all and a special to David
Nunzio

Commenter Avatar February 02 / #

To help protect from SQL injection, I always check that the referring page is the one you expect it to be, i.e.
if (isset($_GET["delete"]) && is_numeric($_GET["delete"]) && (array_shift(explode(“?”, basename($_SERVER["HTTP_REFERER"]))) == “show_records_page.php”)) { … }

This checks the referring page’s filename, removes any query string and compares against whatever filename you put in for your page that calls the Request. I’d always try to catch a dodgy query rather than run it with something that defaults back to something ’safe’.

Commenter Avatar February 20 / #

Would love a rewrite for jQuery. It is by far the superior liberary in my view and I already use it on the projects in which I can see use for this.

Commenter Avatar March 03 / #
Truthseeker says:

Amazing thing, I tried something like this myself but it wasn’t working, now I see what I did wrong. Thank you.

Commenter Avatar March 09 / #

This sample isn’t OK. Instead
$result = mysql_query($result,$link);

you must write

$result = mysql_query($query,$link);

By the way, excellent article…

Commenter Avatar March 09 / #
Michael says:

Could someone please show me a working example using CodeIgniter?

Commenter Avatar March 11 / #
bsn says:

I would definitely suggest returning a significant response to the XMLHTTPRequest – you seem to assume that the row has been deleted and visually confirm this to the user, regardless of what has happened server-side. Possible scenarios where the DELETE query could fail are:

MySQL refused the connection for whatever reason
The row does not exist (deleted by another user?)
User does not have permission to delete certain rows

Something always goes wrong, normally when the client’s spouse is clicking aroung in IE7.

Why not return ‘0′ on success, and an integer error code (e.g. ‘1 : database error’, ‘2 : row not exist’) for any other eventuality? Then the ‘view’ can act accordingly.

I would also suggest adding handlers to tackle a failed or timed-out XMLHTTPRequest, and then provide appropriate feedback to the user. There’s nothing worse than an ajax “waiting” icon that just doesn’t go away, even when the request is completed. (OK, there are worse things.)

Just my quarter of a byte.

Commenter Avatar March 18 / #
Shanky says:

hi ,

i just want to ask that why it does not works on false elements like i m creating li elements through js and using ur script to delete li elements but it does not work but when i hardcoded the li elements on page then it works.. . please any one sortout this problem….

here is my code..

window.addEvent(‘domready’, function() {

//This is the function that will run every time a new item is added or the
//list is sorted.
var showNewOrder = function() {
//This function means we get serialize() to tell us the text of each
//element, instead of its ID, which is the default return.
var serializeFunction = function(el) { return el.get(‘text’); };
//We pass our custom function to serialize();
var orderTxt = sort.serialize(serializeFunction);
//And then we add that text to our page so everyone can see it.
$(‘data’).set(‘text’, orderTxt.join(‘ ‘));
};

function valueExist(myval){
var x=0;
if(myKeywords.length > 0){
for (x=0;x<=myKeywords.length;x++){
if(myKeywords[x]==myval){return true;}
}// and the loop goes on until…
}
}

// begin your js code for deletion

$$(‘a.delete’).each(function(el) {
el.addEvent(‘click’,function(e) {
e.stop();
var request = new Request({
url: ‘mytest.php’,
link: ‘chain’,
method: ‘get’,
data: {
‘delete’: el.getParent(‘li’).get(‘id’).replace(‘record-’,”),
ajax: 1
},
onRequest: function() {
new Fx.Tween(el.getParent(‘li’),{
duration:300
}).start(‘background-color’, ‘#fb6c6c’);
},
onSuccess: function() {
new Fx.Slide(el.getParent(‘li’),{
duration:300,
onComplete: function() {
el.getParent(‘li’).dispose();
}
}).slideOut();
}
}).send();
});
});

// end your js code for deletion

//This code initalizes the sortable list.
var sort = new Sortables(‘.todo’, {
handle: ‘.drag-handle’,
//This will constrain the list items to the list.
constrain: true,
//We’ll get to see a nice cloned element when we drag.
clone: true,
//This function will happen when the user ‘drops’ an item in a new place.
onComplete: showNewOrder
});

//This is the code that makes the text input add list items to the <ul>,
//which we then make sortable.
var i = 1;
var myKeywords = new Array();
$(‘addTask’).addEvent(’submit’, function(e) {
e.stop();
//Get the value of the text input.
var val = $(‘newTask’).get(‘value’);
//The code here will execute if the input is empty.
if (!val) {
$(‘newTask’).highlight(‘#f00′).focus();
return; //Return will skip the rest of the code in the function.
}
else{
if(valueExist(val)==true){
alert(“You have already added this keyword.”);
$(‘newTask’).highlight(‘#f00′).focus();
return;
//Return will skip the rest of the code in the function.
}else{
myKeywords.push(val);
}
}
//Create a new <li> to hold all our content.
var li = new Element(‘li’, {id: ‘record-’+i});
//the draggable element.
var div_clear = new Element(‘div’, {‘class’:'clear’});
div_clear.inject(li);
//This handle element will serve as the point where the user ‘picks up’
//the draggable element.
//var handle = new Element(‘div’, {id:’handle-’+i, ‘class’:'drag-handle’});
//handle.inject(li, ‘top’);

var div_img = new Element(‘div’, {’style’:'float:right;padding:0px;’});
div_img.inject(div_clear);

var div_keyword = new Element(‘div’, {’style’:'float:left;margin-top:2px;’, text:val });
div_keyword.inject(div_clear);

var img_anchor = new Element(‘a’, {‘href’:'?delete=’+i,’title’:'Delete’,'class’:'delete’});
img_anchor.inject(div_img);

var img = new Element(‘img’, {’src’:'images/delete.png’,'alt’:'Delete’,'width’:'24′,’height’:'24′,’border’:'0′});
img.inject(img_anchor);

//var input = new Element(‘input’, {‘type’:'checkbox’,id:’chk’+i,name:’chk’+i});
//input.inject(div_chkbox);

//Set the value of the form to ”, since we’ve added its value to the <li>.
$(‘newTask’).set(‘value’, ”);
//Add the <li> to our list.
$(‘todo’).adopt(li);
//Do a fancy effect on the <li>.
li.highlight();
//We have to add the list item to our Sortable object so it’s sortable.
sort.addItems(li);
//We put the new order inside of the data div.
//showNewOrder();
i++;
});

});

Thanks…

Commenter Avatar March 22 / #
Stephen says:

Hi,

it looks ugly if it is a table and a complete row () that has to be disposed.
Any ideas?

Thanks!

Commenter Avatar March 25 / #
Peter says:

hi,
How to modify this script to adding the entry to base?

Commenter Avatar March 30 / #
Kyle says:

First off, great article, I really like the instant feedback and animation when deleting. I do have one (hopefully) simple question:

Everything works fine, except the fact that the record is not actually deleting from my database. The animation works beautiful, but on a page refresh, the deleted entries re-appear. And the interesting thing is, as soon as I remove the MooTools script, from the HEAD, the entries are deleted with no problem. Just no animation :-(

Any ideas?

Commenter Avatar April 01 / #
pete says:

Yep – thanks for all the work you spent in your excelent articles!

Got the same problem as @Kyle: changed the url in the javascript but the script only deletes from my database if i run it without the
mootools code.

Any idea …??

Commenter Avatar April 13 / #
Andy94 says:

Hi David, my best regards for your website. I’ve never seen a site like this one, it’s so good.
I have the same problem as pete and Kyle: when I click on an element, the animation works fine but there is no deletion. If I reach the page by putting the querystring on the addressbar, all works fine.
Can you help us?
I really appreciate that.

P.S. Sorry for my English, I’m an italian boy, and I am just 14.

David Walsh April 13 / #

@Andy94: Have an example I can look at online?

Commenter Avatar April 14 / #
Andy94 says:

@David: of course I have one. You can visit this website THIS. This is just a test which extracts data from a db, it is located in localhost. As you can see, the animation works fine, but if you refresh the page the element that you deleted reappear. The PHP code, is located at the end of this comment.
Thank you so much David.

PHP CODE:

mysql_connect(my connection parameters);
mysql_select_db(“theuploader”);

$query = ‘SELECT * FROM theuploader_log ORDER BY logid ASC’;
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result))
{
echo’
<div class=”record” id=”record-’,$row['logid'],’”>
<a href=”index.php?delete=’.$row['logid'].’” class=”delete”>Delete</a>
<strong>’,$row['object'],’</strong>
</div>
‘;
}

AND THIS IS THE ONE FOR THE $_GET

if(isset($_GET['delete'])
{
mysql_connect(my connection parameters);
mysql_select_db(“theuploader”);
$query = ‘DELETE FROM theuploader_log WHERE logid=’.$_GET['delete'].”;
$result = mysql_query($query);
}”);

Commenter Avatar April 14 / #
Andy94 says:

@David: of course I have one. You can visit this website THIS. This is just a test which extracts data from a db, it is located in localhost. As tou can see, the animation works fine, but if you refresh the page the element that you deleted reappear. The PHP code, is located at the end of this comment.
Thank you so much David.

PHP CODE:

mysql_connect(my connection parameters);
mysql_select_db(“theuploader”);

$query = ‘SELECT * FROM theuploader_log ORDER BY logid ASC’;
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result))
{
echo’
<div class=”record” id=”record-’,$row['logid'],’”>
<a href=”index.php?delete=’.$row['logid'].’” class=”delete”>Delete</a>
<strong>’,$row['object'],’</strong>
</div>
‘;
}

AND THIS IS THE ONE FOR THE $_GET

if(isset($_GET['delete'])
{
mysql_connect(my connection parameters);
mysql_select_db(“theuploader”);
$query = ‘DELETE FROM theuploader_log WHERE logid=’.$_GET['delete'].”;
$result = mysql_query($query);
}”);

Commenter Avatar April 14 / #
Andy94 says:

@David: of course I have one. You can visit this website THIS. This is just a test which extracts data from a db, it is located in localhost. As tou can see, the animation works fine, but if you refresh the page the element that you deleted reappear. The PHP code, is located at the end of this comment.
Thank you so much David.

I will post the PHP code in another comment, because your system doesn’t allow me to send comments with PHP code inside.

Commenter Avatar April 14 / #
Andy94 says:

@David: Here is my PHP code. Sorry but I had to htmlentities it, so the brs cannot be displayed.
HERE IS

Commenter Avatar April 29 / #
ATLmetal says:

I have been trying to get this working on my site with no luck. Not sure what the problems is. I have implemented everything you have listed above and the closest I can get is when you click the delete link the div fades to red but doesn’t slide away even though the content in the database IS being deleted. What am I doing wrong?

Commenter Avatar May 13 / #
azwan says:

This is cool. Mootools really make things cool. I’ve implemented the this kind of slide/fade style on few of my projects but never tried to do it from scratch..

Commenter Avatar May 22 / #

Hrm… awesome tutorial/demo sooooo useful.

For some reason I can’t get it to send the “delete” id variable back to the page or to any other. When I look at the requests being made even on your demo it just requests the file

“http://davidwalsh.name/dw-content/mootools-record-delete.php”

There aren’t any variables in the query string although there are in the link you click on. Is this correct? If so I don’t understand how the PHP can check to see if the variable isset() for the delete to work.

if(isset($_GET['delete'])) { }

Commenter Avatar June 05 / #

I made two mods to this:

http://zero.dengel.me/confirm-delete.php (Click once to select, again to delete or let timeout).
http://zero.dengel.me/queue-list.php (Made into a Sortable list with deletion confirmation).

d.
ps. Having a hard time getting around the Spam filter setup for these comments… ad to remove the links to get posted.

Commenter Avatar June 22 / #

Very nice and useful things, thnx to share it with us…

Commenter Avatar July 09 / #
Alexander says:

Implemented and now i have it working with any problem, but i would like to know how to disable if the user wants to click it once again and the first request is not yet finished.

I intentinally use a php sleep() function and if i click several time it will send several request. So i need to prevent that.

Any ideas?

Regards,
Alex

David Walsh July 09 / #

@Alexander: Hide the “delete” link when the item’s been clicked.

Commenter Avatar July 09 / #
Alexander says:

@David
Thanks David, did not think about that! hehe..i was more focused on “disabling” the click intead of removeing the delete.

Regards,
Alex

Commenter Avatar September 25 / #
Martin says:

Hi,

I have the code inserted, only difference is I am using an image as the delete link rather than plain text and on inspection everything appears fine but nothing happens when I click the link.

I don’t have the ‘$link’ variable in the queries because I don’t see it declared anywhere and don’t know what it does. If i include it I get errors anyway.

Just to see if anyone could explain it’s purpose.

Cheers

Commenter Avatar October 25 / #
Ronald says:

Hi,

This may sound strange, but how do I get this to work with Mootools 1.1.
This is due to the fact that the CMS I am programming for, works with the old mootools version.

Cheers,

Commenter Avatar February 16 / #
Paul says:

@Michael: Did you manage to get it working with CodeIgniter?

Commenter Avatar February 16 / #
Paul says:

@Michael: Did you manage to get it working with CodeIgniter?

Be Heard!

I want to hear what you have to say! Share your comments and questions below.

Name*:
Email*:
Website:  


© David Walsh 2007-2010. Contact David Walsh. Powered by the remarkable MooTools javascript framework.