Table actions plug-in v0.8 by frequency-decoder.com


/ Published in: JavaScript
Save to your folder(s)

- a javascript for alternate row/column table striping
- supports row hover, column hover and cell hover effects and click events.
demo here:
http://www.frequency-decoder.com/demo/table-actions/


Copy this code and paste it in your HTML
  1. /* Javascript File
  2.  
  3.   Table actions plug-in v0.8 by frequency-decoder.com
  4.  
  5.   Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)
  6.  
  7.   Please credit frequency decoder in any derivative work - thanks
  8.  
  9.   You are free:
  10.  
  11.   * to copy, distribute, display, and perform the work
  12.   * to make derivative works
  13.   * to make commercial use of the work
  14.  
  15.   Under the following conditions:
  16.  
  17.   by Attribution.
  18.   --------------
  19.   You must attribute the work in the manner specified by the author or licensor.
  20.  
  21.   sa
  22.   --
  23.   Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
  24.  
  25.   * For any reuse or distribution, you must make clear to others the license terms of this work.
  26.   * Any of these conditions can be waived if you get permission from the copyright holder.
  27.  
  28.   NOTE: Parts of this script is based on a script originally developped by the veritable Richard Cornford (http://www.litotes.demon.co.uk/example_scripts/tableHighlighter.html)
  29.  
  30. -----------*/
  31.  
  32. /*
  33. Zebra striping
  34.  
  35. To zebra stripe every alternate table row, just give the table a className of the form rowstylealt-XXX where XXX represents the className to give each alternate row, for example; giving the table a className of rowstylealt-alt will give each alternate row the class alt.
  36. i.e...
  37.  
  38. <table class="rowstylealt-bluetr".....
  39.  
  40. with a style:
  41. .bluetr {background:#e8f7ff;}
  42.  
  43. Row Highlighting
  44.  
  45. To highlight the current row i.e. the row the mouse is currently hovering over; just give the table a className of the format rowstylehover-XXX, where XXX represents the class that will be given to the current cell’s parent TR node.
  46.  
  47. Column Highlighting
  48.  
  49. To highlight the current column, just give the table the className colstylehover-XXX, where XXX represents the class that will be given to each node within the current column scope.
  50.  
  51. Highlighting the “current” cell
  52.  
  53. To highlight the current cell, just give the table a className of the format cellhover-XXX, where XXX represents the className that will be given to the current cell.
  54.  
  55. Row selection
  56.  
  57. To enable users to click on a row in order to “select” it, just give the table a className of the format rowselect-XXX, where XXX represents the class to give the current cell’s parent TR node after the click event has fired.
  58. Multiple row selection
  59.  
  60. Whenever the Shift key is held down, users can click and drag the mouse pointer in order to select multiple rows simultaneously. The callback function will be called on the selection of each new row and not just when the drag action has stopped.
  61. Disabling text selection in Internet Explorer
  62.  
  63. I’ve currently hijacked Internet Explorer’s proprietary onselectstart and ondrag events in order to stop the selection of text during the drag action. If other applications are also using these events, you may have to tweak the code to stop it from overwriting handlers already set for these events… and yes, I know hijacking events using old-school methods is wrong.
  64. Single row selection only
  65.  
  66. Should you wish that only one table row can be selected at a time, give the table the class rowselectsingle. This, of course, means that holding the Shift key to select multiple rows will not work.
  67. The callback function
  68.  
  69. Enabling users to select table rows is rather dull if nothing can be done with the user’s selection. To this end, the script enables you to define a reference to a JavaScript callback function (or Object method) that is itself passed an Object detailing the table, the cell clicked, the row clicked, the action to be taken (i.e. add or remove the row from the list of currently selected rows) and the list of currently selected table rows i.e:
  70.  
  71.  
  72.  // The following Object is passed to the callback function
  73.  {
  74.   "table": /* The table */,
  75. "cell": /* The cell that was clicked */,
  76. "row": /* The row to be added or removed */
  77. "remove": /* Are we attempting to remove the row (T/F) */
  78. "rows": /* An array of currently selected TR nodes */
  79. }
  80.  
  81.  
  82.  
  83. The callback function should always return a Boolean value that indicates to the script if it should continue with the add or remove action. This enables you to filter out rows from the selection using bespoke criteria.
  84. Stipulating the callback function
  85.  
  86. Should you wish to define a specific callback function for the table, just give the table a className of the format rowselectcallback-XXX, where XXX represents the name of the JavaScript function to call after a row has been selected or deselected by the user.
  87. Stipulating an Object.method as a callback function
  88.  
  89. Should you wish to define an Object method as a callback function, just give the table a className of the format rowselectcallback-XXX, where XXX represents the name of the JavaScript Object method to call after a row has been selected or deselected by the user.
  90.  
  91. In order to enable the parsing of the “.” (dot) Object notation, the XXX className has all “-” characters replaced by the “.” character, for example; giving the table the className rowselectcallback-anObject-aMethod will mean that the script will attempt to use anObject.aMethod() as the callback function.
  92.  
  93. An example of defining an Object method in this way can be viewed within the table actions demo. Just look at the source code to see how it was done.
  94. Programmatically clearing selected rows
  95.  
  96. Should you wish to programmatically remove the user’s selection, just call the method fdTableActions.deselectAllRows, passing in the ID of the table as an argument, for example; calling fdTableActions.deselectAllRows('testTable'); will remove the user selection for the table with an ID of “testTable”.
  97.  
  98. -----------*/
  99.  
  100.  
  101.  
  102.  
  103. var fdTableActions = {
  104. tableCache:{},
  105. init:function() {
  106. var tables = document.getElementsByTagName("table");
  107. var rowAlt, colHover, rowHover, rowSelect, rowSelectCallback, cellHover, rowList, rowArr, colObj, elem, rowLength, workArr, celCount, rowSpan, colSpan, cel, colHead, rowL, colL;
  108. var uniqueID = 0;
  109. var colspan = "colspan";
  110. var rowspan = "rowspan";
  111. /*@cc_on
  112.   /*@if(@_win32)
  113.   colspan = "colSpan";
  114.   rowspan = "rowSpan";
  115.   /*@end
  116.   @*/
  117.  
  118. for(var k = 0, table; table = tables[k]; k++) {
  119. // Grab the className for the alternate rows
  120. rowAlt = table.className.search(/rowstylealt-([\S-]+)/) == -1 ? "" : table.className.match(/rowstylealt-([\S]+)/)[1];
  121. // Highlight columns?
  122. colHover = table.className.search(/colstylehover-([\S-]+)/) == -1 ? "" : table.className.match(/colstylehover-([\S]+)/)[1];
  123. // Grab the className for the rowStyleHover
  124. rowHover = table.className.search(/rowstylehover-([\S-]+)/) == -1 ? "" : table.className.match(/rowstylehover-([\S]+)/)[1];
  125. // Do we select the rows
  126. rowSelect = table.className.search(/rowselect-([\S-]+)/) == -1 ? "" : table.className.match(/rowselect-([\S]+)/)[1];
  127. // Single or multiple row selection possible
  128. rowSelectSingle = table.className.search(/rowselectsingle/) != -1;
  129. // Do we have a callback for this table whenever a row is selected?
  130. rowSelectCallback = table.className.search(/rowselectcallback-([\S-]+)/) == -1 ? "" : table.className.match(/rowselectcallback-([\S]+)/)[1];
  131. // Replace "-" with "." to enable Object.method callbacks
  132. rowSelectCallback = rowSelectCallback.replace("-", ".");
  133. // Current TH or TD
  134. cellHover = table.className.search(/cellhover-([\S-]+)/) == -1 ? "" : table.className.match(/cellhover-([\S]+)/)[1];
  135.  
  136. // Do we even need to continue?
  137. if(!(rowAlt || rowHover || colHover || rowSelect || cellHover)) continue;
  138.  
  139. // Create a table ID if necessary
  140. if(!table.id) table.id = "fdTable-" + uniqueID++;
  141.  
  142. // Cache this tables details
  143. fdTableActions.tableCache[table.id] = { "trCache":[], "rowSelectSingle":rowSelectSingle, "rowSelect":rowSelect, "rowSelectCallback":rowSelectCallback, "lastColCache":[0,0], "rowHover":rowHover, "colHover":colHover };
  144.  
  145. /*@cc_on
  146.   @if (@_jscript_version >= 5.7)
  147.   if(document.compatMode == "BackCompat") fdTableActions.tableCache[table.id].req = true;
  148.   @else
  149.   fdTableActions.tableCache[table.id].req = true;
  150.   @end
  151.   @*/
  152.  
  153. /*@cc_on
  154.   /*@if(@_win32)
  155.   if(fdTableActions.tableCache[table.id].req) {
  156.   fdTableActions.tableCache[table.id].lastRow = null;
  157.   fdTableActions.tableCache[table.id].lastCell = null;
  158.   fdTableActions.tableCache[table.id].cellHover = cellHover;
  159.   };
  160.   /*@end
  161.   @*/
  162. rowArr = [];
  163. rowList = table.getElementsByTagName('tr');
  164.  
  165. for(var i = 0;i < rowList.length;i++){
  166. colObj = [];
  167. elem = rowList[i].firstChild;
  168. do {
  169. if(elem.tagName && elem.tagName.toLowerCase().search(/td|th/) != -1) {
  170. colObj[colObj.length] = elem;
  171. };
  172. elem = elem.nextSibling;
  173. } while(elem);
  174. if(i % 2 == 0 && rowAlt) rowList[i].className = rowList[i].className + " " + rowAlt;
  175. rowArr[rowArr.length] = colObj;
  176. };
  177.  
  178. /*@cc_on
  179.   /*@if (@_win32)
  180.   if(fdTableActions.tableCache[table.id].req) {
  181.   if(!cellHover && !rowSelect && !colHover && !rowHover) continue;
  182.   } else {
  183.   if(!colHover && !rowSelect) continue;
  184.   };
  185.   @else @*/
  186. if(!colHover && !rowSelect) continue;
  187. /*@end
  188.   @*/
  189.  
  190. if(rowArr.length > 0){
  191. /* Attribution: Parts of the following code based on an original script by Richard Cornford (http://www.litotes.demon.co.uk/example_scripts/tableHighlighter.html) */
  192. if(colHover) {
  193. rowLength = rowArr[0].length;
  194. for(var c = 0;c < rowArr[0].length;c++){
  195. if(rowArr[0][c].getAttribute(colspan) > 1){
  196. rowLength = rowLength + (rowArr[0][c].getAttribute(colspan) - 1);
  197. };
  198. };
  199.  
  200. workArr = new Array(rowArr.length);
  201. for(var c = rowArr.length;c--;){
  202. workArr[c] = new Array(rowLength);
  203. };
  204.  
  205. colHead = new Array(rowLength);
  206. for(var c = rowLength;c--;){
  207. colHead[c] = [];
  208. };
  209.  
  210. for(var c = 0;c < workArr.length;c++) {
  211. celCount = 0;
  212. for(var i = 0;i < rowLength;i++) {
  213. if(!workArr[c][i]) {
  214. cel = rowArr[c][celCount];
  215. colSpan = (cel.getAttribute(colspan) && cel.getAttribute(colspan) > 1) ? cel.getAttribute(colspan) : 1;
  216. rowSpan = (cel.getAttribute(rowspan) && cel.getAttribute(rowspan) > 1) ? cel.getAttribute(rowspan) : 1;
  217. cel.className = cel.className + " fdCellProcessed-" + i + "-" + (i + Number(colSpan));
  218. for(var t = 0;((t < colSpan)&&((i+t) < rowLength));t++){
  219. for(var n = 0;((n < rowSpan)&&((c+n) < workArr.length));n++){
  220. workArr[(c+n)][(i+t)] = cel;
  221. };
  222. colHead[(i+t)][colHead[(i+t)].length] = cel;
  223. };
  224. if(++celCount == rowArr[c].length) break;
  225. };
  226. };
  227. };
  228. /* End attribution */
  229. fdTableActions.tableCache[table.id].colCache = colHead;
  230. };
  231. /*@cc_on
  232.   /*@if (@_win32)
  233.   fdTableActions.addEvent(table, "mouseleave", fdTableActions.tableEvent);
  234.   @else @*/
  235. fdTableActions.addEvent(table, "mouseout", fdTableActions.tableEvent);
  236. /*@end
  237.   @*/
  238.  
  239. fdTableActions.addEvent(table, "mouseover", fdTableActions.tableEvent);
  240.  
  241.  
  242. if(rowSelect) {
  243. fdTableActions.addEvent(table, "click", fdTableActions.clickEvent);
  244. };
  245. };
  246. };
  247. },
  248. clickEvent:function(e) {
  249. e = e || window.event;
  250. var tr, cell;
  251. if(e.target) tr = e.target;
  252. else if (e.srcElement) tr = e.srcElement;
  253.  
  254. while(true) {
  255. if(tr && !cell && tr.nodeName && tr.nodeName.search(/^(TD|TH)$/) != -1) { cell = tr; }
  256. else if(tr && tr.nodeName && tr.nodeName.search(/^(TR)$/) != -1) { break; };
  257. try { tr = tr.parentNode; } catch(err) { break; };
  258. };
  259.  
  260. if(!tr || !tr.nodeName || tr.nodeName.search(/^(TR)$/) == -1) { return; };
  261.  
  262. var remove = (tr.className.search(fdTableActions.tableCache[this.id].rowSelect) != -1);
  263. var action = true;
  264.  
  265. if(fdTableActions.tableCache[this.id].rowSelectCallback) {
  266. var func;
  267. if(fdTableActions.tableCache[this.id].rowSelectCallback.search(".") != -1) {
  268. var split = fdTableActions.tableCache[this.id].rowSelectCallback.split(".");
  269. func = window;
  270. for(var i = 0, f; f = split[i]; i++) {
  271. if(f in func) {
  272. func = func[f];
  273. } else { break; };
  274. };
  275. } else if(fdTableActions.tableCache[this.id].rowSelectCallback in window) {
  276. func = window[fdTableActions.tableCache[this.id].rowSelectCallback];
  277. } else if("rowSelectCallback" in window) {
  278. func = window["rowSelectCallback"];
  279. };
  280. if(typeof func == "function") {
  281. action = func({"table":this, "cell":cell, "row":tr, "remove":remove, "rows":fdTableActions.tableCache[this.id].trCache.concat([])});
  282. };
  283. func = null;
  284. };
  285.  
  286. if(action) {
  287. if(remove) {
  288. tr.className = tr.className.replace(fdTableActions.tableCache[this.id].rowSelect, "");
  289. var rowArr = [];
  290. for(var i = 0, elem; elem = fdTableActions.tableCache[this.id].trCache[i]; i++) {
  291. if(elem != tr) rowArr[rowArr.length] = elem;
  292. };
  293. fdTableActions.tableCache[this.id].trCache = rowArr;
  294. } else {
  295. tr.className = tr.className + " " + fdTableActions.tableCache[this.id].rowSelect;
  296. if(fdTableActions.tableCache[this.id].rowSelectSingle && fdTableActions.tableCache[this.id].trCache.length) {
  297. fdTableActions.tableCache[this.id].trCache[0].className = fdTableActions.tableCache[this.id].trCache[0].className.replace(fdTableActions.tableCache[this.id].rowSelect, "");
  298. fdTableActions.tableCache[this.id].trCache = [];
  299. };
  300. fdTableActions.tableCache[this.id].trCache[fdTableActions.tableCache[this.id].trCache.length] = tr;
  301. };
  302. };
  303. },
  304. tableEvent:function(e) {
  305. e = e || window.event;
  306. var p;
  307. if(e.type == "mouseout") {
  308. p = e.toElement || e.relatedTarget;
  309. } else {
  310. p = e.target || e.srcElement;
  311. if (p.nodeType && p.nodeType == 3) p = p.parentNode;
  312. };
  313.  
  314. while(true) {
  315. if(p && p.nodeName && p.nodeName.search(/^(TD|TH)$/) != -1) { break; };
  316. try { p = p.parentNode; } catch(err) { break; };
  317. };
  318.  
  319. // Get the old column range
  320. var colRangeOld = fdTableActions.tableCache[this.id].lastColCache;
  321. var r = [];
  322. var n = [];
  323.  
  324. if(e.type == "mouseover" && p && p.nodeName.search(/^(TD|TH)$/) != -1) {
  325. // Do current cell & row for bloody IE
  326. /*@cc_on
  327.   /*@if(@_win32)
  328.   if(fdTableActions.tableCache[this.id].req) {
  329.   if(fdTableActions.tableCache[this.id].rowHover) {
  330.   var tr = p.parentNode;
  331.   if(fdTableActions.tableCache[this.id].lastRow != tr) {
  332.   if(fdTableActions.tableCache[this.id].lastRow) {
  333.   fdTableActions.tableCache[this.id].lastRow.className = fdTableActions.tableCache[this.id].lastRow.className.replace(new RegExp(fdTableActions.tableCache[this.id].rowHover, "g"),"");
  334.   fdTableActions.tableCache[this.id].lastRow = false;
  335.   };
  336.   tr.className = tr.className+ " " + fdTableActions.tableCache[this.id].rowHover;
  337.   fdTableActions.tableCache[this.id].lastRow = tr;
  338.   };
  339.   };
  340.   if(fdTableActions.tableCache[this.id].cellHover) {
  341.   if(fdTableActions.tableCache[this.id].lastCell != p) {
  342.   if(fdTableActions.tableCache[this.id].lastCell) {
  343.   fdTableActions.tableCache[this.id].lastCell.className = fdTableActions.tableCache[this.id].lastCell.className.replace(new RegExp(fdTableActions.tableCache[this.id].cellHover, "g"),"");
  344.   fdTableActions.tableCache[this.id].lastCell = false;
  345.   };
  346.   p.className = p.className + " " + fdTableActions.tableCache[this.id].cellHover;
  347.   fdTableActions.tableCache[this.id].lastCell = p;
  348.   };
  349.   };
  350.   };
  351.   /*@end
  352.   @*/
  353. if(!fdTableActions.tableCache[this.id].colHover || p.className.search("fdCellProcessed-") == -1) return;
  354. var m = p.className.match(/fdCellProcessed-([\d]+)-([\d]+)/);
  355. m[1] = Number(m[1]);
  356. m[2] = Number(m[2]);
  357. if(fdTableActions.tableCache[this.id].lastColCache[0] == m[1] && fdTableActions.tableCache[this.id].lastColCache[1] == m[2]) return;
  358. for(var i = colRangeOld[0]; i < colRangeOld[1]; i++) {
  359. if(i < m[1] || i >= m[2]) {
  360. r[r.length] = i;
  361. };
  362. };
  363. fdTableActions.tableCache[this.id].lastColCache = [m[1], m[2]];
  364. n = [m[1], m[2]];
  365. } else {
  366. /*@cc_on
  367.   /*@if(@_win32)
  368.   if(fdTableActions.tableCache[this.id].req) {
  369.   if(fdTableActions.tableCache[this.id].lastRow) {
  370.   fdTableActions.tableCache[this.id].lastRow.className = fdTableActions.tableCache[this.id].lastRow.className.replace(fdTableActions.tableCache[this.id].rowHover,"");
  371.   fdTableActions.tableCache[this.id].lastRow = null;
  372.   };
  373.   if(fdTableActions.tableCache[this.id].lastCell) {
  374.   fdTableActions.tableCache[this.id].lastCell.className = fdTableActions.tableCache[this.id].lastCell.className.replace(fdTableActions.tableCache[this.id].cellHover,"");
  375.   fdTableActions.tableCache[this.id].lastCell = null;
  376.   };
  377.   };
  378.   /*@end
  379.   @*/
  380. if(!fdTableActions.tableCache[this.id].colHover) { return; };
  381. for(var i = colRangeOld[0]; i <= colRangeOld[1]; i++) {
  382. r[r.length] = i;
  383. };
  384. fdTableActions.tableCache[this.id].lastColCache = [0,0];
  385. };
  386.  
  387. // Remove
  388. if(r.length) {
  389. for(var i = 0; i < r.length; i++) {
  390. if(r[i] >= fdTableActions.tableCache[this.id].colCache.length) continue;
  391. elemArr = fdTableActions.tableCache[this.id].colCache[r[i]];
  392. for(var ec = 0, elem; elem = elemArr[ec]; ec++) {
  393. elem.className = elem.className.replace(fdTableActions.tableCache[this.id].colHover, "");
  394. };
  395. };
  396. };
  397.  
  398. // Add
  399. if(n.length) {
  400. for(i = n[0]; i < n[1]; i++) {
  401. elemArr = fdTableActions.tableCache[this.id].colCache[i];
  402. for(var ec = 0, elem; elem = elemArr[ec]; ec++) {
  403. if(elem.className.search(fdTableActions.tableCache[this.id].colHover) == -1) {
  404. elem.className = elem.className + " " + fdTableActions.tableCache[this.id].colHover;
  405. };
  406. };
  407. };
  408. };
  409. },
  410. deselectAllRows:function(tableId) {
  411. if(!(tableId in fdTableActions.tableCache) || !fdTableActions.tableCache[tableId].rowSelect) return;
  412. var trList = fdTableActions.tableCache[tableId].trCache;
  413. for(var i = trList.length; i--;) {
  414. trList[i].className = trList[i].className.replace(fdTableActions.tableCache[tableId].rowSelect, "");
  415. };
  416. fdTableActions.tableCache[tableId].trCache = [];
  417. trList = null;
  418. },
  419. unLoad:function() {
  420. var table;
  421. for(tbl in fdTableActions.tableCache) {
  422. table = document.getElementById(tbl);
  423. if(table) {
  424. fdTableActions.removeEvent(table, "mouseover", fdTableActions.tableEvent);
  425. fdTableActions.removeEvent(table, "mouseout", fdTableActions.tableEvent);
  426. if(fdTableActions.tableCache[tbl].rowSelect) {
  427. fdTableActions.removeEvent(table, "click", fdTableActions.clickEvent);
  428. };
  429. };
  430. /*@cc_on
  431.   /*@if(@_win32)
  432.   fdTableActions.tableCache[tbl].req = fdTableActions.tableCache[tbl].lastRow = fdTableActions.tableCache[tbl].lastCell = null;
  433.   /*@end
  434.   @*/
  435. fdTableActions.tableCache[tbl].colCache = fdTableActions.tableCache[tbl].trCache = null;
  436. fdTableActions.tableCache[tbl] = null;
  437. delete(fdTableActions.tableCache[tbl]);
  438. };
  439. },
  440. addEvent:function(obj, type, fn) {
  441. if( obj.attachEvent ) {
  442. obj["e"+type+fn] = fn;
  443. obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
  444. obj.attachEvent( "on"+type, obj[type+fn] );
  445. } else {
  446. obj.addEventListener( type, fn, true );
  447. };
  448. },
  449. removeEvent: function(obj, type, fn) {
  450. if( obj.detachEvent ) {
  451. try {
  452. obj.detachEvent( "on"+type, obj[type+fn] );
  453. obj[type+fn] = obj["e"+type+fn] = null;
  454. } catch(err) {};
  455. } else {
  456. obj.removeEventListener( type, fn, true );
  457. };
  458. }
  459. };
  460.  
  461. fdTableActions.addEvent(window, "load", fdTableActions.init);
  462. fdTableActions.addEvent(window, "unload", fdTableActions.unLoad);

URL: http://www.frequency-decoder.com/2007/11/15/unobtrusive-table-actions-script

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.