usable in any place a human can be used

20091020

deleting a record

I've been working on a side project in my spare time, what little there is, and its come time to work on the UI, which is one of my favorite things to do, but one of the hardest things to get right.

Trying to decide amongst the various ways to delete something, trying to create a user experience that will be pleasant and not knock you out of your workflow. Here are the various methods I've been mulling over with lists of pro's and con's, and because I'm super nice, some javascript code that implements them.

All of the methods I'm going to go over rely on some backend handler that will be communicated with asynchronously to do the heavy lifting of performing the actual delete. The code could be simplified (in some cases greatly) by using jQuery or some other javascript framework, but in the interest of not being dependent on any particular framework, I wrote the code as straight javascript.

All the code depends on two helper methods

 
/**
 * Finds the nearest ancestor of an element with a given tag name
 * @param elem [HTML Element] element to find the ancestor for
 * @param tag [String] tag name to look up
 * @return elem [HTML Element | null] null if can't be found
 */
function get_parent(elem, tag) {
  if(elem == null || elem.tagName.toUpperCase() == tag.toUpperCase()) {
    return elem;
  }
  return get_parent(elem.parentNode, tag);
}
 
/**
 * Removes a row when given any node within the row or the row itself
 * @param row [HTML Element] row node or any child node of row node
 */
function remove_row(row) {
  row = get_parent(row, "tr");
  var parent = row.parentNode;
  parent.removeChild(row);
}

Method 1: Simple delete

Name Age Delete
Amy 24 delete
Matt 23 delete
Zeke 32 delete
  function simple_delete(lnk) {
    remove_row(lnk);
  }
Pros Cons
Extremely Simple Easy to accidentally delete something
  Very little feedback

Method 2: Confirm Delete

Name Age Delete
Amy 24 delete
Matt 23 delete
Zeke 32 delete
  function confirm_delete(lnk) {
    if(confirm("Really Delete?")) {
      remove_row(lnk);
    }
  }
Pros Cons
No accidental deletes Breaks flow
Easy cross browser implementation Annoying, focus steal, mousing

Method 3: Double-click Delete

Name Age Delete
Amy 24 delete
Matt 23 delete
Zeke 32 delete
  var dblclk_delete_state = null;
  function dblclk_delete(lnk, which) {
    //Hide everything first
    for(var i = 1; i <= 3; ++i) {
      var msg = document.getElementById('dblclk_confirm_' + i);
      if(msg) {
        msg.style.display = 'none';
      }
    }
  
    if(dblclk_delete_state == which) {
      remove_row(lnk);
      //No need to clear state, but it can't hurt
      dblclk_delete_state = null;
    } else {
      dblclk_delete_state = which;
      document.getElementById('dblclk_confirm_' + which).style.display = 'block';
    }
  }
Pros Cons
Easy deletes Some people double-click out of habit
Confirm before delete Double clicks avoid confirmation

Method 4: Binary Delete

Name Age Delete
Amy 24 delete
Matt 23 delete
Zeke 32 delete
//Yes link simply calls remove_row(this)

//This function is called by the delete link and the No link
function binary_message(which) {
  var msg = document.getElementById('binary_confirm_' + which);
  if(msg) {
    if(msg.style.display == 'none') {
      msg.style.display = 'block';
    } else {
      msg.style.display = 'none'; 
    }
  }
}
Pros Cons
No accidental deletes Breaks flow (less)
Can be prettied up with qTip Clicking delete after message appears should perform what action?

Method 5: Binary Modified

Name Age Delete
Amy 24 delete
Matt 23 delete
Zeke 32 delete
  //Yes simply calls remove_row(this); like in the non-modified version

  //delete link calls this function
  function modified_open(which) {
    modified_state(which, 'block');
  }
 
  //No link calls this function
  function modified_close(which) {
    modified_state(which, 'none');
  }

  //Helper function 
  function modified_state(which, disp) {
    document.getElementById('modified_confirm_' + which).style.display = disp;
  }
Pros Cons
No accidental deletes Breaks flow (less)
Also can be made pretty  
Better (?) delete link behavior  

So those are the various methods I'm mulling over now, also whether a delete should remove the row or simply gray it out, should you be able to undo a delete, all kinds of questions. These have serious consequences for my application and are quite outside of the scope of this already way too long post. I think I like Method 4 or 5 the best, can't decide which. Which do you like, is there something that I missed?

Code is hacked together example code, real code will be nicer, possibly even a jQuery plugin

2 comments:

  1. I'm a big fan of "click one and destroy".

    I'm not a fan of undoing deletes. Undoing deletes gets very tricky in the amount of overhead that you're adding to the application.

    Of course, at the end of the day it really depends on the target audience. Who is your audience? Are they experienced users? Developers? My grandma? Each of these target audiences has very different expectations of how a computer will function and very different tolerance levels for their own errors.

    ReplyDelete
  2. The target audience would be business users of all shapes and types. Undoing deletes gets very complicated very quickly, especially if you are cascade deleting children and children's children.

    Click one and destroy is definitely the easiest, but I know someone will blow away some important record and I'll be on the end of the blame stick.

    ReplyDelete