opacity

D

David Mark

[snip]
A couple other notes.
The original stand-alone version tested for the existence of the style
object. The recently posted version comes from a module that checks
that elsewhere. So that check should be added.
And seeing that Opera 5/6 refuse to find the html element using gEBTN
or the all object, I updated my code to use the previously posted
check of all[0] and all[1] if the html variable cannot be set any
other way. That branch will only run for Opera 5/6 or antiquated
mobile browsers and is only a couple of extra lines. It is a moot
point for this example if Opera 5/6 do not support opacity, but the
function that finds the html element is crucial for lots of other
feature tests (at least if they are to be run in the head of the
document.)
If this is to be added to your repository, it would be a good
foundation for it and other style-related code to have functions that
find the html element for a given document and determine if the host
is capable of CSS manipulation by testing for the presence of the
element's style object.
Would you mind posting your function that finds the html element?
Okay. I just updated it this morning and haven't had time to test it
thoroughly.
var doc = this.document;
var isRealObject = function(o) {
return !!(o && typeof(o) == 'object');
};
var htmlElement = function(docNode) {
var html;
docNode = docNode || doc;
html = isRealObject(docNode.documentElement)
If docNode.documentElement is defined, will it ever be any type other
than 'object'? It seems like checking this is unnecessary if there has
never been a known problem.
There is the possibility, however remote, that it could be a null
object.

I asked about this sort of testing on c.l.js a when I was first
venturing into feature testing. I thought I'd have to test everything.
If document.getElementsById is present then I'd have to check if it
was callable before calling it. There is a chance it may not be
callable; however, since this situation has never been observed, the

For that host object methods I use:

isFeature = function(o) {
return !!(o && (typeof(o) == 'object' || typeof(o) == 'function'));
};

This is because there is no reliable way to determine if host object
methods are callable.
general feeling was to trust is it callable until it is observed to be
not callable in at least one browser. Feature testing even when no
problems has ever been observed could balloon the feature tests to
hugely paranoid proportions.

I agree.
That said, I understand you may have had the isRealObject function
around for another situation and it was just handy to use in this
case.

Yes, it is used by lots of functions.
[snip]




[0]:null);
if (!html && docNode.all && docNode.all[0]) {
html = docNode.all[(docNode.all[0].tagName == '!')?1:0] || null;
if (html && html.tagName.toLowerCase() != 'html') { html = null; }
}
return html;
};
The missing documentElementsByTagName function is a wrapper for gEBTN
that only exists if the document object features such a method. It
exists to smooth out problems with the "*" parameter, as well as using
the all object for agents that don't feature gEBTN. It isn't really
needed now that the additional clause has been added to handle Opera
5/6, so you should just change it to doc.getElementsByTagName.
When I looked at document.documentElement, document.all.tags and
document.getElementsByTagName, I stated that in IE4
document.all.tags('html').length is 1. That is only true after
window.onload fires. Before window.onload the length is 0. Since
feature testing is preferably done in the head right when the script
loads, the document.all.tags check isn't really enabling any
particular browsers, as far as I know. This goes along with your "It
isn't really needed now...."
Were all of those results determined after onload?
Yes

If so, I would
like to see what they look like when run in the head. Regardless, I
assume that document.all[0] is defined before the onload event fires,
so the added test should be sufficient.

The added test seems to be fine in IE4 at least with the HTML page I
tested.

That's good to know.
Can you think of an example of a browser without
document.documentElement where style-related functions would work if
the document.all fallback is used for testing? Do the entire scripts
associated with these style-related functions work in these browsers?

For example, a function that shows and/or hides elements. The
requirements are that CSS is supported (style object exists) and
visibility can be adjusted by script (the visibility property of the
style object is a string.) I don't think it would make sense to
exclude IE4, Opera 5/6, etc. from using such a function. Another
example is a function that adds and/or removes elements from the
layout, which requires a working display property. Also, a function
that attaches a listener for drag and drop operations requires a
working position property (assuming it needs to work with statically
positioned elements.) All three of these examples can be written to
work with IE4, and two of the three can work with Opera 5/6 (IIRC,
those browsers cannot update the display property.)
Also, there is no reason why such functions can't run after
the body has been parsed (e.g. in a real or simulated DOMContentLoaded
listener.) I do that for several functions that have to create
elements and append them to the body to detect quirks like Opera's
botched offsetLeft/Top reporting. It seems like setOpacity is
something that wouldn't be needed until the document is ready, else
how would you reliably find the element you want to fade?

Since document.all[0] or document.all[1] seems to work in the head
this point may be moot.

For these examples yes. Other functionality (e.g. calculating an
element's offset position) require feature detection to occur after
the closing body tag has been parsed (thanks to IE's ridiculous
"Operation Aborted" bug.) I haven't found this to be a hindrance as I
never reference DOM nodes before the document is ready anyway (let
alone try to compute their position.)
[snip]
Sort of. How would a script determine if CSS is even available when
running in IE4, Opera 6, etc.? One of the first flags I set is based
on the presence of a style object. If it doesn't exist then lots of
unneeded feature testing is skipped and style-related functions are
not created. Next I check the types of the display, visibility and
position properties of the object (typically strings, but not always)
and those three results determine which style-related modules should
be initialized.

This is exactly the direction in which I'd like to go with this code
repository.

Seeing as you are like-minded and are willing to handle the
documentation and administration of the project, I am willing to help
you fill it up one function at a time. I've got a module for
virtually everything and they all use similar feature detection. It
will take a while, but eventually the result will be a competent
alternative to dreck like jQuery. I for one am tired of seeing
constant script errors in IE every time I browse the Web. I can't
imagine what the Web is like in less-supported agents. It must be
hell for mobile users, people who never upgrade their browsers, Mac
users, etc.
They are also referenced by applications to determine
if it is possible to hide dynamically replaced content during the page
load (i.e. if a style rule is added before the body is parsed, will it
be possible to overrule it with an inline style once the content is
replaced after parsing.) Can you confirm if IE4 can get the HTML
element via document.all[0] (or document.all[1]?)

It can.

That's good to know.
How many of your pages have enabled JavaScript functionality because
of these archaic checks and how many actual visitors benefit? If there

I can't get IE4 to load in XP for some reason and AFAIK, nobody uses
anything I write with IE4 (or Opera 5/6.) I figure a couple of extra
lines doesn't hurt and allows very simple enhancements (e.g. hiding
and showing elements) to work with older mobile devices.
are pages that do become enabled with these checks that would not
become enabled with the modern checks only, it still may be better
overall (faster download times, less code to maintain) to just slide
these down the degradation path given how few users benefit. They
still do get a perfectly working HTML page, after all.

Yes, I agree with that, especially since most widgets I build with my
API have requirements that are beyond the ancient browsers (e.g.
Function.prototype.call.) So despite the fact that basic style-
related functions are available to browsers like IE4, they typically
don't get the chance to use them (the test for call short circuits the
widgets' initialization.)
I think setting the feature threshold at document.getElementById and
document.documentElement is ok today. If there is outrage over this
then the code repository could allow for the the document.all
fallbacks. I cringe a little when I type that thought. I strongly vote
we go "modern" (NN6 November 2000, IE5.5 July 2000).

I'd say that makes sense. I can't remember when Opera 7 was released
though. It seems like it was a two or three years into the century.
I've heard that some older mobile devices use an Opera 6 image, but
those will probably all be in landfills soon (if not already.)
 
P

Peter Michaux

[snip]
Can you think of an example of a browser without
document.documentElement where style-related functions would work if
the document.all fallback is used for testing? Do the entire scripts
associated with these style-related functions work in these browsers?

For example, a function that shows and/or hides elements. The
requirements are that CSS is supported (style object exists) and
visibility can be adjusted by script (the visibility property of the
style object is a string.) I don't think it would make sense to
exclude IE4, Opera 5/6, etc. from using such a function.

I know what you mean. The feature threshold has to be placed somewhere
in the history of browsers. Right around the border where the browsers
transition to a standard is tricky to decide where to draw the line.

Another
example is a function that adds and/or removes elements from the
layout, which requires a working display property. Also, a function
that attaches a listener for drag and drop operations requires a
working position property (assuming it needs to work with statically
positioned elements.) All three of these examples can be written to
work with IE4, and two of the three can work with Opera 5/6 (IIRC,
those browsers cannot update the display property.)
[snip]
This is exactly the direction in which I'd like to go with this code
repository.

Seeing as you are like-minded and are willing to handle the
documentation and administration of the project, I am willing to help
you fill it up one function at a time.
Great!


I've got a module for
virtually everything and they all use similar feature detection. It
will take a while,

Yes it will. Fortunately there is no particular hurry.

but eventually the result will be a competent
alternative to dreck like jQuery. I for one am tired of seeing
constant script errors in IE every time I browse the Web. I can't
imagine what the Web is like in less-supported agents. It must be
hell for mobile users, people who never upgrade their browsers, Mac
users, etc.
[snip]

I can't get IE4 to load in XP

The IE4 program loads for me. To get the program to load a page I have
to use the File > Open menu option and type the url into the dialog
and press OK. The URL never shows in the browser's URL bar.

