Netscape DOM and event handling

I tried this quoestion in the browsers section, but had no luck there, so I try here with the javascript gurus instead :)

I have a problem with events in netscape (NN6.2), when adding and removing nodes in the DOM on a page.

I have made a little sample, that illustrates my problem.
I'm trying to sort the contents of a table in a webpage using the DOM to swap the TR-node.
The sort is working fine, but after having sorted the table, i loose all the eventhandlers that are
associated with the elements/nodes and their children.

In my sample the doSelect() is not called after the sort, but it works fine before the sort.

The sample works fine in IE...

I do not want other solutions on how to sort the contents of the table, as I can find that myself.

The real question is : How do I get NN62 to handle the events after changing the DOM ?

sample start
<!--
<html>
<head>
<title>..::Table Sort::..</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
// global vars
var g_iSortColumn;
var g_iLastSortColumn =-1;
var g_iSortDirection  = 1;

/////////////////////////////////////////////////////////////////////////
//      function sortTable()
//
// function to sort a tab?e in a HTML page
// Sorts the table rows in the table with id as specified in
// strTableId by the column 'iCol'.
// The placement of rows, that does not have at least 'iCol' columns,
// is not specified.
//
// Only 'TR' rows that are inside the first 'TBODY' tag will be sorted.
// Only 'TD' cells are compared, so 'TH' cells first in a table are
// not moved during sort.
//
// HTML for table must be well-formed.
//
// Think twice when using ROWSPAN & COLSPAN, as iCol is used to index
// columns on each row.
//
//     PARAMETERS
//       iCol          : Column index to sort table by.
//       strTableId     : Id of table to sort.
//       iHeaders     : Number of header lines to ignore from sort.
function sortTable(iCol, strTableId, iHeaders) {
     var tbl     = document.getElementById(strTableId);
     var tblBody = tbl.getElementsByTagName("tbody").item(0);
     var tblRows = tblBody.getElementsByTagName("tr");
     
     var arr = new Array( tblRows.length-iHeaders );
     
     // create array of rows so we can sort
     for ( i=iHeaders; i<tblRows.length; i++) {
          arr[i-iHeaders] = tblRows[i].cloneNode(true);
     }
     
     // set column that sort is based on
     g_iSortColumn = iCol;
     
     // find the sort direction
     if (g_iLastSortColumn==iCol)
          g_iSortDirection *= -1;
     else
          g_iSortDirection = 1;
     g_iLastSortColumn=iCol;
     
     // peform the sort
     arr.sort(sortCompare);
     
     // write sorted data from array to table
     for ( i=iHeaders; i < tblRows.length; i++) {
          tblBody.replaceChild( arr[i-iHeaders].cloneNode(true), tblRows[i] );
     }
}

/////////////////////////////////////////////////////////////////////////
// function sortCompare
// internal function used by sortTable function
function sortCompare( val1, val2 ) {
     var cells1= val1.getElementsByTagName("td");
     var cells2= val2.getElementsByTagName("td");
     
     if ( !cells1 || !cells2 ||
          g_iSortColumn < 0 ||
           cells1.length <= g_iSortColumn ||
           cells2.length <= g_iSortColumn  )
     {
          return 0;
     }
     if ( cells1[g_iSortColumn].childNodes[0].nodeValue < cells2[g_iSortColumn].childNodes[0].nodeValue
)
          return -1*g_iSortDirection;
         
     if ( cells1[g_iSortColumn].childNodes[0].nodeValue > cells2[g_iSortColumn].childNodes[0].nodeValue
)
          return g_iSortDirection;
     
     return 0;
}
</script>
<script type="text/javascript">
     function doselect(obj) {
          alert("selection:"+obj.id);
     }
</script>
</style>
</head>

<body bgcolor="#FFFFFF" text="#000000">
<h3>Demonstration of tablesort</h3>
<table border="1" id="theTable">
 <tr>
   <th>
     <input type="button" name="btnSort1" value="Sort" onClick="sortTable(0, 'theTable', 1)">
   </th>
   <th>
     <input type="button" name="btnSort2" value="Sort" onClick="sortTable(1, 'theTable', 1)">
   </th>
 </tr>
 <tr  id="tr1" onclick="doselect(this)">
   <td>a</td>
   <td>13</td>
 </tr>
 <tr  id="tr2" onclick="doselect(this)">
   <td>b</td>
     <td>12</td>
 </tr>
 <tr  id="tr3" onclick="doselect(this)">
   <td>ca</td>
   <td>11</td>
 </tr>

