isElement - determining if an object is an element

A

Aaron Gray

Due to M$'s stupidity in not making DOMElements first class citizens the
following will not work :-

function isElement( o)
{
return o instanceof Element
}

It works for FF, Opera and Safari.

What prototype does is this :-

isElement: function(object) {
return object && object.nodeType == 1
}

My version used Browser sniffing :-

function isElement( o)
{
if (!isIE)
return o instanceof Element
else
return o && o.nodeType == 1 && o.tagName != undefined
}

Test case :-

http://www.aarongray.org/Test/JavaScript/isElement-test.html

The actual 'isIE' test code is not the best but is used for brevity.

Any crevats, problems or enhancements most welcome.

Thanks,

Aaron
 
H

Henry

On Jul 30, 2:46 pm, Aaron Gray wrote:
function isElement( o)
{
if (!isIE)
return o instanceof Element
else
return o && o.nodeType == 1 && o.tagName != undefined
}
<snip>

Why branch for this? If the second test is ever good enough it is
always good enough.

Exact equality (===) with 1 would be more precise than type-converting
equality and reduce the possible false positives.

But overall what it the point? If you inferred from a true result from
a call to your - isElement - method that the object implemented the
whole W3C Core DOM (Level 1 or 2) Element interface you would be wrong
in a number of cases. It would make more sense to decide what features
you wanted from such an element and test for those alone on a basis
driven by need.
 
A

Aaron Gray

Henry said:
On Jul 30, 2:46 pm, Aaron Gray wrote:

<snip>

Why branch for this? If the second test is ever good enough it is
always good enough.

Because I would like to use "conforming" browser traits, not that this is
actually one, but it should have been in my view. I take your point. But the
first test is faster and more accurate and runs on more browsers.
Exact equality (===) with 1 would be more precise than type-converting
equality and reduce the possible false positives.

Yes forgot to change that. Thanks,
But overall what it the point? If you inferred from a true result from
a call to your - isElement - method that the object implemented the
whole W3C Core DOM (Level 1 or 2) Element interface you would be wrong
in a number of cases. It would make more sense to decide what features
you wanted from such an element and test for those alone on a basis
driven by need.

Basically my library widget constructors either take a DOM structure,
normally the enclosing DIV (which is what I was wanting to test for) or a
JSON like object which describes the object, directly (or over AJAX).

Thanks for the feedback,

Aaron
 
A

Aaron Gray

Aaron Gray said:
Because I would like to use "conforming" browser traits, not that this is
actually one, but it should have been in my view. I take your point. But
the first test is faster and more accurate and runs on more browsers.


Yes forgot to change that. Thanks,


Basically my library widget constructors either take a DOM structure,
normally the enclosing DIV (which is what I was wanting to test for) or a
JSON like object which describes the object, directly (or over AJAX).

So here's a more effiecient version

if (!isIE)
var isElement = function( o)
{
return o instanceof Element
}
else
var isElement = function( o)
{
return o && o.nodeType === 1 && o.tagName !== undefined
}

As per usual any critisms are welcome :)

Thanks,

Aaron
 
D

David Mark

So here's a more effiecient version

     if (!isIE)
        var isElement = function( o)
        {
             return o instanceof Element
        }
    else
        var isElement = function( o)
        {
             return o && o.nodeType === 1 && o.tagName !== undefined
        }

As per usual any critisms are welcome :)

Thanks,

Aaron

o.tagName != '!'

Lose the sniffing branch and use typeof to determine if o.tagName is a
string and o.nodeType is a number (IE can blow up otherwise.)

As Richard noted, it is likely that you can design this function out
of your application.
 
A

Aaron Gray

So here's a more effiecient version

if (!isIE)
var isElement = function( o)
{
return o instanceof Element
}
else
var isElement = function( o)
{
return o && o.nodeType === 1 && o.tagName !== undefined
}

As per usual any critisms are welcome :)

Thanks,

Aaron

<o.tagName != '!'

<Lose the sniffing branch and use typeof to determine if o.tagName is a
<string and o.nodeType is a number (IE can blow up otherwise.)

