Annoying NN/Firefox Highlight Bug

5

50295

Hi everyone,

This one is better experienced than explained, so I'm including a code
sample below. Please and save (as an html file) and view with NN or
Firefox (or maybe even Mozilla), and then view. When loaded:

(1.) Place the mouse over "Top Menu" item.
(The menu opens)
(2.) Move to any of the sub-menu items.
(3.) Click on the left or right (but NOT on the text) of the current
sub-item
(The menu collapses)
(4.) Now place the mose over the top-menu
-> The menu opens as expected, but the an annoying highlight
appears, and follows the mouse.

Please tell me there's a work-around.

------------------------ H T M L ------------------------

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">

<script type="text/javascript">

function getIndex(){

if (navigator.appName == "Microsoft Internet Explorer"){
return 2;
}
else if (navigator.appName == "Netscape"){
return 3;
}
}


function bindListeners(){

var anchors = document.getElementsByTagName("A");
for (i = 0; i < anchors.length; i++){

anchors.onmousedown = function() {
this.className = "mouseDown";
}

anchors.onmouseup = function() {
this.className = "mouseUp";

var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


if (object.tagName.toUpperCase() == 'BODY'){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'none';
}
else if( object1.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'none';
}

}


anchors.onmouseover = function(){
var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


if (object.tagName.toUpperCase() == 'BODY'){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'block';
}
else if(
object.parentNode.parentNode.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'block';;
}


}

}


}
</script>

<style type="text/css">

li {
background-color: #FFFFFF;
font-weight: normal;
font-family: arial;
font-size: 12px;

text-align: center;
margin: 0;
padding: 0;
height: 1%;
}

ul{
margin: 0;
padding: 0;
list-style: none;
}

a {
display: block;
width: 120px;
text-decoration: none;
color: #000000;
padding: 2px 15px 2px 15px;
}

li:hover ul {
display: block;
}

li ul {
display: none;
}

body li{
width: 150px;
}

li.collapse {
display: none;
}

ul li ul li {
border-top: 1px solid black;
}

li.topMenu {
border: 1px solid black;
}

</style>

<title>test</title>
</head>

<body onLoad="bindListeners()">

<ul><li class="topMenu">
<a href="#">Top Menu</a>
<ul>
<li><a href="#">Sub-menu 1</a></li>
<li><a href="#">Sub-menu 2</a></li>
<li><a href="#">Sub-menu 3</a></li>
<li><a href="#">Sub-menu 4</a></li>
</ul>
</li></ul>

</body>
</html>


------------------------ H T M L ------------------------

I wonder if this is the same bug that this guy is talking about?
http://groups.google.co.uk/[email protected]&output=gplain

Thanks,

- Olumide
 
F

fox

Hi everyone,

This one is better experienced than explained, so I'm including a code
sample below. Please and save (as an html file) and view with NN or
Firefox (or maybe even Mozilla), and then view. When loaded:

(1.) Place the mouse over "Top Menu" item.
(The menu opens)
(2.) Move to any of the sub-menu items.
(3.) Click on the left or right (but NOT on the text) of the current
sub-item
(The menu collapses)
(4.) Now place the mose over the top-menu
-> The menu opens as expected, but the an annoying highlight
appears, and follows the mouse.

Please tell me there's a work-around.

for items in which you do not want text selection, simply add:

li {
-moz-user-select: none;
}

to your css

problem solved...

------------------------ H T M L ------------------------

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">

<script type="text/javascript">

function getIndex(){

if (navigator.appName == "Microsoft Internet Explorer"){
return 2;
}
else if (navigator.appName == "Netscape"){
return 3;
}
}


function bindListeners(){

var anchors = document.getElementsByTagName("A");
for (i = 0; i < anchors.length; i++){

anchors.onmousedown = function() {
this.className = "mouseDown";
}

anchors.onmouseup = function() {
this.className = "mouseUp";

var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


if (object.tagName.toUpperCase() == 'BODY'){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'none';
}
else if( object1.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'none';
}

}


anchors.onmouseover = function(){
var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


if (object.tagName.toUpperCase() == 'BODY'){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'block';
}
else if(
object.parentNode.parentNode.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'block';;
}


}

}


}
</script>

<style type="text/css">

li {
background-color: #FFFFFF;
font-weight: normal;
font-family: arial;
font-size: 12px;

text-align: center;
margin: 0;
padding: 0;
height: 1%;
}