</table>
</body>
</html>

-->
sample end
klausenAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

b1xml2Commented:
Amended Script
==============
<html>
<head>
<title>..::Table Sort::..</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
// global vars
var g_iSortColumn;
var g_iLastSortColumn =-1;
var g_iSortDirection  = 1;

/////////////////////////////////////////////////////////////////////////
//      function sortTable()
//
// function to sort a tab?e in a HTML page
// Sorts the table rows in the table with id as specified in
// strTableId by the column 'iCol'.
// The placement of rows, that does not have at least 'iCol' columns,
// is not specified.
//
// Only 'TR' rows that are inside the first 'TBODY' tag will be sorted.
// Only 'TD' cells are compared, so 'TH' cells first in a table are
// not moved during sort.
//
// HTML for table must be well-formed.
//
// Think twice when using ROWSPAN & COLSPAN, as iCol is used to index
// columns on each row.
//
//     PARAMETERS
//       iCol          : Column index to sort table by.
//       strTableId     : Id of table to sort.
//       iHeaders     : Number of header lines to ignore from sort.
function sortTable(iCol, strTableId, iHeaders) {
    var tbl     = document.getElementById(strTableId);
    var tblBody = tbl.getElementsByTagName("tbody").item(0);
    var tblRows = tblBody.getElementsByTagName("tr");
   
    var arr = new Array( tblRows.length-iHeaders );
   
    // create array of rows so we can sort
    for ( i=iHeaders; i<tblRows.length; i++) {
         arr[i-iHeaders] = tblRows[i].cloneNode(true);
    }
   
    // set column that sort is based on
    g_iSortColumn = iCol;
   
    // find the sort direction
    if (g_iLastSortColumn==iCol)
         g_iSortDirection *= -1;
    else
         g_iSortDirection = 1;
    g_iLastSortColumn=iCol;
   
    // peform the sort
    arr.sort(sortCompare);
   
    // write sorted data from array to table
    for ( i=iHeaders; i < tblRows.length; i++) {
         tblBody.replaceChild( arr[i-iHeaders].cloneNode(true), tblRows[i] );
    }
    //for NS 6.x
    if (document.getElementById && ! document.all) {
                  var oNodeList = tblBody.getElementsByTagName("tr");
                  var szId;
                  for (var i=0;i<oNodeList.length;i++) {
                        szId = oNodeList.item(i).id;
                        if (szId.indexOf("tr") != -1) oNodeList.item(i).onclick = doselect;
                  }
    }
   
}

/////////////////////////////////////////////////////////////////////////
// function sortCompare
// internal function used by sortTable function
function sortCompare( val1, val2 ) {
    var cells1= val1.getElementsByTagName("td");
    var cells2= val2.getElementsByTagName("td");
   
    if ( !cells1 || !cells2 ||
         g_iSortColumn < 0 ||
          cells1.length <= g_iSortColumn ||
          cells2.length <= g_iSortColumn  )
    {
         return 0;
    }
    if ( cells1[g_iSortColumn].childNodes[0].nodeValue < cells2[g_iSortColumn].childNodes[0].nodeValue

)
         return -1*g_iSortDirection;
         
    if ( cells1[g_iSortColumn].childNodes[0].nodeValue > cells2[g_iSortColumn].childNodes[0].nodeValue

)
         return g_iSortDirection;
   
    return 0;
}
</script>
<script type="text/javascript">
    function doselect(obj) {
                  if ("string" == typeof(this.tagName) && this.tagName == "TR") obj = this;
                  alert("selection:"+obj.id);
    }
</script>
</style>
</head>

<body bgcolor="#FFFFFF" text="#000000">
<h3>Demonstration of tablesort</h3>
<table border="1" id="theTable">
<tr>
  <th>
    <input type="button" name="btnSort1" value="Sort" onClick="sortTable(0, 'theTable', 1)">
  </th>
  <th>
    <input type="button" name="btnSort2" value="Sort" onClick="sortTable(1, 'theTable', 1)">
  </th>
</tr>
<tr  id="tr1" onclick="doselect(this)">
  <td>a</td>
  <td>13</td>
</tr>
<tr  id="tr2" onclick="doselect(this)">
  <td>b</td>
    <td>12</td>
</tr>
<tr  id="tr3" onclick="doselect(this)">
  <td>ca</td>
  <td>11</td>
</tr>

</table>
</body>
</html>

Notes
=====
1. You may be surprised by the results shown in NS <GRIN>
klausenAuthor Commented:
hehe - yes this works fine ! (thanks b1xml2)
But does that mean, that I have to check all childs of my <TR> nodes (recursive) to check for other event too, if I want my sortTable() to work on other tables as well.