Okay so the IE version is not strict enough checking wise not to fall over,
yes, I had half expected that. Okay I will have to look into this better.
Surely the o.nodeType === 1 check is okay. Heres a mod :-

var isElement = function( o)
{
return o && o.nodeType === 1 && typeof o.tagName === string
}

<As Richard noted, it is likely that you can design this function out
<of your application.

My "App" or widget library really wants a good standard function to do th
job that is easy to read, understand and document for other Widget writters.
I get your point though for apps, but then readability and maintainiabily
also apply there as well.

Thanks,

Aaron
 
A

Aaron Gray

Aaron Gray said:
<o.tagName != '!'

<Lose the sniffing branch and use typeof to determine if o.tagName is a
<string and o.nodeType is a number (IE can blow up otherwise.)

What do you mean IE can blow up otherwise ? Sule the instanceof should hold
fine.

I am keeping the IE version for speeds sake mainly.
Okay so the IE version is not strict enough checking wise not to fall
over, yes, I had half expected that. Okay I will have to look into this
better. Surely the o.nodeType === 1 check is okay. Heres a mod :-

var isElement = function( o)
{
return o && o.nodeType === 1 && typeof o.tagName === string
}

Whoopse string needs to be in quotes !

Aaron
 
D

David Mark

<o.tagName != '!'

<Lose the sniffing branch and use typeof to determine if o.tagName is a
<string and o.nodeType is a number (IE can blow up otherwise.)

Okay so the IE version is not strict enough checking wise not to fall over,
yes, I had half expected that. Okay I will have to look into this better.
Surely the o.nodeType === 1 check is okay. Heres a mod :-

No. Try it with an anchor in IE7 that points to a news resource or an
element in a fragment, etc. Always use typeof to test host object
properties.
var isElement = function( o)
{
    return o && o.nodeType === 1 && typeof o.tagName === string

}

<As Richard noted, it is likely that you can design this function out
<of your application.

My "App" or widget library really wants a good standard function to do th

I'm not sure what a "widget library" is.
job that is easy to read, understand and document for other Widget writters.

You already lost that battle if you must expose such a function in
your API. If you are using it to validate arguments passed to your
methods, document acceptable arguments instead.
 
R

RobG

Due to M$'s stupidity in not making DOMElements first class citizens the
following will not work :-

    function isElement( o)
    {
        return o instanceof Element
    }

It works for FF, Opera and Safari.

What prototype does is this :-

        isElement: function(object) {
            return object && object.nodeType == 1
        }

My version used Browser sniffing :-

        function isElement( o)
        {
            if (!isIE)
                return o instanceof Element
            else
                return o && o.nodeType == 1 && o.tagName != undefined
        }

Noting the other advice given here, and that IE 8 will not support
enumeration of nodeType constant values, the following should suit

function isElement(o) {
return o && o.nodeType &&
(o.ELEMENT_NODE === o.nodeType || o.nodeType === 1);
}

Given that you are really trying to distinguish between a DOM node and
a JSON string, why not:

if (typeof o === 'string') {
// assume JSON
} else {
// assume element
}
 
A

Aaron Gray

Due to M$'s stupidity in not making DOMElements first class citizens the
following will not work :-

function isElement( o)
{
return o instanceof Element
}

It works for FF, Opera and Safari.

What prototype does is this :-

isElement: function(object) {
return object && object.nodeType == 1
}

My version used Browser sniffing :-

function isElement( o)
{
if (!isIE)
return o instanceof Element
else
return o && o.nodeType == 1 && o.tagName != undefined
}

<Noting the other advice given here, and that IE 8 will not support
<enumeration of nodeType constant values, the following should suit
<
<function isElement(o) {
< return o && o.nodeType &&
< (o.ELEMENT_NODE === o.nodeType || o.nodeType === 1);
<}

This is messy. My code worked fine on IE8. BTW Just removed IE8 due to other
issues, like behavioual bugs.

<Given that you are really trying to distinguish between a DOM node and
<a JSON string, why not:

Its not a JSON string but an diseminated object at this stage that comes
from a JSON object.

Thanks,

Aaron
 