ul{
margin: 0;
padding: 0;
list-style: none;
}

a {
display: block;
width: 120px;
text-decoration: none;
color: #000000;
padding: 2px 15px 2px 15px;
}

li:hover ul {
display: block;
}

li ul {
display: none;
}

body li{
width: 150px;
}

li.collapse {
display: none;
}

ul li ul li {
border-top: 1px solid black;
}

li.topMenu {
border: 1px solid black;
}

</style>

<title>test</title>
</head>

<body onLoad="bindListeners()">

<ul><li class="topMenu">
<a href="#">Top Menu</a>
<ul>
<li><a href="#">Sub-menu 1</a></li>
<li><a href="#">Sub-menu 2</a></li>
<li><a href="#">Sub-menu 3</a></li>
<li><a href="#">Sub-menu 4</a></li>
</ul>
</li></ul>

</body>
</html>


------------------------ H T M L ------------------------

I wonder if this is the same bug that this guy is talking about?
http://groups.google.co.uk/[email protected]&output=gplain

Thanks,

- Olumide
 
R

RobG

Hi everyone,

This one is better experienced than explained, so I'm including a code
sample below. Please and save (as an html file) and view with NN or
Firefox (or maybe even Mozilla), and then view. When loaded:

(1.) Place the mouse over "Top Menu" item.
(The menu opens)
(2.) Move to any of the sub-menu items.
(3.) Click on the left or right (but NOT on the text) of the current
sub-item
(The menu collapses)
(4.) Now place the mose over the top-menu
-> The menu opens as expected, but the an annoying highlight
appears, and follows the mouse.

Please tell me there's a work-around.

Please don't post code with tabs. Use 2 or 4 spaces for indenting.
Manually wrap code to prevent wrapping errors.

[...]
function getIndex(){

if (navigator.appName == "Microsoft Internet Explorer"){
return 2;
}
else if (navigator.appName == "Netscape"){
return 3;
}
}