f.ex. is there any 'onmouseover' or 'onclick' eventhandlers on any of the <TD> elements or maybe events for another element inside one of the <TD> elements. If there is, then I need to check all childs for all possible eventhandlers which is a shame when moving elements in the DOM (changing the page) is so easy.

In think that this makes, managing the DOM in netscape, less usefull than it needs to be. Can this really be right ?
b1xml2Commented:
klausen, it would appear that the cloneNode() method does not properly clone the events, in NS 6. Either this is an oversight on part the developers, (raise a Bugzilla Report with them if that is the case), or MSIE has implemented extensions to the W3C (which is more than likely).

>>
But does that mean, that I have to check all childs of my <TR> nodes (recursive) to check for other event too, if I want my sortTable() to work on other tables as well.
<<

Unfortunately, yes =(

>>
In think that this makes, managing the DOM in netscape, less usefull than it needs to be. Can this really
be right ?
<<

Not necessarily. If you assign functions to events thru script, rather than thru HTML, you'd be alright. It would appear that you have stumbled on a great weakness of the DOM Implementation under 6.2.


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

klausenAuthor Commented:
well - it seems like there is a problem with the DOM :(

It seems that transforming XML to HMTL using XSLT also means the event are not generated - but i will ask about this in another question.

Thanks for your answer and time on this question.
Even though i'm not happy about the result - you shall have the point that i promised for an answer.

thanks...
b1xml2Commented:
welcome =)
ahosangFinance Systems DeveloperCommented:
Or just don't use cloneNode() function when building array arr. Try:

arr[i-iHeaders] = tblRows[i];

and:

for ( i=iHeaders; i < tblRows.length; i++) {
         tblBody.replaceChild( arr[i-iHeaders], tblRows[i] );
    }

I used a similar sortable table once, but with innerHTML instead of replaceChild and it worked in NS6, but not in IE!

The trials and tribulations of catering to these browsers!!
b1xml2Commented:
ahosang, the problem is not with the cloneNode() method, the problem is with the propagation of the events which do not work with NS 6.x

There's nothing wrong with klausen's code. It is rather elegant.
ahosangFinance Systems DeveloperCommented:
Didn't say the code was bad. My last sentence shows exactly what my point is :-)
b1xml2Commented:
rofl, can we line all the developers of these browsers and send them off to siberia *Grin*
klausenAuthor Commented:
Thought that I had already accepted your answer - but I must have forgot to press some button :)

Again - Thanks for answering, nice to have 'all-knowing' allies on this site...
b1xml2Commented:
klausen, check my responses in the Browser section on Netscape, XML Transforms and XHTML. Thanx
EasyDesignsCommented:
This script works great, but I have one complaint and that is that it does not address sorting of numeric values as appropriate (i.e. 200 comes before 50 because 2 is less than 5). Consider adding

  if (isNumber(cells1[g_iSortColumn].childNodes[0].nodeValue) && isNumber(cells2[g_iSortColumn].childNodes[0].nodeValue)) {
     diff =  cells1[g_iSortColumn].childNodes[0].nodeValue - cells2[g_iSortColumn].childNodes[0].nodeValue;
     if (diff > 0) return g_iSortDirection;
     if (diff < 0) return -1*g_iSortDirection;
     return 0;
  }

at the beginning of the sortCompare() function to add in sort functionality for numeric values.

isNumber() is a simple evaluation of whether or not the value passed to it is a number of any sort. email me if you need/want the script.
klausenAuthor Commented:
Sounds great that you can use this. I have already created different kinds of sort functions (which is why if made it easy to change the sort function).
I have sortCompare(), sortCompareLocale(), sortCompareNumericLocale() fumctions to use with this script.
instead of isNumeric() is just use parseFloat() so that any text that begins with numbers, are sorted based on the numrric value as you suggest.
I did it like this
    try {
        var f1 = parseFloat(cells1[g_iSortColumn].childNodes[0].nodeValue);
        var f2 = parseFloat(cells2[g_iSortColumn].childNodes[0].nodeValue);
       
       
        if ( !isNaN(f1) && !isNaN(f2) ) {
            var result = (f1 - f2)*g_iSortDirection;
            if ( result != 0 )
                return result;
        }
    } catch ( E ) { }

I have also solved my original problem, by avoiding the use of cloneNode() which is causing the problem with the lost event handlers.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Languages and Standards

From novice to tech pro — start learning today.