Animated AJAX Record Deletion Using MooTools

By  on  

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!

Recent Features

  • By
    From Webcam to Animated GIF: the Secret Behind chat.meatspac.es!

    My team mate Edna Piranha is not only an awesome hacker; she's also a fantastic philosopher! Communication and online interactions is a subject that has kept her mind busy for a long time, and it has also resulted in a bunch of interesting experimental projects...

  • By
    Facebook Open Graph META Tags

    It's no secret that Facebook has become a major traffic driver for all types of websites.  Nowadays even large corporations steer consumers toward their Facebook pages instead of the corporate websites directly.  And of course there are Facebook "Like" and "Recommend" widgets on every website.  One...

Incredible Demos

  • By
    Creating Spacers with Flexbox

    I was one of the biggest fans of flexbox before it hit but, due to being shuffled around at Mozilla, I never had the chance to use it in any practice project; thus, flexbox still seems like a bit of a mystery to me.  This greatly...

  • By
    MooTools Equal Heights Plugin:  Equalizer

    Keeping equal heights between elements within the same container can be hugely important for the sake of a pretty page. Unfortunately sometimes keeping columns the same height can't be done with CSS -- you need a little help from your JavaScript friends. Well...now you're...

Discussion

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

    This is good JS. :)

  2. Very nice indeed! Great work.

  3. 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?

  4. 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

  5. Monkeytail

    Nice!

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

  6. cssProdigy

    Another great post.

  7. 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.

  8. @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.

  9. Great, thanks – good to know.

  10. Salih Gedik

    Very Nice. Thank you. I like mootools

  11. 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
  12. Good combination between Class Request and Class Fx.

    Thanks for sharing.

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

  14. 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.

  15. 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

  16. 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’.

  17. 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.

  18. Truthseeker

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

  19. This sample isn’t OK. Instead

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

    you must write

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

    By the way, excellent article…

  20. Michael

    Could someone please show me a working example using CodeIgniter?

  21. bsn

    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.

  22. 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…

  23. Stephen

    Hi,

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

    Thanks!

  24. Peter

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

  25. Kyle

    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?

  26. pete

    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 …??

  27. Andy94

    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.

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

  29. Andy94

    @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.

    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);
    }");
    
  30. Andy94

    @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.

  31. Andy94

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

  32. 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?

  33. 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..

  34. 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’])) { }

  35. 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.

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

  37. Alexander

    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

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

  39. Alexander

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

    Regards,
    Alex

  40. Martin

    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

  41. Ronald

    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,

  42. Paul

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

  43. Paul

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

  44. Hi everyone,

    I have tried to get this working again.

    Everytime I use $link somewhere it breaks and I don’t know what this variable is meant to be doing, it is never declared anywhere?

    Has anyone stripped out any unnecessary code and got it to work?

  45. Try as I might, I can’t get the script to work with a single delete button at the bottom of the list rather than the delete link in each row. The idea is to select one or more rows and press one button to delete the rows.
    Any ideas? anyone do this yet?

  46. Hi David
    This is really good tutorial,but I got the same problem with some people. The animation working fine, but the deleting not working. Deletion only working when disable the animation.
    Please advise that!
    thanks

  47. Hey david,you know ? I Fucking your mother.
    Mad !! this code is not working.not deleting.NO NO NOOOOOOOOO !!!!! fuck fuck fuck you david..

    Plase fuck of david ! david amına koyim david !!

    • Alex

      Why do you hate him so much?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!