The concept behind this function is flawed, see below.
function bindListeners(){

var anchors = document.getElementsByTagName("A");
for (i = 0; i < anchors.length; i++){

anchors.onmousedown = function() {
this.className = "mouseDown";
}

anchors.onmouseup = function() {
this.className = "mouseUp";

var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


You seem to be guessing at how many parentNodes will get you to the
<body> tag, then based on that, how many will take to get to the <ul>
tag at the start of the list. See below for a more efficient
solution.
if (object.tagName.toUpperCase() == 'BODY'){

A more efficient test here is:

if ( /body/i.test(object.tagName) ){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'none';
}
else if( object1.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'none';
}

}


anchors.onmouseover = function(){
var object = this.parentNode.parentNode.parentNode;
var object1 = object.parentNode.parentNode;


if (object.tagName.toUpperCase() == 'BODY'){
// top-menu

this.parentNode.childNodes[getIndex()].style.display = 'block';
}
else if(
object.parentNode.parentNode.tagName.toUpperCase() == 'BODY' ){
// sub-menu
this.parentNode.parentNode.style.display =
'block';;
}


}

}


}
</script>


The way your script works is very prone to failure. Your getIndex()
function will only ever work with browsers that identify as
'Netscape' or 'Microsoft Internet Explorer'.

You also make assumptions about the number of nodes based on your
guess of browser.

It seems that what you are trying to do is find the <ul> tag at the
start of the list that was clicked on. If that is so, the best way
to find it is to go up through the parentNodes until you get there.

If the following function is passed a reference to a node, it will go
up the DOM tree until it finds a UL node, then return a reference to
it:

function getULtag(x){
while (!/ul/i.test(x.nodeName) && x.parentNode ){
x = x.parentNode;
}
return x;
}

Below is a more concise version, but it will fail if x is the UL tag
you were after:

function getULtag(x){
while ( (x = x.parentNode) && !/ul/i.test(x.nodeName)){}
return x;
}

Both the above functions will work in any browser supporting
JavaScript and the W3C DOM without regard for whatever they chose to
report as their 'appName' (which includes IE and Netscape, as well as
many others).

A sample implementation is below.

<script type="text/javascript">
function getULtag(x){
while ( (x = x.parentNode) && !/ul/i.test(x.nodeName)){}
return x;
}
</script>
<ul id="ulA">
<li onclick="alert(getULtag(this).id);">hi</li>
<li onclick="alert(getULtag(this).id);">hi</li>
<li onclick="alert(getULtag(this).id);">hi</li>
<li onclick="alert(getULtag(this).id);">hi</li>
</ul>

[...]
 
R

Richard Cornford

RobG said:
(e-mail address removed) wrote:

A more efficient test here is:

if ( /body/i.test(object.tagName) ){
<snip>

Is it a more efficient test? Have you read the ECMA 262 section on
Regular Expressions and thought about what it must take to implement
that? String comparison is not that complex; maybe compare the length
and return false if they don't match, then compare each corresponding
character in turn, returning false at the first non-match (equality
testing between 16 bit integers is trivial for CPUs to do directly), and
return true if you get to the end of the strings without having returned
false.

Richard.
 
R

RobG

Richard said:
<snip>

Is it a more efficient test? Have you read the ECMA 262 section on
Regular Expressions and thought about what it must take to implement
that? String comparison is not that complex; maybe compare the length
and return false if they don't match, then compare each corresponding
character in turn, returning false at the first non-match (equality
testing between 16 bit integers is trivial for CPUs to do directly), and
return true if you get to the end of the strings without having returned
false.

I'm on lunch, so let's compare:

A. /body/i.test(object.tagName)

to

B. object.tagName.toUpperCase() == 'BODY'

I'll define efficiency as providing the same result while consuming
fewer resources. You may wish to offer some modification of that.

In this case, resources are:

1. Programmer time in keystrokes - A: 27 B: 36
A has 30% fewer keystrokes, therefore it's more efficient for this
criterion.

2. Download bandwidth in characters - A: 27 B: 36
A has 30% fewer characters and is more efficient for this
criterion.

3. Browser execution -
I could compare the two algorithms and see which *should* be
quicker, but that that would just be my opinion of an
implementation and may not have any basis in fact. So I'll do the
pragmatic (and considerably less intellectual) thing and just
write a test and run it through a few of browsers.

Just for fun, I put in a case C that uses a compiled RegExp - the
full script is below.

Results vary by browser - Firefox, Netscape & Mozilla were
virtually identical, so I've just reported Firefox:

A B C
Firefox 360 770 360
IE 950 310 360
Opera 530 820 530

Make what you will of that! For Geko-based browsers and Opera,
the RegExp method is about twice as fast always: for IE, the
string method is nearly 3 times faster unless a compiled RegExp is
used (this points to the others as compiling and caching the
RegExp regardless, but that is pure conjecture).

Since we are talking 100,000 iterations here, it likely makes no
practical difference in most cases. In those cases where it does
matter, a compiled RegExp is just as fast in IE as the string
method and twice as fast in other browsers.

And the number of keystrokes for C is less than for either of the
other two methods - 26 characters (the relevant part is below):

var z = /BODY/i;
z.test(t)

So the worst-case-scenario (using IE of course) is that a compiled
RegExp is about the same speed as using a string with toLoweCase()
(or toUpperCase() as the 'case' may be). And in at least Opera and
Geko browsers you about halve the time of execution.

So given the above, I stand by my statement regarding efficiency.
Over to you.


<script type="text/javascript">
function doTest(t){
var r = 100000;
var i = r, start, end, timeA, timeB, timeC;
var start = new Date().getTime();

while ( /BODY/i.test(t) && i-- ){}
var end = new Date().getTime();
timeA = end - start;

i = r;
start = new Date().getTime();
while ( t.toUpperCase() == 'BODY' && i-- ){}
end = new Date().getTime();
timeB = end - start;

i = r;
start = new Date().getTime();
var z = /BODY/i;
while ( z.test(t) && i-- ){}
var end = new Date().getTime();
timeC = end - start;

document.getElementById('result').innerHTML =
'timeA: ' + timeA + '<br>' +
'timeB: ' + timeB + '<br>' +
'timeC: ' + timeC + '<br>';
}
</script>

<input type="button" value="Do test" onclick="doTest('BODY')"><br>
<span id="result"></span>
 
R

RobG

RobG wrote:
[...]
And the number of keystrokes for C is less than for either of the
other two methods - 26 characters (the relevant part is below):

var z = /BODY/i;
z.test(t)

Ooops, the correct comparison here should have been:

var z = /BODY/i;
z.test(object.tagName)

Which is 38, or the same as the string method.
So the worst-case-scenario (using IE of course) is that a compiled
RegExp is about the same speed as using a string with toLoweCase()
(or toUpperCase() as the 'case' may be). And in at least Opera and
Geko browsers you about halve the time of execution.

So given the above, I stand by my statement regarding efficiency.
Over to you.

This does not allow for the overhead of creating the RegExp, which is
distributed over the 100,000 iterations. If challenged I'll go the
extra yard, but not right now.
 
M

Mick White

RobG wrote:
{snip}
<script type="text/javascript">
function doTest(t){
var r = 100000;
var i = r, start, end, timeA, timeB, timeC;
var start = new Date().getTime();

while ( /BODY/i.test(t) && i-- ){}

I'm curious, Rob, why:
while ( /BODY/i.test(t) && i-- ){}
?
Why the curly brackets?
Mick
 
5

50295

RobG said:
Please don't post code with tabs. Use 2 or 4 spaces for indenting.
Manually wrap code to prevent wrapping errors.

I DID remove the tabs but replaced them with 6 spaces.
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Mon,
18 Apr 2005 11:56:59, seen in Mick White
RobG wrote:

I'm curious, Rob, why:
while ( /BODY/i.test(t) && i-- ){}
?
Why the curly brackets?

If omitted, the while would or could extend onto the next line. But a
test in IE4 suggests that a semicolon is enough, saving two key
depressions.
 
R

RobG

Mick said:
RobG wrote:
{snip}



I'm curious, Rob, why:
while ( /BODY/i.test(t) && i-- ){}
?
Why the curly brackets?
Mick

It makes it obvious to me that the while has no body, others may
use a semi-colon:

while ( /BODY/i.test(t) && i-- );

but there you go. Either the braces or a semi-colon are required in
this case. Is one way better than the other? I don't think it
should matter, but I'm no guru here! :)


[...]
 
M

Mick White

RobG said:
It makes it obvious to me that the while has no body, others may
use a semi-colon:

while ( /BODY/i.test(t) && i-- );

but there you go. Either the braces or a semi-colon are required in
this case. Is one way better than the other? I don't think it
should matter, but I'm no guru here! :)
I see. "timeB" slowest for me. Mac(FF and Safari).
Mick
 
R

Richard Cornford

I'm on lunch, so let's compare:

A. /body/i.test(object.tagName)

to

B. object.tagName.toUpperCase() == 'BODY'

Yes, that is the idea; test the proposition.

Just for fun, I put in a case C that uses a compiled
RegExp - the full script is below.

Worth observing the results (particularly in IE) but not part of the
original proposition.
Results vary by browser - Firefox, Netscape
& Mozilla were virtually identical,

As you would expect given that they all use the same JS engine.
so I've just reported Firefox:

A B C
Firefox 360 770 360
IE 950 310 360
Opera 530 820 530

Make what you will of that! For Geko-based browsers and Opera,
the RegExp method is about twice as fast always: for IE, the
string method is nearly 3 times faster unless a compiled RegExp
is used (this points to the others as compiling and caching the
RegExp regardless, but that is pure conjecture).
So given the above, I stand by my statement regarding
efficiency. Over to you.

My figures don't agree with yours. Taking string comparison as 100% I
get the following (fairly consistently):-

IE 6 Mozilla 1.6 Opera 7.54

(/body/i.test(S)) 328.9% 195.4% 54.2%
("BODY".toUpperCase() == S) 100% 100% 100%

Opera's regular expressions looks fast, or its string manipulation is
appalling, but IE and Mozilla come down in favour of string comparison.

while ( /BODY/i.test(t) && i-- ){}

I thought we were comparing - /body/i - not - /BODY/i -.

while ( t.toUpperCase() == 'BODY' && i-- ){}
<input ... onclick="doTest('BODY')"><br>

Good choice of string for the test ;) String comparisons that match
being the worst-case as the entire string needs to be processed in order
to determine that there is a match, while most non-matching strings will
be reveals by the first character comparison.

Some additional points worth mentioning are that - /body/i - will
produced a false positive if it encounters "TBODY", so the regular
expression - /^body$/i - would be preferable.

Most of the processing in - "BODY".toUpperCase() == S - is in the
implied creation of a String object for the method call, and the
subsequent method call. A comparison of - "BODY" == S - with (case
sensitive) - /^BODY$/.test(S) - demonstrates the extent to which this is
significant:-

IE 6 Mozilla 1.6 Opera 7.54

(/^BODY$/.test(S)) 3002.9% 24257.4% 1176.4%
("BODY" == S) 100% 100% 100%

- But because in HTML DOMs (in reality and by specification) the -
tagName - property is uppercase the overhead in trying to be case
insensitive is futile.

And finally, the most efficient formulation will be:-

if(object == document.body){ ...

My test page, for comparison and verification:-

<html>
<head>
<title></title>
<script type="text/javascript">
var frm = null;
var fncts = ['emptyL','regularExpression','stringComparison'];
var running = false;
function setButtons(bl){
frm['loopLimit'].disabled = bl;
frm["string"].disabled = bl
var sw = frm['bt'];
if(typeof sw.length == 'undefined'){
sw = [sw];
}
for(var c = 0;c < sw.length;c++){
sw[c].disabled = bl;
}
}
function doTests(){
if(!running){
frm = document.forms['f'].elements;
setButtons(true);
frm["Dur0"].value = '';frm["Avr0"].value = '';
for(var c = 1;c < fncts.length;c++){
frm["Dur"+c].value = '';
frm["Avr"+c].value = '';
frm["CAvr"+c].value = '';
frm["PAvr"+c].value = '';
frm["Res"+c].value = '';
}
running = true;
act(0);
}
}

function act(p){
/* setTimeout is used to minimise the occurrences
of 'a script on this page is running slow' dialogs. */
if(p >= fncts.length){
setTimeout('report()',100);
}else{
setTimeout((fncts[p]+'('+p+');'),200);
}
}

function report(){
var lim = +frm['loopLimit'].value;
var emDur = +frm["Dur0"].value
var unaC = (frm["Dur"+(fncts.length-1)].value - emDur) / lim;
frm["CAvr"+(fncts.length-1)].value = unaC;
frm["PAvr"+(fncts.length-1)].value = '100';
for(var c = 1;c < (fncts.length-1);c++){
var evaC = (frm["Dur"+c].value - emDur) / lim;
frm["CAvr"+c].value = evaC;
frm["PAvr"+c].value = ((evaC/unaC)*100);
}
setButtons(false);
running = false;
}

function emptyL(p){
var lim = +frm['loopLimit'].value;
var N, S = frm["string"].value;
var totTime,stTime = new Date().getTime();
for(var c = 0;c < lim;c++){
N = true;
}
totTime = (new Date().getTime() - stTime)
frm["Dur0"].value = totTime;
frm["Avr0"].value = (totTime/lim);
act(p+1);
}

function stringComparison(p){
var lim = +frm['loopLimit'].value;
var N, S = frm["string"].value;
var totTime,stTime = new Date().getTime();
for(var c = 0;c < lim;c++){
N = ("BODY".toUpperCase() == S)
}
totTime = (new Date().getTime() - stTime)
frm["Dur"+p].value = totTime;
frm["Avr"+p].value = (totTime/lim);
frm["Res"+p].value = N;
act(p+1);
}

function regularExpression(p){
var lim = +frm['loopLimit'].value;
var N, S = frm["string"].value;
var totTime,stTime = new Date().getTime();
for(var c = 0;c < lim;c++){
N = (/body/i.test(S));
}
totTime = (new Date().getTime() - stTime)
frm["Dur"+p].value = totTime;
frm["Avr"+p].value = (totTime/lim);
frm["Res"+p].value = N;
act(p+1);
}

</script>
</head>
<body>
<p>
<form name="f" action="#">
Loop Length = <input type="text" value="1300000"
name="loopLimit"> Some browsers will put up an &quot;A script on
this page is making the browser run slowly&quot; dialog. If this
happens the results for the test will be invalid and a shorter loop
will be needed. However, JavaScript Date objects do not tend to be
accurate to less than 10 milliseconds so duration results that are
not different by at least 20 milliseconds (and preferably 100+) are
not necessarily meaningful and a longer loop may be needed to acquire
useful results.<br><br>
Test Value = <input type="text" value="INPUT" name="string"> <br>
<input type="button" value="Test" name="bt" onclick="doTests();">
Repeat tests to reduce/expose the influence of background tasks.
<br><br>
Empty Loop Duration (milliseconds) = <input type="text" value="X"
name="Dur0"><br>
Empty Loop Average (milliseconds) = <input type="text" value="X"
name="Avr0" size="22"><br><br>

<code>(/body/i.test(S))</code> Duration (milliseconds) =
<input type="text" value="X" name="Dur1"><br>
<code>(/body/i.test(S))</code> Average (milliseconds) =
<input type="text" value="X" name="Avr1" size="22"><br>
(result = <input type="text" value="X" name="Res1" size="22">)<br><br>

<code>("BODY".toUpperCase() == S)</code> Duration (milliseconds) =
<input type="text" value="X" name="Dur2"><br>
<code>("BODY".toUpperCase() == S)</code> Average (milliseconds) =
<input type="text" value="X" name="Avr2" size="22"><br>
(result = <input type="text" value="X" name="Res2" size="22">)<br><br>
<br>

<input type="button" value="Test" name="bt" onclick="doTests();">
Repeat tests to reduce/expose the influence of background tasks.
<br><br>

Results: (duration of test - duration of empty loop) / loop length<br>

<code>(/body/i.test(S))</code> Average (milliseconds) =
<input type="text" value="X" name="CAvr1" size="22"><br>
<code>("BODY".toUpperCase() == S)</code> Average (milliseconds) =
<input type="text" value="X" name="CAvr2" size="22"><br>

<br>
Differences (<code>("BODY".toUpperCase() == S)</code> = 100%)<br>

<code>(/body/i.test(S))</code>
<input type="text" value="X" name="PAvr1" size="22">%<br>
<code>("BODY".toUpperCase() == S)</code>
<input type="text" value="X" name="PAvr2" size="22">%<br>
<br>
</form>
</p>
</body>
</html>

Richard.
 
R

RobG

Richard said:
RobG wrote: [...]
A B C
Firefox 360 770 360
IE 950 310 360
Opera 530 820 530

Make what you will of that! For Geko-based browsers and Opera,
the RegExp method is about twice as fast always: for IE, the
string method is nearly 3 times faster unless a compiled RegExp
is used (this points to the others as compiling and caching the
RegExp regardless, but that is pure conjecture).

So given the above, I stand by my statement regarding
efficiency. Over to you.


My figures don't agree with yours. Taking string comparison as 100% I
get the following (fairly consistently):-

IE 6 Mozilla 1.6 Opera 7.54

(/body/i.test(S)) 328.9% 195.4% 54.2%
("BODY".toUpperCase() == S) 100% 100% 100%

Using your page, my results are:

IE 6 Mozilla 1.6 Opera 7.54

(/body/i.test(S)) 300.5% 34.0% 24.6%
("BODY".toUpperCase() == S) 100% 100% 100%


I have no idea why my results vary so much from yours for the non-IE
browsers.
Opera's regular expressions looks fast, or its string manipulation is
appalling, but IE and Mozilla come down in favour of string comparison.

Based on both results, Opera's RegExp is ordinary but string
comparison is awful.
I thought we were comparing - /body/i - not - /BODY/i -.






Good choice of string for the test ;) String comparisons that match
being the worst-case as the entire string needs to be processed in order
to determine that there is a match, while most non-matching strings will
be reveals by the first character comparison.

In practice it appears to make little difference - a bit of testing
showed it's impossible to split the results using 'BODY' or 'body'.

I imagine there are a small number of highly refined algorithms used
in comparison operations that make such differences irrelevant.
Some additional points worth mentioning are that - /body/i - will
produced a false positive if it encounters "TBODY", so the regular
expression - /^body$/i - would be preferable.

Yes, my bad there.
Most of the processing in - "BODY".toUpperCase() == S - is in the
implied creation of a String object for the method call, and the
subsequent method call. A comparison of - "BODY" == S - with (case
sensitive) - /^BODY$/.test(S) - demonstrates the extent to which this is
significant:-

IE 6 Mozilla 1.6 Opera 7.54

(/^BODY$/.test(S)) 3002.9% 24257.4% 1176.4%
("BODY" == S) 100% 100% 100%

- But because in HTML DOMs (in reality and by specification) the -
tagName - property is uppercase the overhead in trying to be case
insensitive is futile.

You're right about tagName, but I don't think the exercise is futile.

The DOM level 2 spec encourages case-insensitivity for element name
comparisons (or conversion and testing with lower case) where
compatibility is required between XHTML and HTML DOM. I guess that
suggestion stuck (Section 1.3).

My other excuse is that there are lots of things that the spec
requires of browsers that aren't fully or universally implemented, so
a bit of "belt 'n braces" seems OK.

Having said all that, removing toUpperCase() from the string
comparison reduces the time by 80% for IE and a staggering 95% for
the 'zillas, making them about 5 times faster than IE. The RegExp
method gets no such benefit if the 'i' is removed.

So if performance is an issue and XHTML compatibility isn't, use a
string comparison without toUpper/LowerCase().
And finally, the most efficient formulation will be:-

if(object == document.body){ ...

HEY! That's cheating! The exercise was string comparison, not
finding DOM elements!

But I guess you're just winding me up. ;-)


[...]
 
R

Richard Cornford

RobG said:
Richard Cornford wrote:

Using your page, my results are:

IE 6 Mozilla 1.6 Opera 7.54

(/body/i.test(S)) 300.5% 34.0% 24.6%
("BODY".toUpperCase() == S) 100% 100% 100%


I have no idea why my results vary so much from yours for
the non-IE browsers.

I would suspect the CPU and/or the OS could potentially make a big
difference. And in this case it looks like the OS makes the difference
as earlier in the week I had a chance to repeat the test on a Windows
2000 machine with otherwise broadly similar hardware (same type of CPU)
and got results similar to yours (Mozilla did much better on that
machine).

You're right about tagName, but I don't think the exercise is
futile.

The DOM level 2 spec encourages case-insensitivity for element
name comparisons (or conversion and testing with lower case)
where compatibility is required between XHTML and HTML DOM.
I guess that suggestion stuck (Section 1.3).

To say "encourage" is a matter of interpretation. The spec states that
if scripts are to be written to work for both HTML and XHTML then such
testing needs to be case insensitive.
My other excuse is that there are lots of things that the spec
requires of browsers that aren't fully or universally implemented,
so a bit of "belt 'n braces" seems OK.

Cautious scripting is generally advisable but 10-30 times worse
performance in exchange is not necessarily a good idea. In this case, I
have not yet encountered (or heard of) an HTML DOM implementation that
has a - tagName - property and does not follow the standard in being
uppercase (including IE 4 which introduced the property, pre-dating the
standard), and planning for the consequences of the browser not
implementing the property at all necessary regardless of its case.

So if performance is an issue and XHTML compatibility isn't,
use a string comparison without toUpper/LowerCase().

When would XHML compatibility be an issue? A document may be either HTML
or XHTML when it gets to the client. The script that accompanies it only
needs to be capable of interacting with one type of DOM.

And do you see people trying to write scripts that function in both
types of DOM? Consider the namespace qualified method on the DOM (the
ones with the "NS" prefix), they should be used in XMTML. How often have
you seen a script that tests for the NS methods in preference to the
non-NS versions and uses those in preference? And if the NS method are
being used in DOMs that may be HTML or XHTML it becomes necessary to
determine which namespace to use with those methods prior to using them.

That is a lot of extra authoring effort, and an overhead in execution,
in a world where XHTML is normally never served to UAs in a commercial
context. (The very few sites that use content negotiation, get it right,
and server variable document types, don't have to serve the same scripts
with each type). So with only a limited need for XHTML client-side
scripts, and then only outside of a public, commercial context,
consequences that follow only form attempting to write cross-DOM scripts
are not worth the consideration of javascript authors. We would be
ill-advised to be attempting to write cross-DOM scripts in the first
place.
HEY! That's cheating! The exercise was string comparison,
not finding DOM elements!

The context was finding DOM elements (or at least confirming that they
had been found).
But I guess you're just winding me up. ;-)

Stirring you up, maybe, not winding. ;)

Richard.
 
R

Richard Cornford

Richard Cornford wrote:
..., and planning for the consequences of the browser
not implementing the property at all necessary regardless of
its case.
<snip>

Well that is gibberish. It should have read:-

"..., and planning for the consequences of the browser not implementing
the property at all is necessary regardless, in any case."

Richard.
 

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,995
Messages
2,570,225
Members
46,815
Latest member
treekmostly22

Latest Threads

Top