A

Aaron Gray

<o.tagName != '!'

<Lose the sniffing branch and use typeof to determine if o.tagName is a
<string and o.nodeType is a number (IE can blow up otherwise.)

Okay so the IE version is not strict enough checking wise not to fall
over,
yes, I had half expected that. Okay I will have to look into this better.
Surely the o.nodeType === 1 check is okay. Heres a mod :-

<No. Try it with an anchor in IE7 that points to a news resource or an
<element in a fragment, etc. Always use typeof to test host object
<properties.

donot follow could you give me a more explict example or two ?
var isElement = function( o)
{
return o && o.nodeType === 1 && typeof o.tagName === string

}

<As Richard noted, it is likely that you can design this function out
<of your application.

My "App" or widget library really wants a good standard function to do th

<I'm not sure what a "widget library" is.

Comes fro Window Gadget, being a tree or a table. See ExtJS 2.0 for a Widget
based library :-

http://extjs.com/
job that is easy to read, understand and document for other Widget
writters.

<You already lost that battle if you must expose such a function in
<your API. If you are using it to validate arguments passed to your
<methods, document acceptable arguments instead.

Its for validation (and the equalient of overloaded function behaviour) and
for core support library functionality. ie other wishing to extend the
library or create new widgets.

Aaron
 
K

kangax

So here's a more effiecient version

     if (!isIE)
        var isElement = function( o)
        {
             return o instanceof Element
        }
    else
        var isElement = function( o)
        {
             return o && o.nodeType === 1 && o.tagName !== undefined
        }

As per usual any critisms are welcome :)

The double function expression is unnecessary.

It could be rewritten like so:

var isElement = (function(){
var el = document.createElement('div'), fn;
if (el instanceof Element) {
fn = function(o) {
return o instanceof Element;
}
}
else {
fn = function(o) {
return o && typeof 'nodeType' in o && o.nodeType === 1;
}
}
el = null;
return fn;
})();

We don't perform such branching in prototype.js to avoid
inconsistencies across browsers.

isElement(document.createElement('div')); // true in IE and FF
isElement({ nodeType: 1 }); // true in IE, but false in FF
 
R

RobG

That should be sufficient everywhere, there is no need for browser
sniffing. However, the function is pretty useless for the reasons
expressed in other posts.


What is the point of the tagName test? Do you know of a case where
the nodeType test returns true but the tagName test returns false
(even if changed to a typeof comparison)?

<Noting the other advice given here, and that IE 8 will not support
<enumeration of nodeType constant values, the following should suit

That turned out to be incorrect advice: I downloaded and installed IE8
Beta 1, element.nodeType property returns appropriate values,
element.ELEMENT_NODE returns undefined.

<
<function isElement(o) {
<  return o && o.nodeType &&
<        (o.ELEMENT_NODE === o.nodeType || o.nodeType === 1);
<}

This is messy.

Messy? In hind sight, unnecessarily long.

My code worked fine on IE8.

"Worked" is only sufficient where there is no other criterion that can
be used.

BTW Just removed IE8 due to other
issues, like behavioual bugs.