for some reason and AFAIK, nobody uses
anything I write with IE4 (or Opera 5/6.) I figure a couple of extra
lines doesn't hurt and allows very simple enhancements (e.g. hiding
and showing elements) to work with older mobile devices.


Yes, I agree with that, especially since most widgets I build with my
API have requirements that are beyond the ancient browsers (e.g.
Function.prototype.call.) So despite the fact that basic style-
related functions are available to browsers like IE4, they typically
don't get the chance to use them (the test for call short circuits the
widgets' initialization.)




I'd say that makes sense. I can't remember when Opera 7 was released
though.

January 1, 2003

It seems like it was a two or three years into the century.
I've heard that some older mobile devices use an Opera 6 image, but
those will probably all be in landfills soon (if not already.)

I use try-catch and Randy Webb's cell phone only updated last month to
support that. It didn't cause me any grief knowing a syntax error
would occur on his browser. Go back far enough and almost any new
script will syntax error in some ancient browser.

I'm going to go with just checking document.documentElement for now
and if we see this was a particularly bad choice we can change in the
future.

Peter
 
D

David Mark

On Dec 4, 8:22 pm, Peter Michaux <[email protected]> wrote:
[snip]




Of the browsers I checked that can set opacity, only IE4 and Opera 6-
don't have document.documentElement. I don't mind degrading those
browsers to not use opacity setting. This would make the setOpacity
and all other functions requiring an element for feature testing not
dependent on an htmlElement function like you've outlined. In my eyes,
Opacity yes, but all other style-related functions? That seems
extreme.
Can you think of an example of a browser without
document.documentElement where style-related functions would work if
the document.all fallback is used for testing? Do the entire scripts
associated with these style-related functions work in these browsers?
For example, a function that shows and/or hides elements. The
requirements are that CSS is supported (style object exists) and
visibility can be adjusted by script (the visibility property of the
style object is a string.) I don't think it would make sense to
exclude IE4, Opera 5/6, etc. from using such a function.

I know what you mean. The feature threshold has to be placed somewhere
in the history of browsers. Right around the border where the browsers
transition to a standard is tricky to decide where to draw the line.
Another
example is a function that adds and/or removes elements from the
layout, which requires a working display property. Also, a function
that attaches a listener for drag and drop operations requires a
working position property (assuming it needs to work with statically
positioned elements.) All three of these examples can be written to
work with IE4, and two of the three can work with Opera 5/6 (IIRC,
those browsers cannot update the display property.)
[snip]




only checking for document.documentElement is a reasonable place to
draw the line in browser history given the approximate distribution of
browser version in use today. Any non-trivial CSS that may need the
fallbacks in the htmlElement function for a feature tests likely won't
work in IE4 anyway. Sound reasonable?
Sort of. How would a script determine if CSS is even available when
running in IE4, Opera 6, etc.? One of the first flags I set is based
on the presence of a style object. If it doesn't exist then lots of
unneeded feature testing is skipped and style-related functions are
not created. Next I check the types of the display, visibility and
position properties of the object (typically strings, but not always)
and those three results determine which style-related modules should
be initialized.
This is exactly the direction in which I'd like to go with this code
repository.
Seeing as you are like-minded and are willing to handle the
documentation and administration of the project, I am willing to help
you fill it up one function at a time.
Great!

I've got a module for
virtually everything and they all use similar feature detection. It
will take a while,

Yes it will. Fortunately there is no particular hurry.

I agree. The Web is already such a mess of misguided scripts and
misinformation that it would take years to make a significant impact,
even if a complete alternative to Prototype, jQuery, etc. existed
yesterday. Those miserable things have got a lot of momentum, with
people writing books about them, building widgets on top of them, etc.
but eventually the result will be a competent
alternative to dreck like jQuery. I for one am tired of seeing
constant script errors in IE every time I browse the Web. I can't
imagine what the Web is like in less-supported agents. It must be
hell for mobile users, people who never upgrade their browsers, Mac
users, etc.
[snip]

I can't get IE4 to load in XP

The IE4 program loads for me. To get the program to load a page I have
to use the File > Open menu option and type the url into the dialog
and press OK. The URL never shows in the browser's URL bar.

