Table Edit enhancement for Javascript

Summary

Support dynamic list management, can put an 'Add Row' or 'Delete Row' button on a table to manage lists of fields. The buttons must be created in the page render.

Example

id value action
252
277

On noscript, the 'delete' button will submit the form with an appropriate command, so the action can be handled normally. Noscript 'new' should be handled in the traditional manner, by adding an empty row in the code.

Code


/////////////////////////////////////////////////////////////////

/**
 * Add a new row to a table by duplicating the last row.
 * This allows users to keep adding new info to a table that includes
 * editable text fields as the last row.
 * Should be triggered from a button of your own that calls this function.
 * Table must have an 'id' set
 */
function add_row_to_edit_table(event_src) {
  if (document.getElementById) {

    var parentN = event_src.parentNode;
    while(parentN && (parentN != parentN.parentNode) && (parentN.nodeName.toLowerCase() != "table") ){
      parentN = parentN.parentNode;
    }
    var t;
    if(parentN.nodeName.toLowerCase() == "table"){
      t = parentN;
    }

    if(t){
      // find last row,
      var rows = t.getElementsByTagName("tr");
      var lastrow = rows[rows.length-1];
      var copyrow = lastrow.cloneNode(true);

      // Cannot allow duplicate IDs
      // By changing the old row, hopefully scripts will still work on the new row
      _set_unique_ids(lastrow,rows.length)

      lastrow.parentNode.appendChild(copyrow);

      // need to do this to add the event to the button again
      if(typeof(init_combo_boxes)=='function'){init_combo_boxes()};
    } else {
      alert("container table not found, cannot add a row to it");
    }
    return false;
  }
  return true
}

/**
 * Recursively ensure that any item in this tree with an id gets suffix added to it
 * Continues xhtml compliance and means that getElementByID doesn't break.
 */
function _set_unique_ids(node,count) {
  var child = node.firstChild;
  if(!child) return;
  while (child){
    if(child.id){
      //incidentally, delete the 'new' button
      if(child.id=="new_button"){
        child.parentNode.removeChild(child);
      } else {
        child.id = child.id + count ;
      }
    }
    // To support Drupal 4.7, I also have to update the field names?
    if(child.name){
      child.name = child.name.replace(/\[\]/,"[0]");
      if(found = child.name.match(/\[(\d+)\]/) ){
        child.name = child.name.replace( /\[(\d+)\]/, "["+ count +"]" );
        child.setAttribute('name',child.name);
      }
    }
    _set_unique_ids(child,count);
    child = child.nextSibling;
  }
}


/**
 * Remove the target row from a table.
 * Called from a button with the parameter 'this', this func
 * will find the nearest tr and wipe it.
 */
function delete_row_from_edit_table(event_src, row_id) {
  if (document.getElementById) {
    var parentN = event_src['parentNode']; // 'parent' is a reserved word in IE!

    while(parentN && (parentN != parentN.parentNode) && (parentN.nodeName.toLowerCase() != "tr") ){
      parentN = parentN.parentNode;
      var grandparent = parentN.parentNode;
    }
    if(parentN.nodeName.toLowerCase() == "tr"){
      // and remove from the DOM altogether // # not (actually hide) it
      var s = parentN.getAttribute('style');
      if(s){ s.display = "none"; }
      parentN.setAttribute('style','display:none;');
      // HIDE it don't delete as we need to know it's being deleted.
      // Catcher script must act on the 'delete' parameter
      // parentN.parentNode.removeChild(parentN);
    } else {
      alert("No row found above here - was this action launched from inside a table at all?");
    }
    if(row_id){
      // leave behind a hidden element that sends a message to 'delete' the item
      var newField = document.createElement('input');
      newField.setAttribute('type','hidden');
      newField.name = row_id; newField.setAttribute('name', row_id);
      newField.setAttribute('value', 'delete');
      grandparent.appendChild(newField);
    }
    return false;
  }
  return true;
}

/////////////////////////////////////////////////////////////////
DOM by
Self-documenting code idea from Tantek Çelik