Thanks for the warning... :-(

<Given that you are really trying to distinguish between a DOM node and
<a JSON string, why not:

Its not a JSON string but an diseminated object at this stage that comes
from a JSON object.

The strategy of feature detection is to determine if objects have the
properties you need them to have, rather than whether they have a
particular feature and inferring that they have other features as a
result.

For example, the fact that an object has a nodeType property with a
value of 1 should not infer support for all relevant DOM 2 interface
features (Node, HTMLElement, etc.) which is what might be inferred
from an isElement function.

Therefore, within your code, you might consider a simple test to
distinguish between an element or "diseminated object" such as
(presuming the other object doesn't have a nodeType property):

if (o.nodeType) {
// o is an element
} else {
// o is something else
}

which does not seem to be any more code than an equivalent isElement()
test (and removes all the code for isElement also).
 
R

RobG

It could be rewritten like so:

var isElement = (function(){
  var el = document.createElement('div'), fn;
  if (el instanceof Element) {

That causes IE8 Beta 1 to barf with "Element is not defined".
Modifying it to:

if (Element && (el instanceof Element))

or

if (Element) {
if (...) {


gets the same result, it wants a more explicit test:

if (typeof Element != 'undefined' && (...))


Perhaps that's documented on one of the IE8 blogs somewhere...

    fn = function(o) {
      return o instanceof Element;
    }
  }
  else {
    fn = function(o) {
      return o && typeof 'nodeType' in o && o.nodeType === 1;

After applying your fix to the above to remove typeof, what is the
point of the test? Its use as a guard here seems unnecessary: if o
doesn't have a nodeType property, o.nodeType will return undefined and
the test fails (even in IE8b).
    }
  }
  el = null;
  return fn;

})();

We don't perform such branching in prototype.js to avoid
inconsistencies across browsers.

isElement(document.createElement('div')); // true in IE and FF
isElement({ nodeType: 1 }); // true in IE, but false in FF

But there you have an inconsitency, lending support to the argument
that feature detection is much better than inference (i.e. if you
expect an object to have a particular property, test for that
property, don't test it for some other property to infer that is has
the one you really want).

The instanceof test is open to the same criticism.
 
G

Gregor Kofler

kangax meinte:
return o && typeof 'nodeType' in o && o.nodeType === 1;

Wouldn't

return typeof o.nodeType !== "undefined" && o.nodeType === 1;

be sufficient?

Gregor
 
L

Lasse Reichstein Nielsen

RobG said:
That causes IE8 Beta 1 to barf with "Element is not defined".
Modifying it to:

if (Element && (el instanceof Element))

or

if (Element) {
if (...) {


gets the same result, it wants a more explicit test:

if (typeof Element != 'undefined' && (...))


Perhaps that's documented on one of the IE8 blogs somewhere...

That's the expected behavior if there is no "Element" variable
defined.
if (Element) { // ...
only works if there *is* an Element variable, and it has a non-falsey
value.

/L
 
H

Henry

Because I would like to use "conforming" browser traits, not
that this is actually one, but it should have been in my view.

Whatever you may think, or may prefer, the second branch is the one
based upon documented (W3C) standard behaviour.
I take your point. But the first test is faster and more
accurate and runs on more browsers.

The first test is not significantly more accurate, as it is still
trivial to fool. But even so, a more accurate test in one branch is
more likely to result in issues that get through testing unnoticed
that consistent behaviour in all environments. Imagine someone who
only tests on, say, Firefox and Safari and the test successfully
rejecting its subjects, but then being released and exposed to IE
where the test lets objects passed that it should not.
Yes forgot to change that. Thanks,


Basically my library widget constructors either take a DOM
structure, normally the enclosing DIV (which is what I was
wanting to test for) or a JSON like object which describes
the object, directly (or over AJAX).
<snip>

How does a test that would fail on at least two current browsers and
dozens of older DOM standard browsers 'run on more browsers' than a
test that would work consistently with all?

Whether the first test is faster is something that you will have to
demonstrate, but the difference is likely to be small compared with
the overheads of the function call and the test for the branching.

At the point of getting each of these two structures you knew
precisely which you had. You knew that because mechanism for
retrieving DOM fragments/branches don't retrieve native JS object
structures, and methods for retrieving JSON structures don't result in
DOM branches. If at some later point you have lost that information
then it is because you threw it away. You solve your problem by
keeping the information you had, not trying to recover it through
dubious inferences.

Remember that while javascript can do a crude emulation of method
overloading it is clunky when its subjects are native JS objects, and
hugely problematic when they are host objects (nearly all the
'popular' libraries have got themselves into a mess attempting to
handle this).

We know that you knew what the object was when you acquired it, at
that should always be the case in your javascript code (it is part of
the programmer's discipline to keep track of the object types in their
code, because there is no language type-system to do that for them).
And knowing the type of object you have you are in a position to chose
an appropriate function method to call with that type as an argument.
That is a lot easier to program, and much more efficient, than having
fewer functions/methods buy having them jump through hoop trying to
work out what types of arguments they received and how they should be
behaving as a result.
 

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

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top