P
Peter Michaux
There have been many threads lately about testing for the existence of
host object methods. I have usually just feature tested the existence
of host methods with the following
if (document.getElementById)
There is concern from several people in the group that this is
insufficient for at least a couple reasons.
One reason for concern is the above test does not determine if
document.getElementById is callable. No one has reported a case where
a browser that has document.getElementById will be non-callable. In
some browsers it may be that some other JavaScript has assigned a non-
callable value to the the document.getElementById property. In those
browsers, it may also be true that someone has assigned a function to
document.getElementById. If this has been done then none of the
proposed tests would detect that the callable value is not the
expected callable value. Thomas Lahn seems particularly concerned
about these problems (and he is preparing to tell I am wrong or that I
have missed the point.)
Another reason for concern is that even though the host may provide a
callable document.getElementById but that when writing just "if
(document.getElementById)" it isn't the [[Call]] property the [[Get]]
property that is used. David Mark seems to think this is a problem
with some (all?) ActiveX objects. All host objects are required to
implement [[Get]] so IE is not ECMAScript compliant if it does not. So
when we are feature testing host objects we are worried about testing
ECMAScript non-compliant browsers.
Both Thomas' and David's feature testing uses typeof for testing host
methods.
Thomas tests the document.evaluate host method
<URL: http://pointedears.de/scripts/types.js>
function isMethodType(s)
{
return /\b(function|object)\b/i.test(s);
}
<URL: http://pointedears.de/scripts/dhtml.js>
if (this.isMethodType(typeof document.evaluate) && document.evaluate)
{
// W3C DOM Level 3 XPath
return function dhtml_getElemByTagName(s, i)
{
if (!s)
{
// ---------------------
David tests for document.getElementById
<URL: http://groups.google.com/group/comp.lang.javascript/msg/d8a9ec709205ae47>
var reFeaturedMethod = new RegExp('^function|object$', 'i');
var isFeaturedMethod = function(o, m) {
var t = typeof(o[m]);
return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
};
if (isFeaturedMethod(doc, 'getElementById')) {
return function(id, docNode) {
return idCheck((docNode || doc).getElementById(id), id);
};
}
// ---------------------
The ECMA standard says that the value of a "typeof hostObject"
expression can be any string. So both Thomas' and David's techniques
could result in false negatives which would leave a browser unenabled.
This is better than a false positive where a function is enabled in a
browser but the function will not function.
What I'm more concerned about is that typeof is being considered a
solution for the unimplemented [[Get]] problem David wrote about. Both
if (document.getElementById)
and
typeof document.getElementById
are described as calling [[Get]] somewhere in their evaluation. If a
browser really did not supply the [[Get]] property at all for an
object then both likely throw an error and probably a TypeError. (More
serious alternatives would be crashing the browser or computer and we
can't protect against that.) The ECMAScript specification of typeof
does not say it will catch that error. Using typeof isn't some sort of
panacea for feature detecting a host method to avoid errors thrown in
non-compliant browsers.
It does seem that using the technique "typeof document.getElementById"
works better in IE than "if (document.getElementById)" if document
happened to be an ActiveX object. However, according to the ECMAScript
standard, there is no reason one should be superior to the other.
Some additional protection for non-compliant browsers could be gained
by adjusting David's code, for example, by adding a try-catch. I've
also changed it to check "unknown" objects for null. It seems to me an
"unknown" object that is null would be somewhat useless.
var reFeaturedMethod = new RegExp('^function|object|unknown$', 'i');
var isFeaturedMethod = function(o, m) {
try {
var t = typeof(o[m]); // throws error if o doesn't have [[Get]]
return !!(reFeaturedMethod.test(t) && o[m]);
}
catch(e) {
return false;
}
};
This is a general mess thanks to the possible behaviors of non-
compliant browsers. It is somewhat clear that using typeof for testing
a host object feature in a non-complaint browser is not guaranteed to
work but does seem to work in the population of browsers today. It
seems to be a practical solution or at least a better way to feature
detect. It is not a theoretical solution since typeof doesn't catch
errors.
host object methods. I have usually just feature tested the existence
of host methods with the following
if (document.getElementById)
There is concern from several people in the group that this is
insufficient for at least a couple reasons.
One reason for concern is the above test does not determine if
document.getElementById is callable. No one has reported a case where
a browser that has document.getElementById will be non-callable. In
some browsers it may be that some other JavaScript has assigned a non-
callable value to the the document.getElementById property. In those
browsers, it may also be true that someone has assigned a function to
document.getElementById. If this has been done then none of the
proposed tests would detect that the callable value is not the
expected callable value. Thomas Lahn seems particularly concerned
about these problems (and he is preparing to tell I am wrong or that I
have missed the point.)
Another reason for concern is that even though the host may provide a
callable document.getElementById but that when writing just "if
(document.getElementById)" it isn't the [[Call]] property the [[Get]]
property that is used. David Mark seems to think this is a problem
with some (all?) ActiveX objects. All host objects are required to
implement [[Get]] so IE is not ECMAScript compliant if it does not. So
when we are feature testing host objects we are worried about testing
ECMAScript non-compliant browsers.
Both Thomas' and David's feature testing uses typeof for testing host
methods.
Thomas tests the document.evaluate host method
<URL: http://pointedears.de/scripts/types.js>
function isMethodType(s)
{
return /\b(function|object)\b/i.test(s);
}
<URL: http://pointedears.de/scripts/dhtml.js>
if (this.isMethodType(typeof document.evaluate) && document.evaluate)
{
// W3C DOM Level 3 XPath
return function dhtml_getElemByTagName(s, i)
{
if (!s)
{
// ---------------------
David tests for document.getElementById
<URL: http://groups.google.com/group/comp.lang.javascript/msg/d8a9ec709205ae47>
var reFeaturedMethod = new RegExp('^function|object$', 'i');
var isFeaturedMethod = function(o, m) {
var t = typeof(o[m]);
return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
};
if (isFeaturedMethod(doc, 'getElementById')) {
return function(id, docNode) {
return idCheck((docNode || doc).getElementById(id), id);
};
}
// ---------------------
The ECMA standard says that the value of a "typeof hostObject"
expression can be any string. So both Thomas' and David's techniques
could result in false negatives which would leave a browser unenabled.
This is better than a false positive where a function is enabled in a
browser but the function will not function.
What I'm more concerned about is that typeof is being considered a
solution for the unimplemented [[Get]] problem David wrote about. Both
if (document.getElementById)
and
typeof document.getElementById
are described as calling [[Get]] somewhere in their evaluation. If a
browser really did not supply the [[Get]] property at all for an
object then both likely throw an error and probably a TypeError. (More
serious alternatives would be crashing the browser or computer and we
can't protect against that.) The ECMAScript specification of typeof
does not say it will catch that error. Using typeof isn't some sort of
panacea for feature detecting a host method to avoid errors thrown in
non-compliant browsers.
It does seem that using the technique "typeof document.getElementById"
works better in IE than "if (document.getElementById)" if document
happened to be an ActiveX object. However, according to the ECMAScript
standard, there is no reason one should be superior to the other.
Some additional protection for non-compliant browsers could be gained
by adjusting David's code, for example, by adding a try-catch. I've
also changed it to check "unknown" objects for null. It seems to me an
"unknown" object that is null would be somewhat useless.
var reFeaturedMethod = new RegExp('^function|object|unknown$', 'i');
var isFeaturedMethod = function(o, m) {
try {
var t = typeof(o[m]); // throws error if o doesn't have [[Get]]
return !!(reFeaturedMethod.test(t) && o[m]);
}
catch(e) {
return false;
}
};
This is a general mess thanks to the possible behaviors of non-
compliant browsers. It is somewhat clear that using typeof for testing
a host object feature in a non-complaint browser is not guaranteed to
work but does seem to work in the population of browsers today. It
seems to be a practical solution or at least a better way to feature
detect. It is not a theoretical solution since typeof doesn't catch
errors.