I have IE4, 5, 5.5 and 6 on one machine and all but 4 load and run. 4
blows up instantly (I don't even see the interface.) One interesting
thing with 5.x is that neither can do opacity manipulations in this
environment (even on Microsoft's test pages.) One throws a "library
not registered" error and the other fails silently.
January 1, 2003



I use try-catch and Randy Webb's cell phone only updated last month to
support that. It didn't cause me any grief knowing a syntax error
would occur on his browser. Go back far enough and almost any new
script will syntax error in some ancient browser.

Only a couple of my modules (Ajax and Flash) use try/catch and I never
concatenate them with scripts that do not use try/catch, so that if
they fail to parse, they don't take other modules with them.
I'm going to go with just checking document.documentElement for now
and if we see this was a particularly bad choice we can change in the
future.

As there will have to be a common initialization script to avoid
duplication of code, I recommend something like:

var htmlElement, isStyleCapable, doc = this.document;
if (doc) {
htmlElement = function(docNode) {
return (docNode || doc).documentElement;
};
isStyleCapable = function(docNode) {
var html = htmlElement(docNode);
return !!(html && html.style);
};
}

That way one could replace the htmlElement function like all of the
rest if backward compatibility with IE4, Opera 5, etc. is a
requirement. Same goes for gEBTN and gEBI. The difference with the
two document methods is that other low-level library functions don't
use them (they come into play at the widget level), so the wrappers
for them could be optional.
 
P

Peter Michaux

On Dec 4, 12:17 am, (e-mail address removed) wrote:

[this quoted code is a composite of David Mark's code snips and
changes]
setOpacity = (function(el) {

var i,l;
var reOpacity = new RegExp('alpha\\(opacity=[^\\)]+\\)', 'i');
var opacityStyles = ['opacity', 'MozOpacity', 'KhtmlOpacity'];

if (typeof el.style.filter == 'string') {

return function(el, o) {

var f;

if (el.filters) {
if (!el.currentStyle || !el.currentStyle.hasLayout) {
el.style.zoom = 1;
}

f = el.filters.alpha;
if (f) {
// IE screws up some fonts when filter is leftenabled
f.enabled = o != 1;
// This has got to be faster than string concatenation
f.opacity = o * 100;
}
else { // IE6/7 never get here unless an opacity has never been set
s = 'alpha(opacity=' + (o * 100) + ')';
if (reOpacity.test(el.style.filter)) { // IE5 every time
el.style.filter = el.style.filter.replace(reOpacity, s);
}
else { // IE6/7 once
el.style.filter += s;
}

}

}
};

}

i = 0;

l = opacityStyles.length;

while (i < l) {
if (typeof(el.style[opacityStyles]) == 'string') {
return (function(s) {
return function(el, o) {
el.style = (o < .00001)?0:eek:;
};
})(opacityStyles);
}
i++;
}

})(html);



I propose the following code

var setOpacity = (function() {

var html,
// Reverse order in next array for use with reverse for loop.
// Saves a length variable in the loop and a few characters.
props = ['KhtmlOpacity', 'MozOpacity', 'opacity'];

if (typeof document != 'undefined' &&
(html = document.documentElement) &&
html.style) {

if (typeof html.style.filter == 'string' &&
html.currentStyle &&
''.replace) {

return function(el, o) {

if (!el.currentStyle.hasLayout) {
el.style.zoom = 1;
}

var s = el.style;
s.filter = s.filter.replace(/alpha\([^\)]*\)/gi,'') +
((o < 1) ? 'alpha(opacity='+o*100+')' : '');

};

}

for (var i=props.length; i--; ) {
var p = props;
if (typeof(html.style[p]) == 'string') {
return function(el, o) {
el.style[p] = (o < .00001) ? 0 : o;
};
}
}
}

})();

I just quickly tested this code in FF2, S3, O9, S1.2, FF1, IE5, IE5.5,
NN6.2, NN7 and it worked nicely. In a sampling of slightly older
browsers there were no errors and the setOpacity function was left
undefined.

Some identifiers have been shortened compared with the quoted code
above.

I decided to use regular expression literals. I think that, as a
policy, it is not a good idea to be shy of ECMAScript edition 3 syntax
unless it is known to cause a problem in a quite recent browser (IE5+,
NN6+). For example, NN4 syntax errors reasons other than the regexp
literal anyway.

By only using document.documentElement and not a document.all fallback
to find the root element for feature testing. Of the browsers known to
have opacity setting ability this only sacrifices IE4. Because of this
known sacrifice I made the html.currentStyle object mandatory for the
filters branch.

I'm surprised something like "(o < .00001) ? 0 : o" is not needed in
the filters branch. I seems ok to use "alpha(opacity=1e-7)".

The f.enabled code has not been used because it errors in IE5.5. The
error message said something like "library not installed". I also
don't think that the time taken for the string concatenation would be
substantial compared to the time to render the element with changed
opacity. RobG and I did some testing about a year ago and the
rendering time seemed to eclipse any actual code running time. This
really needs formal testing but this certainly was impression. RobG
suggested the graphics card may be a substantial factor. (It may be
that with this method of setting opacity that the issue about font
rendering in IE is not a problem?) The error in IE5.5 was the
fundamental reason for not using the f.enabled code.

The feature testing from David's quoted code above seems great. I
hadn't used any feature testing on my old opacity code because I had
never looked into feature testing with CSS related properties. I'd
like to know why, for the browsers using the non-filters part, that
even when the opacity of an element has not been set the
html.style[props] object is a string. This really does seem to be
constantly implemented in the browsers that support opacity. Is this
part of some standard?

In the non-filters part of the code the the "(function(s){...})
(opacityStyles)" wrapper isn't particularly needed. In the code
I've proposed the "p" variable will never be changed again after the
setOpacity function is created.

Way too much thinking about opacity...and opacity setting is supposed
to be a picnic?

Peter
 
D

David Mark

On Dec 4, 12:17 am, (e-mail address removed) wrote:

[this quoted code is a composite of David Mark's code snips and
changes]




setOpacity = (function(el) {
var i,l;
var reOpacity = new RegExp('alpha\\(opacity=[^\\)]+\\)', 'i');
var opacityStyles = ['opacity', 'MozOpacity', 'KhtmlOpacity'];
if (typeof el.style.filter == 'string') {
return function(el, o) {
var f;
if (el.filters) {
if (!el.currentStyle || !el.currentStyle.hasLayout) {
el.style.zoom = 1;
}
f = el.filters.alpha;
if (f) {
// IE screws up some fonts when filter is leftenabled
f.enabled = o != 1;
// This has got to be faster than string concatenation
f.opacity = o * 100;
}
else { // IE6/7 never get here unless an opacity has never been set
s = 'alpha(opacity=' + (o * 100) + ')';
if (reOpacity.test(el.style.filter)) { // IE5 every time
el.style.filter = el.style.filter.replace(reOpacity, s);
}
else { // IE6/7 once
el.style.filter += s;
}
}
}
};
}
i = 0;
l = opacityStyles.length;
while (i < l) {
if (typeof(el.style[opacityStyles]) == 'string') {
return (function(s) {
return function(el, o) {
el.style = (o < .00001)?0:eek:;
};
})(opacityStyles);
}
i++;
}
})(html);


I propose the following code

var setOpacity = (function() {

var html,
// Reverse order in next array for use with reverse for loop.
// Saves a length variable in the loop and a few characters.
props = ['KhtmlOpacity', 'MozOpacity', 'opacity'];


Makes sense.
if (typeof document != 'undefined' &&
(html = document.documentElement) &&
html.style) {

I prefer:

var doc = this.document;
if (doc && doc.documentElement) {
html = this.documentElement;
if (html.style) {

I concur with JSLint that assignments in conditions are a bad
practice. I also think that the creation of doc and html should
definitely be in a seperate (and required) snippet. You will find
that virtually every function that feature tests will need them.
if (typeof html.style.filter == 'string' &&
html.currentStyle &&
''.replace) {

I don't think you need the test for replace, especially since the
currentStyle test precedes it.
return function(el, o) {

if (!el.currentStyle.hasLayout) {
el.style.zoom = 1;
}

var s = el.style;
s.filter = s.filter.replace(/alpha\([^\)]*\)/gi,'') +
((o < 1) ? 'alpha(opacity='+o*100+')' : '');

};

}

for (var i=props.length; i--; ) {

Personally I prefer:

var i = props.length;
while (i--) {

I don't think that one needs to be debated though as it is just a
preference.
var p = props;
if (typeof(html.style[p]) == 'string') {
return function(el, o) {
el.style[p] = (o < .00001) ? 0 : o;
};
}


Works for me.
}
}

})();

I just quickly tested this code in FF2, S3, O9, S1.2, FF1, IE5, IE5.5,
NN6.2, NN7 and it worked nicely. In a sampling of slightly older

I had previously tested the original version in all but the old Safari
browser. Can multiple versions of that co-exist on the same box?
browsers there were no errors and the setOpacity function was left
undefined.

Some identifiers have been shortened compared with the quoted code
above.

I decided to use regular expression literals. I think that, as a
policy, it is not a good idea to be shy of ECMAScript edition 3 syntax
unless it is known to cause a problem in a quite recent browser (IE5+,
NN6+). For example, NN4 syntax errors reasons other than the regexp
literal anyway.

I agree that NN4 should not be a consideration.
By only using document.documentElement and not a document.all fallback
to find the root element for feature testing. Of the browsers known to
have opacity setting ability this only sacrifices IE4. Because of this
known sacrifice I made the html.currentStyle object mandatory for the
filters branch.

Okay. But I still think it makes sense to use a one-line function to
return doc.documentElement. Then if somebody really wants to support
IE4, they can swap it for the version that checks doc.all.
I'm surprised something like "(o < .00001) ? 0 : o" is not needed in
the filters branch. I seems ok to use "alpha(opacity=1e-7)".
Right.


The f.enabled code has not been used because it errors in IE5.5. The
error message said something like "library not installed". I also

So it worked without the f.enabled line? That surprises me. I got
the same errors on the MSDN opacity demos and chalked it up to IE5.5
co-existing with IE6 on XP.
don't think that the time taken for the string concatenation would be
substantial compared to the time to render the element with changed
opacity. RobG and I did some testing about a year ago and the

I don't know about that. The DirectX manipulation should be very
fast. It probably isn't worth worrying about until special effect
transitions are added.
rendering time seemed to eclipse any actual code running time. This
really needs formal testing but this certainly was impression. RobG
suggested the graphics card may be a substantial factor. (It may be

Yes it would.
that with this method of setting opacity that the issue about font
rendering in IE is not a problem?) The error in IE5.5 was the
fundamental reason for not using the f.enabled code.

I don't think it will affect the font rendering issue. I'll try
updating mine to skip the part you removed and see if it makes a
difference.
The feature testing from David's quoted code above seems great. I
hadn't used any feature testing on my old opacity code because I had
never looked into feature testing with CSS related properties. I'd
like to know why, for the browsers using the non-filters part, that
even when the opacity of an element has not been set the
html.style[props] object is a string. This really does seem to be


Because an inline style can be an empty string, which means that only
cascaded styles are applied to the element. If the property were
undefined, that would indicate that the agent doesn't support that
inline style.
constantly implemented in the browsers that support opacity. Is this
part of some standard?

Ask Richard Cornford. It was his idea.
In the non-filters part of the code the the "(function(s){...})
(opacityStyles)" wrapper isn't particularly needed. In the code
I've proposed the "p" variable will never be changed again after the
setOpacity function is created.


Makes sense.
Way too much thinking about opacity...and opacity setting is supposed
to be a picnic?

It might seem that way, but in fact issues larger than opacity are
being worked out. That is why I keep mentioning the doc variable and
htmlElement function as virtually every other style-related function
that comes after this will need them. I think the next logical step
is getOpacity; however, that one references my getComputedStyle
wrapper (when available.) So once we get the central support details
worked out, I think we would deal with getComputedStyle. It happens I
recently worked on that function to untangle cascaded and computed
styles (where possible) and I will be interested to hear feedback on
it. First things first though. Looking over the code for that one, I
see dependant functions that reference doc. Example:

elementDocument = function(el) { return el.ownerDocument || doc; };

As you can see, it needs the doc reference and like the htmlElement
function, is designed to work in any document context, which enables
its use with documents pulled down with Ajax, documents in IFrames,
etc.

This is why I propose this function, which shortens virtually all of
the other functions. As it is referenced in so many places, I think
it makes sense to allow for the possibility of multiple documents.

htmlElement = function(docNode) { return (docNode ||
doc).documentElement; };

Another depencency of getComputedStyle (one branch of it anyway) is
elementParent, which I have never been particularly confident in:

api.elementParent = function(el) { return el.parentNode ||
el.parentElement; };

I have no idea if ancient versions of IE (or anything else) fail to
support parentNode or if parentElement is a proper substitute in all
cases, but I have used that code for years without incident.

Anyway, I think it is apparent that virtually none of these functions
will be able to exist as islands, without a lot of repetitive code. I
don't know of a simple build process that can consolidate such
repetition, so I think it is best to avoid it from the start.
 
P

Peter Michaux

On Dec 6, 2:01 am, Peter Michaux <[email protected]> wrote:

[snip proposed code and discussion of personal style. This could go on
forever :-S]
I had previously tested the original version in all but the old Safari
browser. Can multiple versions of that co-exist on the same box?

Multiple versions of Safari 1.3- can exist on a OS X 10.3 box.
Multiple versions of Safari 2.0+ can exist on a OS X 10.4 box.

<URL: http://michelf.com/projects/multi-safari/>

[snip]

So it worked without the f.enabled line?

The f.enabled and f.opacity lines will both case the "library not
installed" error.

That surprises me. I got
the same errors on the MSDN opacity demos and chalked it up to IE5.5
co-existing with IE6 on XP.

It could be.

With regard to the proposed setOpacity code, even if your code works
with the f.enabled and f.opacity and a single IE 5.5 install, it
doesn't work with either of our installs or likely with any web
developer's mutliple IE installs. So, in fact, the proposed code
without f.enabled actually works in a wider set of browsers/os/
installation combinations. Also, I would definitely like to have code
that I can verify works. My proposed code tests successfully on my
install.

[snip]

It might seem that way, but in fact issues larger than opacity are
being worked out. That is why I keep mentioning the doc variable and
htmlElement function as virtually every other style-related function
that comes after this will need them. I think the next logical step
is getOpacity; however, that one references my getComputedStyle
wrapper (when available.)

After the holidays ;-)

So once we get the central support details
worked out, I think we would deal with getComputedStyle. It happens I
recently worked on that function to untangle cascaded and computed
styles (where possible) and I will be interested to hear feedback on
it. First things first though. Looking over the code for that one, I
see dependant functions that reference doc. Example:

elementDocument = function(el) { return el.ownerDocument || doc; };

As you can see, it needs the doc reference and like the htmlElement
function, is designed to work in any document context, which enables
its use with documents pulled down with Ajax, documents in IFrames,
etc.

This is why I propose this function, which shortens virtually all of
the other functions. As it is referenced in so many places, I think
it makes sense to allow for the possibility of multiple documents.

htmlElement = function(docNode) { return (docNode ||
doc).documentElement; };

[snip]

You seem convinced and your work with many of these CSS related
functions far out weighs mine.

How about this...

if (typeof document != 'undefined') {

var getHtmlElement = function(docNode) {
return (docNode || document).documentElement;
};

}

The two changes I made above:

I still don't understand the need for your "doc" variable. There is
already a "document" host object for that.

I think the function should be called "getHtmlElement" or
"getDocumentElement". The former options is shorter so I used it
above.



My proposed code then changes to the following

if (typeof getHtmlElement != 'undefined') {

var setOpacity = (function() {

var html = getHtmlElement(),
props = ['KhtmlOpacity', 'MozOpacity', 'opacity'];

if (html &&
html.style) {

if (typeof html.style.filter == 'string' &&
''.replace) {

return function(el, o) {

if (!el.currentStyle ||
!el.currentStyle.hasLayout) {
el.style.zoom = 1;
}

var s = el.style;
s.filter = s.filter.replace(/alpha\([^\)]*\)/gi,'') +
((o < 1) ? 'alpha(opacity='+o*100+')' : '');

};

}

for (var i=props.length; i--; ) {
var p = props;
if (typeof(html.style[p]) == 'string') {
return function(el, o) {
el.style[p] = (o < .00001) ? 0 : o;
};
}
}
}

})();

}


If a developer wants to use a getHTMLElement that works with IE4 then
this will work with IE4 and I'll test that later.

Peter
 
T

Thomas 'PointedEars' Lahn

David said:
For that host object methods I use:

isFeature = function(o) { return !!(o && (typeof(o) == 'object' ||
typeof(o) == 'function')); };

But evaluating the reference before testing its type is more error-prone
than doing it vice-versa. Hence my isMethodType() approach.
This is because there is no reliable way to determine if host object
methods are callable.

It would seem there is. There does not seem to be a reliable way yet to
determine whether or not an arbitrary value is callable, though. However,
in reasonably written no-debug scripts the latter question will never come up.
For example, a function that shows and/or hides elements. The
requirements are that CSS is supported (style object exists) and
visibility can be adjusted by script (the visibility property of the
style object is a string.)

Showing or hiding elements is ambiguous. There are *two* CSS properties and
therefore two properties of "style objects" that are to consider: `display'
and `visibility'.
I don't think it would make sense to exclude IE4, Opera 5/6, etc. from
using such a function.

Or NN4 which does not need a "style object" for that.


PointedEars
 
D

David Mark

On Dec 6, 2:01 am, Peter Michaux <[email protected]> wrote:

[snip proposed code and discussion of personal style. This could go on
forever :-S]
I had previously tested the original version in all but the old Safari
browser. Can multiple versions of that co-exist on the same box?

Multiple versions of Safari 1.3- can exist on a OS X 10.3 box.
Multiple versions of Safari 2.0+ can exist on a OS X 10.4 box.

<URL:http://michelf.com/projects/multi-safari/>

[snip]
So it worked without the f.enabled line?

The f.enabled and f.opacity lines will both case the "library not
installed" error.

Right. It would work in standard IE5.5 installations.
It could be.

It is, but I agree that it makes sense to make it work for developers,
not to mention for your own testing.
With regard to the proposed setOpacity code, even if your code works
with the f.enabled and f.opacity and a single IE 5.5 install, it
doesn't work with either of our installs or likely with any web
developer's mutliple IE installs. So, in fact, the proposed code
without f.enabled actually works in a wider set of browsers/os/
installation combinations. Also, I would definitely like to have code
that I can verify works. My proposed code tests successfully on my
install.

[snip]
It might seem that way, but in fact issues larger than opacity are
being worked out. That is why I keep mentioning the doc variable and
htmlElement function as virtually every other style-related function
that comes after this will need them. I think the next logical step
is getOpacity; however, that one references my getComputedStyle
wrapper (when available.)

After the holidays ;-)


