Why can't I call a function which does what it does if a click on theheader of a column?

S

Stefan Mueller

It's frustrating! Since several days I'm trying to program something
which looks so simple but I'm not able to do it.
I hope someone can give me a hint.

Here is my example code (or use: http://test.seekware.ch/example.html)

=====================================================

<html>
<head>
<script type = "text/javascript" src = "http://test.seekware.ch/
sort.js"></script>
</head>

<body>
<table class = "sortable">
<thead>
<tr><th>Name</th><th>Team</th></tr>
</thead>
<tbody>
<tr><td>Zora</td><td>Liverpool</td></tr>
<tr><td>Albert</td><td>Manchester</td></tr>
<tr><td>Roman</td><td>Tottenham</td></tr>
<tr><td>Steven</td><td>Arsenal</td></tr>
</tbody>
</table>
</body>
</html>

=====================================================

I'd like to have a table where I can sort each column by clicking on
its header. This works pretty well.

Now I'd like have a variable with defines which column has to be
sorted just after the page is loaded. However, I'm not able to call
the function by script to sort the table (it only works by clicking on
its header).

After the page is loaded the function 'page_is_loaded' gets called by
'window.onload = page_is_loaded;'.
Now I'm trying to figure out what command I have to execute (at the
position where currently 'alert ("Here I'd like to call the function
to sort e.g. the first column");' is written) to sort e.g. the first
column of the table just after the page is loaded.

Unbelievable, but I'm not able to sort the table by script. It only
works by clicking on the header of a column.

Any help is very appreciated.
Stefan
 
M

Matt Kruse

I'd like to have a table where I can sort each column by clicking on
its header.

Is it a learning exercise? Otherwise, why re-invent the wheel?
After the page is loaded the function 'page_is_loaded' gets called by
'window.onload = page_is_loaded;'.
function page_is_loaded() {
sort.init;

What do you expect that first line to do?
Now I'm trying to figure out what command I have to execute (at the
position where currently 'alert ("Here I'd like to call the function
to sort e.g. the first column");' is written) to sort e.g. the first
column of the table just after the page is loaded.

Your sorting logic is trapped inside the 'makeSortable' function. You
need to refactor it out into a stand-alone method that can be called
on page load to sort the column you wish.

Even better would be to sort the table correctly server-side, so you
don't need to do any sorting on initial load.

Matt Kruse
 
T

Thomas 'PointedEars' Lahn

Stefan said:
It's frustrating!

Not interested.
Since several days I'm trying to program something
which looks so simple but I'm not able to do it.

Irrelevant. Omit that next time.
I hope someone can give me a hint.

Here is my example code (or use: http://test.seekware.ch/example.html)

=====================================================

<html>
<head>
<script type = "text/javascript" src = "http://test.seekware.ch/
sort.js"></script>

Although it probably does not matter, remove the whitespace before and after
the `='s.

Use said:
</head>

<body>
<table class = "sortable">

See above.
<thead>
<tr><th>Name</th><th>Team</th></tr>
</thead>
<tbody>
<tr><td>Zora</td><td>Liverpool</td></tr>
<tr><td>Albert</td><td>Manchester</td></tr>
<tr><td>Roman</td><td>Tottenham</td></tr>
<tr><td>Steven</td><td>Arsenal</td></tr>
</tbody>
</table>
</body>
</html>

=====================================================

I'd like to have a table where I can sort each column by clicking on
its header. This works pretty well.

Now I'd like have a variable with defines which column has to be
sorted just after the page is loaded. However, I'm not able to call
the function by script to sort the table (it only works by clicking on
its header).

After the page is loaded the function 'page_is_loaded' gets called by
'window.onload = page_is_loaded;'.

Bad idea. Use the `onload' attribute of the BODY element instead.
Now I'm trying to figure out what command I have to execute (at the
position where currently 'alert ("Here I'd like to call the function
to sort e.g. the first column");' is written) to sort e.g. the first
column of the table just after the page is loaded.

Unbelievable, but I'm not able to sort the table by script. It only
works by clicking on the header of a column.

With this code, it cannot work. Assuming in your favor that

sort.init;

in the code that you have presented is in fact

sort.init();

in the *real* code, you could check the source code of sort.init() and
eventually sort.makeSortable() to see why it works only on click. That is,
after you have reformatted it to be easily readable; for example, your
indentation is way off in several places.

Did you even write that code yourself? Its junkyness[1], and bloatedness
better fits the common "clueless newbie includes/copypastes prepackaged
Unobtrusive JavaScript library written by other wannabes" pattern.


PointedEars
___________
[1] Undeclared identifiers like `sort', bogus feature tests like `stIsIE'
and type-converting feature-tests, \b in className matches,
createElement() with untesteed `innerHTML', needless user-defined
forEach()es), Webdings font with FONT element(!), to name a few
 
T

Thomas 'PointedEars' Lahn

Thomas said:
With this code, it cannot work. Assuming in your favor that

sort.init;

in the code that you have presented is in fact

sort.init();

in the *real* code, you could check the source code of sort.init() and
eventually sort.makeSortable() to see why it works only on click. That
is, after you have reformatted it to be easily readable; for example, your
indentation is way off in several places.

I see now that it is only

if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", sort.init, false);
}

that makes it work (in non-IEs). All the code above is meaningless without
the fix that I proposed. And the following --

/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer
src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
sort.init();
}
};
/*@end @*/

if (/WebKit/i.test(navigator.userAgent)) {
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
sort.init();
}
}, 10);
}

-- is probably the worst, needless piece of source code I have ever seen.

You want to rewrite that script first.


PointedEars
 
D

David Mark

Is it a learning exercise? Otherwise, why re-invent the wheel?

I know I would. Good wheels are very hard to come by in this
business. ;)

Any recommendations?
 
R

RobG

I see now that it is only

if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", sort.init, false);
}

that makes it work (in non-IEs). All the code above is meaningless without
the fix that I proposed. And the following --

/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer
src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
sort.init();
}
};
/*@end @*/

if (/WebKit/i.test(navigator.userAgent)) {
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
sort.init();
}
}, 10);

}

-- is probably the worst, needless piece of source code I have ever seen.

You want to rewrite that script first.

A close second is the getInnerText function. For the OP:

| getInnerText: function(node) {
|
| hasInputs = (typeof node.getElementsByTagName == 'function') &&
| node.getElementsByTagName('input').length;

Guess which browser will always return false, even if
getElementsByTagName is a method of node? Or is this a kind of browser
sniff?


| if (node.getAttribute("sort_customkey") != null) {
| return node.getAttribute("sort_customkey");
| }

Why use getAttribute at all - oh right, using invalid attributes in
the markup. Not a good idea. The class attribute is available for this
purpose.


| else if (typeof node.textContent != 'undefined' && !hasInputs) {
| return node.textContent.replace(/^\s+|\s+$/g, '');
| }

I think that:

typeof node.textContent == 'string' ...

would be a better condition, since that is what textContent is
specified to return if setting it has some effect. e.g.

alert( typeof document.textContent ); // null

<URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-textContent
[...snip..]

It would be a good idea to search the archives for a much simpler
cross browser function.

The OP seems to have fallen into the trap of trying to make a general
case table sorting function that works in every case. It is much more
efficient to write one that only addresses particular cases, otherwise
the result is likely to be a huge function that still does not work in
all cases.
 
M

Matt Kruse

I know I would.  Good wheels are very hard to come by in this
business.  ;)
Any recommendations?

It needs a little work, but I think my table sorter is pretty decent.
It's faster than almost all the others and combined sorting, paging,
filtering, etc. I have another cleanup/bug-fix/enhancement release
half done, but haven't gotten back to it for many months...

http://javascripttoolbox.com/lib/table/

Matt Kruse
 
S

Stefan Mueller

No, it's not a learning exercise.
I do need this code and I really appreciate your feedbacks and
correction hints. It seems that I really have to rewrite that code.
However, the code is working in all tested browsers (IE, Mozilla,
Opera, Safari) and is quite fast.
But my problem is that I have no idea which function I have to call by
the script to sort the table. I'm only able to sort the table by
clicking with the mouse on the header of a column.

Is it really not possible to call the sort function within the script?
 
M

Matt Kruse

But my problem is that I have no idea which function I have to call by
the script to sort the table. I'm only able to sort the table by
clicking with the mouse on the header of a column.

The point is that there is no callable function available. It's
embedded into the code as an anonymous function:

makeSortable: function(table) {
[...]
dean_addEvent(headrow,"click", function(e) {
if (this.className.search(/\bsort_sorted\b/) != -1) {
[...]

That "function(e) { ... " is what runs to sort.

You need to pull that out into a separate callable function in order
to call it in your init.

You obviously didn't write this script, right? Since it is fairly
poorly written, I would recommend finding a different one.

Matt Kruse
 
G

Gregor Kofler

Matt Kruse meinte:
It needs a little work, but I think my table sorter is pretty decent.
It's faster than almost all the others and combined sorting, paging,
filtering, etc. I have another cleanup/bug-fix/enhancement release
half done, but haven't gotten back to it for many months...

http://javascripttoolbox.com/lib/table/

Is there anything practical about the pagination? Since all the data is
already streamed to the client and processed there, setting a proper
overflow style with scrollbar does the job in a more flexible way, and
is easier to grasp (being on a subpage and changing the sort order can
be confusing to say the least).

Gregor
 
S

Stefan Mueller

Matt, thanks a lot for the hint about the 'function(e)'. With this
information I moved the content of the 'function(e)' to an external
function called 'function_sort(e, this)'. In order that the sorting is
still working I had to replace 'this' with 'var_this' in the external
function.
Now I think I do have the missed external sort function which I can
call from 'window.onload'.

However, the "only" question is now how to call the external sort
function 'function_sort(e, var_this)' so that e.g. the first column
gets sorted? Could this work or am I totally on the wrong way?

The modified example code is still at http://test.seekware.ch/example.html
 
D

David Mark

It needs a little work, but I think my table sorter is pretty decent.
It's faster than almost all the others and combined sorting, paging,
filtering, etc. I have another cleanup/bug-fix/enhancement release
half done, but haven't gotten back to it for many months...

http://javascripttoolbox.com/lib/table/

Yeah, I'm sure it's not outrageously terrible like most. As long as
it doesn't use jQuery or the like. ISTM that Richard published a
table sorter a long time ago as well (or better likely).
 
M

Matt Kruse

Yeah, I'm sure it's not outrageously terrible like most.  As long as
it doesn't use jQuery or the like.

Nope, it's pure js, no dependencies. Although I did add a jQuery
"interface" so you could run the sorting in a jQuery-ish way if you
wish.

You're welcome to analyze the code in the same way that you critique
jQuery if you want. I always welcome criticism.
 ISTM that Richard published a
table sorter a long time ago as well (or better likely).

I remember him posting a static-table-header script, but I've never
seen a table sorter from him. If it exists I'm sure it's robust, and
there would be pros and cons to each of our approaches.

Matt Kruse
 
D

David Mark

Nope, it's pure js, no dependencies. Although I did add a jQuery
"interface" so you could run the sorting in a jQuery-ish way if you
wish.

Nothing wrong with that (for those who inexplicably prefer such an
interface).
You're welcome to analyze the code in the same way that you critique
jQuery if you want.

I know. Only so many hours in the day for free advice. :)
I always welcome criticism.

That's good.
I remember him posting a static-table-header script, but I've never
seen a table sorter from him. If it exists I'm sure it's robust, and
there would be pros and cons to each of our approaches.

I may have been thinking of the static header thing.
 
M

Matt Kruse

Matt, thanks a lot for the hint about the 'function(e)'. With this
information I moved the content of the 'function(e)' to an external
function called 'function_sort(e, this)'. In order that the sorting is
still working I had to replace 'this' with 'var_this' in the external
function.
Now I think I do have the missed external sort function which I can
call from 'window.onload'.

However, the "only" question is now how to call the external sort
function 'function_sort(e, var_this)' so that e.g. the first column
gets sorted? Could this work or am I totally on the wrong way?

The modified example code is still athttp://test.seekware.ch/example.html

I think you're missing some required basic understanding of javascript
in order to continue down this path. I could tell you how to fix this
problem, but then you would just be stuck on the next step.

<rant>
A common problem when people ask for help is that they are not asking
for a factual answer like they think they are. For example, "how far
is it to the sun?" or "how fast does light travel?" or "why is the sky
blue?". These have answers. Google can find them for you. The problem
is just a lack of information, and once the person has the answer,
they can continue with what they were doing.

Instead, many people are really asking for a process, or the solution
to a problem. You want a sortable table, and you have an existing
script, and you want it to work but you don't know what to do. The
problem is not a missing piece of information, but a lack of
understanding of the process needed to get you from point A to point
B. You don't just need knowledge (facts), but you need
_understanding_. And that takes more than a simple answer on a
newsgroup or a link to a page about how to call functions and pass
references to DOM objects and how to handle events. You cannot Google
and find the answer, because there isn't a single answer.

All too often, people asking questions don't realize that they think
they are looking for factual straight-forward answers, but really they
need to learn how to think and understand and tackle the problem in
front of them. And then they get frustrated when the people who have
the understanding "won't help them" by just giving them the answer.

What the world sorely lacks is an understanding of logic, problem-
solving, and critical thinking. We need to stop cramming facts into
heads. Facts are easily looked up. Anyone can do it. Knowing facts
isn't powerful anymore. Understanding how to find facts and filter out
the ones that are needed and how to use them and how to analyze
problems and come up with solutions - THAT is powerful.
</rant>

Sorry... anyway, I suggest you do some reading about javascript to
understand the code you are working with and why it is doing what it
is doing. Specifically, look into how event handlers are called, how
events are passed, what "this" means in difference contexts, how to
get a reference to DOM nodes, and how to pass them to functions. Or
just find another script that does what you want without having to
learn why.

Matt Kruse
 
S

Stefan Mueller

Matt, you're absolutely right. I don't have enough understanding about
javascript. I do write Visual Basic code. However, web browsers don't
understand Visual Basic and therefore I have/try to write some code in
javascript.
In this special case I'm really overextended. I tried many things
before and I couldn't find any solution which worked. So I've started
this post and especially you showed me where the problem is located.
I've never heard something about 'embedded into the code as an
anonymous function'.
Now I think I understand it a little and with your hint I see a light
at the end of the tunnel. Therefore I tried last night to extract the
anonymous function to an external function. But then I faced the next
problem and because you and many other specialists read this post – I
really appreciate it very must - I just asked again for a solution for
my new problem before intensively trying to find a solution by myself.
I’m really sorry for that and I absolutely understand your answer.

Because I'm now quite lost I would appreciate it very much if someone
would comment if I'm on the right track with “my” solution or if I'm
totally wrong.

Anyway, many thank to all of you for your time and feedbacks.

PS: Matt, your statements exactly describe how it is.
 
S

Stefan Mueller

Yes, I was on the right path. Now it works.

I don't need 'function(e)'. 'function()' is enough. No idea why there
was 'function(e)'.
And I just need to call the external function 'function_sort' with

function page_is_loaded() {
var headrow;

sort.init();

forEach(document.getElementsByTagName('table'), function
(var_table) {
headrow = var_table.tHead.rows[0].cells;
function_sort(headrow[0]);
});
}

Many thanks again for your help.
 
D

Dr J R Stockton

In comp.lang.javascript message <c37ce8d4-2cda-40c2-8085-17f6b2b5befa@u1
g2000pre.googlegroups.com>, Wed, 9 Dec 2009 17:05:13, RobG
The OP seems to have fallen into the trap of trying to make a general
case table sorting function that works in every case. It is much more
efficient to write one that only addresses particular cases, otherwise
the result is likely to be a huge function that still does not work in
all cases.

Is there a problem with efficiency? The Subject says that sorting is in
response to a click, and sorting a table should be expected not to be
instant, and one second is an adequate target - or more if there is an
active progress indicator.

That assumes that there's a touch or two of intelligent high-level
optimisation :

The actual sort method should not be a bubble sort, taking N^2 time, but
one of the faster ones (Wikipedia &c). It should be possible to use the
built-in array sort.

If the required comparisons are not of string or numeric value of
elements, but some more complex function of elements, then one should in
a preliminary pass taking time N to compute a sort key for each row,
such that keys can be compared directly. That can be the .key of each
rows[J]?

One assumes that the number of columns will be no more than, say, 20;
and the number of rows no more than 1000 - OR that the user will
understand the need to wait for more than a second.

The swapping of rows can be done by exchanging TR elements in a manner
which is essentially just a pointer exchange, without any object
creation or pseudo-destruction. One should be able to sort the rows
array, with a comparison function that just compares rows[J].key.



Something which sprung to mind on reading MK's contribution - is there
an assumption in it that sorting is row-wise as per Subject? In
general, one might want to sort columns according to the data in a
particular row. That case could be added, by using additional time
N*M*2 to transpose the table before and after the ordinary sort.


I am reminded of a friend, a maths teacher using some form of primitive
Basic, who complained that sorting the data for one school form (say 30
pupils) took a quarter of an hour. I spoke on swapping pointers rather
than copying data, and the next I heard was that it took less time than
he could see.
 
T

Thomas 'PointedEars' Lahn

Dr said:
The actual sort method should not be a bubble sort, taking N^2 time, but
one of the faster ones (Wikipedia &c). It should be possible to use the
built-in array sort.

ACK, which I presume to be an implementation of Quicksort or better.
If the required comparisons are not of string or numeric value of
elements, but some more complex function of elements, then one should in
a preliminary pass taking time N to compute a sort key for each row,
such that keys can be compared directly.

What would be the advantage of computing static keys and comparing them as
compared to comparing the live values?
That can be the .key of each rows[J]?

One assumes that the number of columns will be no more than, say, 20;
and the number of rows no more than 1000 - OR that the user will
understand the need to wait for more than a second.

The swapping of rows can be done by exchanging TR elements in a manner
which is essentially just a pointer exchange, without any object
creation or pseudo-destruction. One should be able to sort the rows
array, with a comparison function that just compares rows[J].key.

The object that the `rows' property of a table object refers to is not an
Array instance, but a NodeList implementation; that is what would make this
approach not considerably more efficient than the other one of reading table
data into an array, sorting the array and recreating the table from the sort
result. And are you actually suggesting to augment host objects here?


PointedEars
 
L

Lasse Reichstein Nielsen

Thomas 'PointedEars' Lahn said:
ACK, which I presume to be an implementation of Quicksort or better.

Likely quicksort, often with a fallback to insertion sort for shorter
segments.
What would be the advantage of computing static keys and comparing them as
compared to comparing the live values?

If the comparison of two elements requires a significant transformation
of the elements before comparions, performing the transformation once,
at first, or caching the result the first time it's needed, might take
less time than doing it log(n) times (on average) during the sorting.

On the other hand, it complicates the algorithm if you need to keep
two arrays in sync (but that can be avoided by creating an array of
pairs of transformed elements and original indices, and sorting just
that on the transformed elements), and it requires more space, which
would probably be overkill for most smaller arrays.

In the case of the default compare function, it converts both arguments
to strings before comparing them lexicographically.
If that is an expensive operation (which it potentially is, if you
are comparing objects with an expensive toString function), it might
make sense to cache the results. In most cases, the values being compared
with the default compare function will be strings or numbers (and the
numbers really should use a custom compare function too), so any extra
work will be wasted.

That can be the .key of each rows[J]?

One assumes that the number of columns will be no more than, say, 20;
and the number of rows no more than 1000 - OR that the user will
understand the need to wait for more than a second.

Hmm, I don't see why.
The swapping of rows can be done by exchanging TR elements in a manner
which is essentially just a pointer exchange, without any object
creation or pseudo-destruction. One should be able to sort the rows
array, with a comparison function that just compares rows[J].key.

However, the row container doesn't have a swap operation, so you will
have to go through the normal DOM methods. That won't be nearly as
fast as doing things directly in JavaScript.
The object that the `rows' property of a table object refers to is not an
Array instance, but a NodeList implementation; that is what would make this
approach not considerably more efficient than the other one of reading table
data into an array, sorting the array and recreating the table from the sort
result. And are you actually suggesting to augment host objects here?

My guess (it was a long time since I did the test) is that working directly
on the DOM is significantly slower than creating an array in Javascript and
sorting that, and then put the DOM in the correct order (e.g., by removing all
rows and reinserting them in the correct order).


On problem with using the built-in sort is that it's not guaranteed to be
stable. When sorting a table, it's desireable that sorting first on column
and then another is stable wrt. the result of the first sort.
/L
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top