So once we get the central support details
worked out, I think we would deal with getComputedStyle. It happens I
recently worked on that function to untangle cascaded and computed
styles (where possible) and I will be interested to hear feedback on
it. First things first though. Looking over the code for that one, I
see dependant functions that reference doc. Example:
elementDocument = function(el) { return el.ownerDocument || doc; };
As you can see, it needs the doc reference and like the htmlElement
function, is designed to work in any document context, which enables
its use with documents pulled down with Ajax, documents in IFrames,
etc.
This is why I propose this function, which shortens virtually all of
the other functions. As it is referenced in so many places, I think
it makes sense to allow for the possibility of multiple documents.
htmlElement = function(docNode) { return (docNode ||
doc).documentElement; };

[snip]

You seem convinced and your work with many of these CSS related
functions far out weighs mine.

It will become apparent on adding additional style functions (or DOM,
events, etc.)
How about this...

if (typeof document != 'undefined') {

var getHtmlElement = function(docNode) {
return (docNode || document).documentElement;
};

}

That looks good.
The two changes I made above:

I still don't understand the need for your "doc" variable. There is
already a "document" host object for that.

One reason is that it is shorter than this.document. Referring to it
as "document" alone implies that such a global property exists
(typically a good assumption in a browser, but not the best practice
as it will error otherwise.) I think it is a good idea to run all of
these functions through JSLint as it is good for pointing out
potential pitfalls.
I think the function should be called "getHtmlElement" or
"getDocumentElement". The former options is shorter so I used it
above.

getHtmlElement (or getHTMLElement) sounds good to me, though if that
becomes the standard for the library, every function that fetches a
property will have to be start with "get." I typically use a "set"
prefix on functions that set properties, which differentiates them
from the more numerous getters. I have a function that dumps all
defined methods to the console (and/or a supplied element), sorted by
name, and the output would be harder to wade through if everything
started with "g" or "s". Another popular alternative (which I don't
care for) is to combine get/set in one function and branch on the
number of arguments passed.
My proposed code then changes to the following

if (typeof getHtmlElement != 'undefined') {

var setOpacity = (function() {

var html = getHtmlElement(),
props = ['KhtmlOpacity', 'MozOpacity', 'opacity'];

if (html &&
html.style) {

if (typeof html.style.filter == 'string' &&
''.replace) {

return function(el, o) {

if (!el.currentStyle ||
!el.currentStyle.hasLayout) {
el.style.zoom = 1;
}

var s = el.style;
s.filter = s.filter.replace(/alpha\([^\)]*\)/gi,'') +
((o < 1) ? 'alpha(opacity='+o*100+')' : '');

};

}

for (var i=props.length; i--; ) {
var p = props;
if (typeof(html.style[p]) == 'string') {
return function(el, o) {
el.style[p] = (o < .00001) ? 0 : o;
};
}
}
}

})();

}

If a developer wants to use a getHTMLElement that works with IE4 then
this will work with IE4 and I'll test that later.


Makes sense to me. You've got the code for the alternative
getHTMLElement (or getHtmlElement as it is spelled in the code.) My
preference for the name would have HTML in caps, but only because that
is the convention I use.
 
D

David Mark

But evaluating the reference before testing its type is more error-prone
than doing it vice-versa. Hence my isMethodType() approach.

I don't see that. The reference is evaluated before the function ever
runs (when it is passed.) That's why I don't check for typeof
'unknown' in that function as it would error when passing the
reference to the function.
It would seem there is. There does not seem to be a reliable way yet to

How do you figure?
determine whether or not an arbitrary value is callable, though. However,

As long as it isn't a host object, typeof(arbitrary) == 'function'
should suffice.
in reasonably written no-debug scripts the latter question will never come up.
Agreed.



Showing or hiding elements is ambiguous. There are *two* CSS properties and
therefore two properties of "style objects" that are to consider: `display'
and `visibility'.

And two different functions in a competently written library. Only
one has to do with logical visibility. The other removes elements
from the layout (or changes their display mode), which does indeed
make them invisible to the eye, but logically they are still visible.
One wouldn't consider setting an element's opacity to 0 or moving it
to negative coordinates to hide it either, despite the fact that the
element becomes empirically invisible as a result.

We'll talk about this further when we get to the toggle-related
functions.
Or NN4 which does not need a "style object" for that.

Never mind that one. It can make due without such scripted
enhancements.
 
T

Thomas 'PointedEars' Lahn

David said:
I don't see that. The reference is evaluated before the function ever
runs (when it is passed.) That's why I don't check for typeof
'unknown' in that function as it would error when passing the
reference to the function.

Which is the problem with that approach. You overlook the possibility that
the argument may have to be an unqualified reference or can be a qualified
reference to a host object. Evaluating that reference before the call is
error-prone, especially as a host object may implement the [[Get]] method in
any way the implementor saw fit. Unqualified references are necessary at
times, and there are host objects that are callable but throw an exception
on read access.
How do you figure?

Because evaluating the type before and trying read access only if the type
indicates feasibility has not caused unexpected behavior so far.
As long as it isn't a host object, typeof(arbitrary) == 'function'
should suffice.

No, it should not. See above.
Never mind that one. It can make due without such scripted
enhancements.

That is a choice that can be made. However, supporting document.all and
not supporting NN4's style properties at the same time appears a bit weird.
My display() and visibility() methods in
http://PointedEars.de/scripts/dhtml.js support NN4 as well, with little
extra effort.


PointedEars
 
D

David Mark

I don't see that. The reference is evaluated before the function ever
runs (when it is passed.) That's why I don't check for typeof
'unknown' in that function as it would error when passing the
reference to the function.

Which is the problem with that approach. You overlook the possibility that
the argument may have to be an unqualified reference or can be a qualified
reference to a host object. Evaluating that reference before the call is
error-prone, especially as a host object may implement the [[Get]] method in
any way the implementor saw fit. Unqualified references are necessary at
times, and there are host objects that are callable but throw an exception
on read access.

I know that and they will throw that exception when passed to the test
function, which means that the test function will never evaluate such
objects.
Because evaluating the type before and trying read access only if the type
indicates feasibility has not caused unexpected behavior so far.

And if you get typeof(o) == 'object', how does that tell you it is
callable?
No, it should not. See above.

See what above? If a non-host object is of type function, then it has
an internal [[call]] method and is therefore callable.
That is a choice that can be made. However, supporting document.all and
not supporting NN4's style properties at the same time appears a bit weird.

Doesn't appear that way to me.
My display() and visibility() methods inhttp://PointedEars.de/scripts/dhtml.jssupport NN4 as well, with little
extra effort.

I am aware of the syntax. I pruned it out of my stock show/hide code
years ago, along with any reference to layers. They were just taking
up space.
 
P

Peter Michaux

[snip]
How about this...
if (typeof document != 'undefined') {
var getHtmlElement = function(docNode) {
return (docNode || document).documentElement;
};


Testing in IE4 with the document.all fallback showed some problems.

While the script is running in the head

document.all[0] // !
document.all[1] // HEAD

after the window.onload event

document.all[0] // !
document.all[1] // HTML

That seems to make it impossible to use the html element to check for
the style object while head scripts are evaluated. However it is just
as easy to use the head element to check for the style object. I
prefer to do feature tests in the head as the code is first evaluated
if possible. An entire Ajax library can be feature tested in the head
but we need one element for form serialization to check for the
attributes property.

The only change this requires is changing the name from
"getHtmlElement" to "getAnElement" which actually make makes more
semantic sense. It isn't really that we want the documentElement in
particular but we just need some element to test.

Here are the two versions I have. The second one could be a bit more
elegant so toLowerCase is called only once. I just wanted to post that
this change is necessary and get any feedback about the change in
general.

// -------------------------------------

if (typeof document != 'undefined' &&
document.documentElement) {

var getAnElement = function(d) {

return (d || document).documentElement;

};

}

// -------------------------------------
// (I know I went a bit extreme on the short identifiers)

if (typeof document != 'undefined') {

var getAnElement = (function() {

function f(d) {

d = d || document;

var g = 'getElementsByTagName',
t = d.documentElement ||
// The following line doesn't seem to add any benefit
// in any of the tested browsers.
// ((t=d.all) && t.tags && (t=t.tags('html')) && t[0])
||
(d[g] && d[g]('html')[0]);

if (!t && d.all && (t=d.all[0])) {
t = d.all[(t.tagName == '!')?1:0] || null;
if (t &&
t.tagName.toLowerCase() != 'html' &&
// for during load in IE4
t.tagName.toLowerCase() != 'head') {
t = null;
}
}
return t;

};

if (f()) {
return f;
}

})();

}

// -------------------------------

Peter
 
D

David Mark

[snip]
How about this...
if (typeof document != 'undefined') {
var getHtmlElement = function(docNode) {
return (docNode || document).documentElement;
};
}

Testing in IE4 with the document.all fallback showed some problems.

While the script is running in the head

document.all[0] // !
document.all[1] // HEAD

after the window.onload event

document.all[0] // !
document.all[1] // HTML

That seems to make it impossible to use the html element to check for
the style object while head scripts are evaluated. However it is just
as easy to use the head element to check for the style object. I
prefer to do feature tests in the head as the code is first evaluated
if possible. An entire Ajax library can be feature tested in the head
but we need one element for form serialization to check for the
attributes property.

The only change this requires is changing the name from
"getHtmlElement" to "getAnElement" which actually make makes more
semantic sense. It isn't really that we want the documentElement in
particular but we just need some element to test.

There will be times when it is useful to get the HTML element (and
only the HTML element.) Numerous functions related to scrolling,
viewport size, element positioning, etc. rely on such a function.
Here are the two versions I have. The second one could be a bit more
elegant so toLowerCase is called only once. I just wanted to post that
this change is necessary and get any feedback about the change in
general.

// -------------------------------------

if (typeof document != 'undefined' &&
document.documentElement) {

var getAnElement = function(d) {

return (d || document).documentElement;

};

Strictly, that should be this.document, which is why I always assign
that to a variable named "doc" at the top of each module. I think
that all of these functions should pass JSLint. I know it will
complain that is an implied global. Obviously in the context of a
function running in the global context that already checked the
existence of a global document property, it is a moot point, but I
still think it should be made to pass. That way there is no need to
document any ignored errors.

Another style point is that variables shouldn't be declared inside if
blocks. I think JSLint complains about that too.
}

// -------------------------------------
// (I know I went a bit extreme on the short identifiers)

if (typeof document != 'undefined') {

var getAnElement = (function() {

function f(d) {

d = d || document;

var g = 'getElementsByTagName',
t = d.documentElement ||
// The following line doesn't seem to add any benefit
// in any of the tested browsers.
// ((t=d.all) && t.tags && (t=t.tags('html')) && t[0])
||
(d[g] && d[g]('html')[0]);

if (!t && d.all && (t=d.all[0])) {
t = d.all[(t.tagName == '!')?1:0] || null;
if (t &&
t.tagName.toLowerCase() != 'html' &&
// for during load in IE4
t.tagName.toLowerCase() != 'head') {

I changed my version of htmlElement to check that a second parameter
(bAnyElement) isn't truthy here, which allows it to return anything it
finds at position 1. If you want to keep it as getAnElement, you
could use a tag parameter that if present would dictate what tag is
allowed. Or perhaps you want to break this up into two functions?

And speaking of base functions, I do think we should have wrappers for
gEBI and certainly for gEBTN (if only for the "*" issue.) The gEBTN
wrapper is required by my bodyElement and headElement functions. The
other one does little besides enable the use of document.all, but I
think that is a good idea to have as an option (if we are going to
support opacity for IE4, document.all would seem a must.) The
bodyElement and headElement functions are called during feature
detection for some modules. That's why some functions cannot
initialize until after the DOM is ready and a few cannot run their
feature tests in the head (e.g. the module that adds scripts to the
head of the document.)
 
T

Thomas 'PointedEars' Lahn

David said:
David said:
On Dec 6, 3:02 pm, Thomas 'PointedEars' Lahn <[email protected]>
wrote:
David Mark wrote:
If docNode.documentElement is defined, will it ever be any type
other than 'object'? It seems like checking this is unnecessary if
there has never been a known problem.
There is the possibility, however remote, that it could be a null
object.
I asked about this sort of testing on c.l.js a when I was first
venturing into feature testing. I thought I'd have to test everything.
If document.getElementsById is present then I'd have to check if it was
callable before calling it. There is a chance it may not be callable;
however, since this situation has never been observed, the
For that host object methods I use:
isFeature = function(o) { return !!(o && (typeof(o) == 'object' ||
typeof(o) == 'function')); };
But evaluating the reference before testing its type is more error-prone
than doing it vice-versa. Hence my isMethodType() approach.

I don't see that. The reference is evaluated before the function ever
runs (when it is passed.) That's why I don't check for typeof
'unknown' in that function as it would error when passing the
reference to the function.

Which is the problem with that approach. You overlook the possibility that
the argument may have to be an unqualified reference or can be a qualified
reference to a host object. Evaluating that reference before the call is
error-prone, especially as a host object may implement the [[Get]] method in
any way the implementor saw fit. Unqualified references are necessary at
times, and there are host objects that are callable but throw an exception
on read access.

I know that and they will throw that exception when passed to the test
function, which means that the test function will never evaluate such
objects.

That it can throw an exception is the major flaw of this approach.
Because the intention behind all feature-testing is to determine
whether or not a property is viable for further operations *without*
allowing the possibility of the corresponding script to *break*.
And if you get typeof(o) == 'object', how does that tell you it is
callable?

If I get that result and *then* the value is not a false-value and I
am expecting the identifier to designate a host object's method, at
the least I can be pretty sure that it is callable.
No, it should not. See above.

See what above? If a non-host object is of type function, then it has
an internal [[call]] method and is therefore callable.

Even if the property owner is a native object, you require the caller
of the method to provide a qualified reference for a feature test if
that test is not supposed to be error-prone. That is not necessary if
you pass the `typeof' result instead. And since there is also the
[[Get]] issue with host objects, the latter is without doubt the less
error-prone and more reasonable approach.


PointedEars
 
P

Peter Michaux

Strictly, that should be this.document, which is why I always assign
that to a variable named "doc" at the top of each module.


[begin quotations from a previous post]

David said:
One reason is that it is shorter than this.document. Referring to it
as "document" alone implies that such a global property exists
(typically a good assumption in a browser, but not the best practice
as it will error otherwise.)

[end quotations from a previous post]

I don't understand your argument for a few reasons but most
importantly there seems to be a logical inconstancy. If you write "var
doc = this.document" in the global scope and then check for "doc"
later you are doing exactly what I am doing when I check "typeof
document != 'undefined'". If there is any chance that your "doc" could
be shadowed in another scope, the same chance exists for "document".

You also state that "this is typically a good assumption in a browser"
and this is a body of code specifically for browsers.
I think that all of these functions should pass JSLint.

I don't.

JSLint is not an ECMAScript v3 validator.

For example, give JSLint the script "new Foo();" and it actually
cannot continue even though the script is a valid ExpressionStatement.
I know it will complain that is an implied global.

If I give JSLint the script "alert('a')" it complains about an implied
global. I don't think writing that script is risky for a browser
environment.

If you click the "Assume a brower" box in JSLint then the implied
global warnings for "document" and "alert" go away.

Give JSLint the script "var a = new Foo();" and it complains about an
implied global but I may know that my target host has a "Foo" or that
I declared it elsewhere.
Obviously in the context of a
function running in the global context that already checked the
existence of a global document property, it is a moot point, but I
still think it should be made to pass. That way there is no need to
document any ignored errors.

JSLint "errors" are not necessarily ECMAScript errors. I wouldn't
document what JSLint has to say. Try turning on Firefox's strict
warnings and see all its warnings about the valid ECMAScript.
Development tools should help develop code but not dictate the code.

Another style point is that variables shouldn't be declared inside if
blocks. I think JSLint complains about that too.

I believe JSLint used to complain about this but it doesn't today. For
example,

function a() {
if (true) {
var b = 1;
}
}

seems to be ok with JSLint. So if my memory is correct, JSLint is a
moving target.

The only reason I can see to avoid declaring variables inside a set of
curly brackets that are not a function body, is because of experience/
baggage coming from programming in another language.

I also think assignment in conditionals can be very handy and elegant
for if-else-else... statements.

JSLint is a great tool to learn what another JavaScript programmer
thinks is good style but I certainly don't agree with many of
Crockford's opinions.

// -------------------------------------
// (I know I went a bit extreme on the short identifiers)
if (typeof document != 'undefined') {
var getAnElement = (function() {
function f(d) {
d = d || document;
var g = 'getElementsByTagName',
t = d.documentElement ||
// The following line doesn't seem to add any benefit
// in any of the tested browsers.
// ((t=d.all) && t.tags && (t=t.tags('html')) && t[0])
||
(d[g] && d[g]('html')[0]);
if (!t && d.all && (t=d.all[0])) {
t = d.all[(t.tagName == '!')?1:0] || null;
if (t &&
t.tagName.toLowerCase() != 'html' &&
// for during load in IE4
t.tagName.toLowerCase() != 'head') {

I changed my version of htmlElement to check that a second parameter
(bAnyElement) isn't truthy here, which allows it to return anything it
finds at position 1. If you want to keep it as getAnElement, you
could use a tag parameter that if present would dictate what tag is
allowed. Or perhaps you want to break this up into two functions?

I would break this into two functions.

And speaking of base functions, I do think we should have wrappers for
gEBI

NOOOOOOOOOOoooooooooo............. ;-)

[begin quotations from a previous post]
I'd say that makes sense.

[end quotations from a previous post]

You can see my confusion on your stance here. This will likely not a
affect many functions but this is an important design decision. This
is the perfect place for me to lean on the "many implementations"
philosophy.

A web page is going to work with just plain HTML and no JavaScirpt
because that is the right way to build a web page. We choose to
enhance some web pages with JavaScript if the features of the browser
meet some feature threshold. We decide what features are required
based on what we know about the history of browsers.

Browsers without document.getElementById are old and/or not common. By
choosing to not enhance pages in these browsers we are reducing the
code base size and complexity with almost no impact on the user
population. Other than trivial enhancements supporting fallbacks to
document.getElementById will not result in enhanced pages anyway as
the more modern feature requirements will not exist. I also agree with
Thomas Lahn (I can't believe I wrote that!) that if the code supports
IE4 it should probably also support NN4.

Not that this should necessarily mean much but worrying about
fallbacks for document.getElementById will make the code look archaic.

So this is where multiple implementations come in. It is possible to
have implementations of functions that do and don't allow for
document.getElementById fallbacks. This doubles the code in the
repository for some functions. Unfortunately that means more
maintenance and it is more maintanence for no real practical benefit
in my mind.

For the time being, I am going to commit code to the repository that
follows this design principle.

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

Code in this repository should not make any special concessions to
enable functionality in browsers older than IE5 or NN6.

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

I hope this doesn't scare anyone off but it seems well within the
range of sensible to me given how few users this will impact. By the
way, I lay awake thinking about this for quite a while.

By the time this repository is ready (>8 years after IE5 and NN6 were
released), if implementations of functions that do support IE4 and NN4
are really wanted by the group then they can be added to the
repository.

[snip]

Peter
 
D

David Mark

Strictly, that should be this.document, which is why I always assign
that to a variable named "doc" at the top of each module.

[begin quotations from a previous post]

David said:
Peter Michaux wrote:
One reason is that it is shorter than this.document. Referring to it
as "document" alone implies that such a global property exists
(typically a good assumption in a browser, but not the best practice
as it will error otherwise.)

[end quotations from a previous post]

I don't understand your argument for a few reasons but most
importantly there seems to be a logical inconstancy. If you write "var
doc = this.document" in the global scope and then check for "doc"
later you are doing exactly what I am doing when I check "typeof
document != 'undefined'". If there is any chance that your "doc" could
be shadowed in another scope, the same chance exists for "document".

I don't define it in the global scope, but inside each anonymous
function that wraps a module.
You also state that "this is typically a good assumption in a browser"
and this is a body of code specifically for browsers.

Fair enough. Some of it could be used in other environments, but
mostly not.
I don't.

JSLint is not an ECMAScript v3 validator.
Right.


For example, give JSLint the script "new Foo();" and it actually
cannot continue even though the script is a valid ExpressionStatement.

Haven't tried that one.
If I give JSLint the script "alert('a')" it complains about an implied
global. I don't think writing that script is risky for a browser
environment.

I typically use this.alert or global.alert, depending on the context.
If you click the "Assume a brower" box in JSLint then the implied
global warnings for "document" and "alert" go away.
Right.


Give JSLint the script "var a = new Foo();" and it complains about an
implied global but I may know that my target host has a "Foo" or that
I declared it elsewhere.

Right. But if you validate an entire file (e.g. a build with the
various functions from the repository), it should be declared
somewhere within. I realize that it could be declared in another
file, but there is always the possibility that that file fails to
download.
JSLint "errors" are not necessarily ECMAScript errors. I wouldn't
document what JSLint has to say. Try turning on Firefox's strict
warnings and see all its warnings about the valid ECMAScript.
Development tools should help develop code but not dictate the code.

I use Firebug, but haven't tried the strict mode. I'm not even sure
where to turn it on.
I believe JSLint used to complain about this but it doesn't today. For
example,

function a() {
if (true) {
var b = 1;
}

}

seems to be ok with JSLint. So if my memory is correct, JSLint is a
moving target.

It is.
The only reason I can see to avoid declaring variables inside a set of
curly brackets that are not a function body, is because of experience/
baggage coming from programming in another language.

That's what I was getting at.
I also think assignment in conditionals can be very handy and elegant
for if-else-else... statements.

I always declare the variables at the top of each function, which
sometimes adds a few extra characters, but I think it is easier to
maintain.
JSLint is a great tool to learn what another JavaScript programmer
thinks is good style but I certainly don't agree with many of
Crockford's opinions.

I find that I agree with most of what it complains about. It is
certainly an invaluable tool.
// -------------------------------------
// (I know I went a bit extreme on the short identifiers)
if (typeof document != 'undefined') {
var getAnElement = (function() {
function f(d) {
d = d || document;
var g = 'getElementsByTagName',
t = d.documentElement ||
// The following line doesn't seem to add any benefit
// in any of the tested browsers.
// ((t=d.all) && t.tags && (t=t.tags('html')) && t[0])
||
(d[g] && d[g]('html')[0]);
if (!t && d.all && (t=d.all[0])) {
t = d.all[(t.tagName == '!')?1:0] || null;
if (t &&
t.tagName.toLowerCase() != 'html' &&
// for during load in IE4
t.tagName.toLowerCase() != 'head') {
I changed my version of htmlElement to check that a second parameter
(bAnyElement) isn't truthy here, which allows it to return anything it
finds at position 1. If you want to keep it as getAnElement, you
could use a tag parameter that if present would dictate what tag is
allowed. Or perhaps you want to break this up into two functions?

I would break this into two functions.
And speaking of base functions, I do think we should have wrappers for
gEBI

NOOOOOOOOOOoooooooooo............. ;-)

[begin quotations from a previous post]
Peter wrote:
I'd say that makes sense.

I know I said that, but looking at my gEBI wrapper later changed my
mind, due to one concern: IE's insistence on returning elements with
names that match the id queried (even when these elements have no ID.)
[end quotations from a previous post]

You can see my confusion on your stance here. This will likely not a
affect many functions but this is an important design decision. This
is the perfect place for me to lean on the "many implementations"
philosophy.

Right. I think it makes sense to have the wrapper that fixes the name
problem in IE and then a second one that includes the .all fallback
(else, how could one use the IE4 opacity support?)
A web page is going to work with just plain HTML and no JavaScirpt
because that is the right way to build a web page. We choose to
enhance some web pages with JavaScript if the features of the browser
meet some feature threshold. We decide what features are required
based on what we know about the history of browsers.
Right.


Browsers without document.getElementById are old and/or not common. By
choosing to not enhance pages in these browsers we are reducing the
code base size and complexity with almost no impact on the user
population. Other than trivial enhancements supporting fallbacks to
document.getElementById will not result in enhanced pages anyway as
the more modern feature requirements will not exist. I also agree with
Thomas Lahn (I can't believe I wrote that!) that if the code supports

I can't believe you did either.
IE4 it should probably also support NN4.

I do not agree with that as NN4 would require new branches for
virtually all of the style-related code, whereas IE4 will work with
the simpler functions as is (e.g. opacity, show/hide element, etc.)
Not that this should necessarily mean much but worrying about
fallbacks for document.getElementById will make the code look archaic.

An alternate version that adds an extra line for document.all seems
like it wouldn't hurt anything. We already have at least one function
with an .all fallback. But at the very least, I think the name
attribute issue should be smoothed out with a wrapper.
So this is where multiple implementations come in. It is possible to
have implementations of functions that do and don't allow for
document.getElementById fallbacks. This doubles the code in the

What I am saying is that all functions should call an "element"
function. Then there is no extra maintenance. One version (the
preferred one IMO) would just fix the name attribute problem. Another
can add support for document.all (which would be needed if IE4 opacity
is to be supported.) I do not recommend a third that uses
document.layers as that doesn't work the same as gEBI or document.all.
repository for some functions. Unfortunately that means more
maintenance and it is more maintanence for no real practical benefit
in my mind.

For the time being, I am going to commit code to the repository that
follows this design principle.

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

Code in this repository should not make any special concessions to
enable functionality in browsers older than IE5 or NN6.

But we already did this with opacity.
==============

I hope this doesn't scare anyone off but it seems well within the
range of sensible to me given how few users this will impact. By the
way, I lay awake thinking about this for quite a while.

No need to. If wrappers are used, then people can plug in whatever
version fits their needs.
By the time this repository is ready (>8 years after IE5 and NN6 were
released), if implementations of functions that do support IE4 and NN4
are really wanted by the group then they can be added to the
repository.

That's what I am saying, but if a wrapper is not used (e.g. functions
call gEBI directly), then that will require rewrites.
 
P

Peter Michaux

[snip]
If I give JSLint the script "alert('a')" it complains about an implied
global. I don't think writing that script is risky for a browser
environment.

I typically use this.alert or global.alert, depending on the context.

I think you are exhibiting unique behavior here.

[snip]

Right. But if you validate an entire file (e.g. a build with the
various functions from the repository), it should be declared
somewhere within. I realize that it could be declared in another
file, but there is always the possibility that that file fails to
download.

If having a variable declared in one file and used in another is a
possibility and the first file may not load then the second file must
check for that variable. In this case JSLint will complain when
validating the second file. These sorts of things make me take
JSLint's output with a box of salt.

I use Firebug, but haven't tried the strict mode. I'm not even sure
where to turn it on.

<URL: https://addons.mozilla.org/en-US/firefox/addon/60>

Then in the menus

Tools
Web Developer
Disable
Disable JavaScript
Strict Warnings


[snip]
I find that I agree with most of what it complains about. It is
certainly an invaluable tool.

I do use JSLint sometimes to catch missing semi-colons and similar
little things. I've encountered many JavaScript developers have hacked
versions of JSLint where they have removed many of Crockfords
preferences. The preferences certainly aren't universally liked.

<URL: http://dean.edwards.name/weblog/2006/06/jslint/>

[snip]
NOOOOOOOOOOoooooooooo............. ;-)
[begin quotations from a previous post]

I know I said that, but looking at my gEBI wrapper later changed my
mind, due to one concern: IE's insistence on returning elements with
names that match the id queried (even when these elements have no ID.)

My feeling is developers just shouldn't cause this problem; however,
do you have a solution for this problem? This may be more persuasive
than enabling IE4.

Just out of interest, do you ever depend on this fallback? I don't
think it is a good idea to spend time on features that no one using
the library will utilize.

[end quotations from a previous post]
You can see my confusion on your stance here. This will likely not a
affect many functions but this is an important design decision. This
is the perfect place for me to lean on the "many implementations"
philosophy.

Right. I think it makes sense to have the wrapper that fixes the name
problem in IE and then a second one that includes the .all fallback
(else, how could one use the IE4 opacity support?)

If the gEBID wrapper is there then surely a document.all fallback
could be written.

[snip]

I can't believe you did either.


I do not agree with that as NN4 would require new branches for
virtually all of the style-related code, whereas IE4 will work with
the simpler functions as is (e.g. opacity, show/hide element, etc.)

If "as is" means that IE4 can use the same code as newer browser then
that just isn't true. Already we've seen substantial work solely
dedicated to enabling IE4.

An alternate version that adds an extra line for document.all seems
like it wouldn't hurt anything.

No but it is a slippery slope. If it is only a little effort to enable
some functions for IE4 then for the medium difficulty implementations
we will feel some pressure to do those ones to. Where to draw the line
for which of the possible "many implementations" to include in the
repository that do or don't support IE4. I think it makes sense to
choose the least grey decisions and said no special effort for IE4.
The user base is small and shrinking daily and they will still get
working pages. Also the things that can be wrapped and enabled in IE4
are likely not the limiting features for widgets as more modern
features will be required.

The reason I continue discussing this is because I'm hoping you will
see the practical side of what I'm saying. There isn't a right or
wrong answer and the multiple implementations can handle accommodate
every option. I just don't see that it is worth *any* extra effort to
enable functions on IE4.

[snip]

But at the very least, I think the name
attribute issue should be smoothed out with a wrapper.

I'd like to see your code and how your fallback will find the element
with the id if an element with the same name exists.

That's what I am saying, but if a wrapper is not used (e.g. functions
call gEBI directly), then that will require rewrites.

Consider Function.prototype.apply. That can be simulated in older
browsers. Do you want to put that in a wrapper?

I don't want to have to wrap apply and then need to include code like
the following.

if (Function.prototype.apply) {

function myApply(obj, f) {
var args = [];
for (var i=2, ilen=arguments.length; i<ilen; i++) {
args[args.length] = arguments;
}
return f.apply(obj, args);
}

}

Yes by using this wrapper it would allow someone to write an
implementation like this.

function myApply(obj, f) {
obj._temp = f;
// some number of arguments
obj._temp(args, arguments[1], arguments[2],
arguments[3], arguments[4]);
delete obj._temp;
}

So many things can be wrapped which causes bloat. There needs to be a
limit on what is wrapped. I don't think we should wrap when the
wrapping is just for IE4.

BTW, this is an interesting turn in the design requirements for the
library. To IE4 or not to IE4? That is the question. The multiple
implementations approach can satisfy everyone but script download
times and maintenance of the project are the underlying issue.

Peter
 
P

Peter Michaux

Yes by using this wrapper it would allow someone to write an
implementation like this.

function myApply(obj, f) {
obj._temp = f;
// some number of arguments
obj._temp(args, arguments[1], arguments[2],
arguments[3], arguments[4]);
delete obj._temp;

}

Never mind the bugs :)
 
D

David Mark

[snip]
I know it will complain that is an implied global.
If I give JSLint the script "alert('a')" it complains about an implied
global. I don't think writing that script is risky for a browser
environment.
I typically use this.alert or global.alert, depending on the context.

I think you are exhibiting unique behavior here.
Perhaps.
[snip]
Give JSLint the script "var a = new Foo();" and it complains about an
implied global but I may know that my target host has a "Foo" or that
I declared it elsewhere.
Right. But if you validate an entire file (e.g. a build with the
various functions from the repository), it should be declared
somewhere within. I realize that it could be declared in another
file, but there is always the possibility that that file fails to
download.

If having a variable declared in one file and used in another is a
possibility and the first file may not load then the second file must
check for that variable. In this case JSLint will complain when
Right.

validating the second file. These sorts of things make me take
JSLint's output with a box of salt.
I use Firebug, but haven't tried the strict mode. I'm not even sure
where to turn it on.

<URL:https://addons.mozilla.org/en-US/firefox/addon/60>

Then in the menus

Tools
Web Developer
Disable
Disable JavaScript
Strict Warnings

[snip]
I find that I agree with most of what it complains about. It is
certainly an invaluable tool.

I do use JSLint sometimes to catch missing semi-colons and similar
little things. I've encountered many JavaScript developers have hacked
versions of JSLint where they have removed many of Crockfords
preferences. The preferences certainly aren't universally liked.

<URL:http://dean.edwards.name/weblog/2006/06/jslint/>

[snip]


And speaking of base functions, I do think we should have wrappers for
gEBI
NOOOOOOOOOOoooooooooo............. ;-)
[begin quotations from a previous post]
David wrote:
Peter wrote:
I think setting the feature threshold at document.getElementById and
document.documentElement is ok today. If there is outrage over this
then the code repository could allow for the the document.all
fallbacks. I cringe a little when I type that thought. I strongly vote
we go "modern" (NN6 November2000, IE5.5July2000).
I'd say that makes sense.
I know I said that, but looking at my gEBI wrapper later changed my
mind, due to one concern: IE's insistence on returning elements with
names that match the id queried (even when these elements have no ID.)

My feeling is developers just shouldn't cause this problem; however,
do you have a solution for this problem? This may be more persuasive
than enabling IE4.

I simply check to see if the element to be returned actually has the
appropriate id.
Just out of interest, do you ever depend on this fallback? I don't
think it is a good idea to spend time on features that no one using
the library will utilize.

No, but as it would fail when names conflict with id's, rather than
enable such a bad practice, I think it is good to have.
[end quotations from a previous post]
You can see my confusion on your stance here. This will likely not a
affect many functions but this is an important design decision. This
is the perfect place for me to lean on the "many implementations"
philosophy.
Right. I think it makes sense to have the wrapper that fixes the name
problem in IE and then a second one that includes the .all fallback
(else, how could one use the IE4 opacity support?)

If the gEBID wrapper is there then surely a document.all fallback
could be written.

That's what I am saying. I just don't think it makes sense to call
gEBI directly in higher-level functions.
[snip]
I can't believe you did either.
I do not agree with that as NN4 would require new branches for
virtually all of the style-related code, whereas IE4 will work with
the simpler functions as is (e.g. opacity, show/hide element, etc.)

If "as is" means that IE4 can use the same code as newer browser then
that just isn't true. Already we've seen substantial work solely
dedicated to enabling IE4.

I'm talking about code like:

el.style.visibility = 'hidden';

The syntax is NN4 is different.
No but it is a slippery slope. If it is only a little effort to enable
some functions for IE4 then for the medium difficulty implementations
we will feel some pressure to do those ones to. Where to draw the line

I don't know. If the alternate version of the gEBI wrapper's only
function is to enable finding an element by ID, then I don't think
that implies that everything else can work with IE4.
for which of the possible "many implementations" to include in the
repository that do or don't support IE4. I think it makes sense to
choose the least grey decisions and said no special effort for IE4.
The user base is small and shrinking daily and they will still get
working pages. Also the things that can be wrapped and enabled in IE4
are likely not the limiting features for widgets as more modern
features will be required.

That's true.
The reason I continue discussing this is because I'm hoping you will
see the practical side of what I'm saying. There isn't a right or
wrong answer and the multiple implementations can handle accommodate
every option. I just don't see that it is worth *any* extra effort to
enable functions on IE4.

But we already broke that rule with the opacity function. It wasn't
much extra effort and I think it was worth it to make that basic
function optionally work for IE4.
[snip]
But at the very least, I think the name
attribute issue should be smoothed out with a wrapper.

I'd like to see your code and how your fallback will find the element
with the id if an element with the same name exists.

It doesn't, it just disallows returning the wrong one. That provides
a clue to the caller that there is a mistake in their markup.
Consider Function.prototype.apply. That can be simulated in older
browsers. Do you want to put that in a wrapper?

The only simulation I have seen for that was pretty ugly (used eval.)
I don't use a wrapper for it myself. I instead avoid using call and
apply unless absolutely necessary and only if those features are
detected.
I don't want to have to wrap apply and then need to include code like
the following.

Neither do I.
if (Function.prototype.apply) {

function myApply(obj, f) {
var args = [];
for (var i=2, ilen=arguments.length; i<ilen; i++) {
args[args.length] = arguments;
}
return f.apply(obj, args);


You can use slice for this, but either way is a performance hit.
}

}

Yes by using this wrapper it would allow someone to write an
implementation like this.

function myApply(obj, f) {
obj._temp = f;
// some number of arguments
obj._temp(args, arguments[1], arguments[2],
arguments[3], arguments[4]);
delete obj._temp;

}

I definitely don't see any value in that.
So many things can be wrapped which causes bloat. There needs to be a
limit on what is wrapped. I don't think we should wrap when the
wrapping is just for IE4.

For the most part I agree. In this case, I think the name vs. id
issue is sufficient to warrant a wrapper for gEBI.
BTW, this is an interesting turn in the design requirements for the
library. To IE4 or not to IE4? That is the question. The multiple

It isn't exactly the question for me. But I think we've already
answered it to some extent. When it is simple to include an
additional version of a function that enables IE4 and somebody is
willing to write it, then it makes sense to include it. Most
applications won't need it, but why not have it available for those
that do?
implementations approach can satisfy everyone but script download
times and maintenance of the project are the underlying issue.

Those who don't use the alternate versions of getAnElement, etc. won't
incur the extra download time. Other than that one and the gEBI
wrapper, I don't have any additional functions that warrant
alternative IE4-friendly versions. My gEBTN wrapper fixes a problem
with IE5.x as well. And other than that, I can't think of anything
else I've written that specifically addresses pre-IE6 issues.
 
P

Peter Michaux

I simply check to see if the element to be returned actually has the
appropriate id.

Ahh, ok. I thought you might be doing an elaborate DOM walk to find
the element that really has the correct id. This would have saved me
from a problem in a mashup that burned about an hour of my time last
week. In fact I think this would be a good function to have because it
fixes a bug in recent browsers. You win :)

But we already broke that rule with the opacity function.

The rule was a result of that experience.

It wasn't
much extra effort and I think it was worth it to make that basic
function optionally work for IE4.

I think that the time to enabled opacity on IE4 was plenty. I had to
figure out about document.all.tags and document.all[] during page load
and after. I had all sorts of browsers fired up. This sort of testing
isn't free.

[snip]
For the most part I agree. In this case, I think the name vs. id
issue is sufficient to warrant a wrapper for gEBI.

I agree. I had never even thought about writing code to fix that
problem before. It is a good idea. That is a bug that can be fixed.

It isn't exactly the question for me. But I think we've already
answered it to some extent. When it is simple to include an
additional version of a function that enables IE4

This is a grey area with the "simple". I'm looking for a consistent
rule for what to include. If a bit of code in the repository happens
to work on IE4 great but if not that is ok with me.

and somebody is
willing to write it, then it makes sense to include it. Most
applications won't need it, but why not have it available for those
that do?

I suppose it comes down to the fact that I'll probably have to write
the documentation, tests and almost certainly maintain it. These are
big time commitments. I'm not interested for IE4 having enhanced
JavaScript pages. Just HTML is fine.

Those who don't use the alternate versions of getAnElement, etc. won't
incur the extra download time. Other than that one and the gEBI
wrapper, I don't have any additional functions that warrant
alternative IE4-friendly versions. My gEBTN wrapper fixes a problem
with IE5.x as well. And other than that, I can't think of anything
else I've written that specifically addresses pre-IE6 issues.

Ok! I see a solution. So you need only two functions to help you
support IE4 at the level that satisfies you (but perhaps not someone
else). If the gEBID wrapper is included in the library to fix the IE5+
bug then there is only one issue for you: getAnElement.

I never intended that the repository would have all the multiple
implementations possible. The repository should have the ones that are
commonly used. A developer using the repository may have extenuating
circumstances that require an implementation not suitable to whatever
the guidelines are for the repository. The developer is free to
maintain an implementation of a particular function outside the
repository and include it in his build process. This is what I
envisioned from the beginning.

So if you were going to use the repository you would just maintain
your own getAnElement. That is just one function outside the
repository. How does that sound?

Peter
 

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,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top