isElement - determining if an object is an element

R

RobG

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.

Ah yes, of course - it fails with a reference error. A falsey test
would be:

if (window.Element && ... ) { ... }

or perhaps:

var global = (function(){return this})();
if (global.Element && ... ){ ... }
 
H

Henry

On Jul 31, 5:46 am, RobG wrote:
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.
<snip>

Which is correct W3C Core DOM conforming behaviour. The ECMAScript
biddings define the ELEMENT_NODE, ATTRIBUTE_NODE, etc properties as
properties of "The Node class" and list them as "Node. ELEMENT_NODE"
etc. suggesting that if there were a Node constructor it would be the
object that had the properties.

In any event, the dynamic looking up of the values at runtime seems a
bit pointless as changes to this aspect of the DOM are most likely to
represent additions to the list of constants not changes in the value
for any existing type, and changes to the DOM sufficiently far
reaching as to suggest that the value of, for example, -
Node.ELEMENT_NODE - be changed would suggest a radical re-design of
the DOM and little chance of back-compatibility in that redesign.
 
A

Aaron Gray

The double function expression is unnecessary.

It could be rewritten like so:

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

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

Right. Is the inconsistancy really that bad as to cause problems with an
example like this ?
isElement(document.createElement('div')); // true in IE and FF
isElement({ nodeType: 1 }); // true in IE, but false in FF

Yes this is the sort of behaviour that I was trying to avoid, hence adding
in the 'typeof o.tagName === string' to make the test stricter.

If IE had followed the other browsers and made browser types first class we
would not have these problems.

Thanks kangax.

Aaron
 
K

kangax

[...]


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...

Interesting. Should one really care about such quirks while a browser
is in its early development state?
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

I wrongly assumed that `in` doesn't call `GetValue` and so is safer to
use with host objects.
the test fails (even in IE8b).



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).

It certainly makes sense to test for a particular property rather than
relying on some generalized helper. It appears, though, that the need
for "isElement" exists. The goal is to implement its behavior in a
most intuitive and generalized way.
 
H

Henry

On Jul 31, 4:19 pm, kangax wrote:
... . It appears, though, that the need
for "isElement" exists.

How does that appear? It appears that there is a desire and that the
desire is being serviced but nothing has yet suggested a need.
The goal is to implement its behavior in a
most intuitive and generalized way.

Pre-supposing the existence of a need might suggest a goal along those
lines, but without first establishing a need pursuing that goal might
be effort wasted.

And why 'most intuitive' or 'most generalised'? If a function/method
is just going to return true or false and third parties, are going to
trust that it does that in the way that its documentation says it
will, then it really doesn't matter that much how unintuitive the code
it contains may be.

While no real situations are truly general so striving to create the
'most general' anything probably is not that good an idea when
'sufficiently general' could be a knowable state and 'most general' an
unachievable (and possibly undesirable) ideal.

Consider the Core DOM's namespace qualified methods. Even in something
as theoretically general as a general-purpose HTML DOM library it will
not be necessary to use namespace qualified Node/Element methods. And
the - isElement - code shown here is certainly no grantee that the
object for which a - true - result is achieved has those methods (as
IE will give true results for objects implementing the Element
interface but does not provide the namespace qualified methods in its
HTML DOM). That is fine because nobody is gong to need to use
namespace qualified methods in an HTML DOM.

However, the more general situation of trying to write a general-
purpose library for both HTML and XHTML DOMs necessitates the use of
the namespace qualified methods in the XHTML DOM, but the - isElement
- code here does not give any relationship between its true results
and the objects having the necessary methods. More general code might
be indicated for that more general situation, but as that situation is
extremely uncommon it probably does not matter if any - isElement -
code is less than the 'most general'.
 
A

Aaron Gray

Henry said:
On Jul 31, 4:19 pm, kangax wrote:


How does that appear? It appears that there is a desire and that the
desire is being serviced but nothing has yet suggested a need.

You have to double or tripple up on constructors to do the same trick. Its
neater with one constructor and using what in static class languages is
called overoading.

Aaron
 
H

Henry

On Jul 31, 4:07 pm, Aaron Gray wrote:
return o && 'nodeType' in o && o.nodeType === 1;

Don't do this in preference to your previous version. The type-
converting to boolean test on - o - only guarantees it has trueness,
not that it is an object. If the right hand side of an - in -
operation is not an object an exception is thrown. Thus if - o - is a
primitive value that has truness avoidable exceptions will be thrown
in this code rather than false values returned (note that - instanceof
- can handle a primitive type left hand side operand). But once - o -
has been established as having tureness the expression - o.nodeType -
will not fail due to - o - being a primitive value, as it implies
internal type-conversation of the primitive into the corresponding
object (none of which will have - nodeType - properties).

Yes this is the sort of behaviour that I was trying to avoid,
hence adding in the 'typeof o.tagName === string' to make
the test stricter.

If IE had followed the other browsers and made browser types
first class we would not have these problems.

No, you would still have precisely this problem because javascript
objects can still be created to fool - o instanceof Element - into
returning a true result for an object that is not a DOM Element.
 
A

Aaron Gray

Henry said:
On Jul 31, 4:07 pm, Aaron Gray wrote:


Don't do this in preference to your previous version. The type-
converting to boolean test on - o - only guarantees it has trueness,
not that it is an object. If the right hand side of an - in -
operation is not an object an exception is thrown. Thus if - o - is a
primitive value that has truness avoidable exceptions will be thrown
in this code rather than false values returned (note that - instanceof
- can handle a primitive type left hand side operand). But once - o -
has been established as having tureness the expression - o.nodeType -
will not fail due to - o - being a primitive value, as it implies
internal type-conversation of the primitive into the corresponding
object (none of which will have - nodeType - properties).

Opps, not looking what I am doing there, yes I need to sit back and review
this whole thing properly rathr than just responding to the small peices,
badly.
No, you would still have precisely this problem because javascript
objects can still be created to fool - o instanceof Element - into
returning a true result for an object that is not a DOM Element.

Yes I would not be too bothered about that, its a real side case if it
exists.

How do you do that anyway ?

Thanks Henry,

Aaron
 
H

Henry

Henry wrote in message


You have to double or tripple up on constructors to do the same
trick. Its neater with one constructor and using what in static
class languages is called overoading.

That does not follow, and still no need then.
 
D

David Mark

The double function expression is unnecessary.
It could be rewritten like so:
var isElement = (function(){
 var el = document.createElement('div'), fn;

   if (Element && el instanceof Element) {  // Thanks RobG>    fn = function(o) {
     return o instanceof Element;
   }
 }
 else {
   fn = function(o) {

      return o && 'nodeType' in o && o.nodeType === 1;
   }
 }
 el = null;
 return fn;
})();
We don't perform such branching in prototype.js to avoid
inconsistencies across browsers.
LOL.


Right. Is the inconsistancy really that bad as to cause problems with an
example like this ?

You and the Prototype guy are both trying to solve the same problem
that does not need to be solved. Nobody need a GP function to
discriminate between an Object object and an element.
Yes this is the sort of behaviour that I was trying to avoid, hence adding
in the 'typeof o.tagName === string' to make the test stricter.

If IE had followed the other browsers and made browser types first class we
would not have these problems.

We don't have a problem. It is people who wish to make the language
work more like Java who have a problem. The problem is easily
diagnosed: they do not understand the language they are using or
browser scripting in general.
 
H

Henry

Yes I would not be too bothered about that, its a real
side case if it exists.

It is hardly going to be a common case to create a javascript object
with a - nodeType - property with the value 1 and a string type -
tagName - property, unless you are creating something that you want to
be treated like an Element in which case the making the discrimination
is pointless.
How do you do that anyway ?

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<head>
<title></title>
</head><body>
<div>
<script type="text/javascript">

function testObj1(){
}
testObj1.prototype = document.createElement('DIV');

var o = new testObj1();

alert((o instanceof Element));

</script>
</body>

Javascript's - instanceof - is not about object types, but rather
about runtime relationships between object on a prototype chain and
the objects referred to by the - prototype - properties of
constructors.
 
A

Aaron Gray

That should be 'overloading'.
That does not follow, and still no need then.

Basically take an accordian widget, that either gets initialized from a DIV
or UL,LI structure, hence needing to know its element based, or via a JSON
based approach. Now either you have two constructors one for each casew or
you have one overrided constructor that detects the parameter type. Which is
what I prefer to do until I see any reason not to and to use two
constructors.

Aaron
 
D

David Mark

That should be 'overloading'.


Basically take an accordian widget, that either gets initialized from a DIV
or UL,LI structure, hence needing to know its element based, or via a JSON

I don't follow. DIV or UL, LI is a common discrimination for such
widgets and is easily accomplished by checking the tagName (or
nodeName) property of the indicated element.
based approach. Now either you have two constructors one for each casew or

Why would you need two constructors? You could certainly create
options for this, but it seems like an object that creates one of
these widgets from whole cloth should inherit from one that adds
behavior to an existing DOM structure.
you have one overrided constructor that detects the parameter type. Whichis

Overloaded?

A design that forces you to write code to discriminate between Object
objects and random host objects? Not an appropriate strategy for this
(or virtually anything in JavaScript--see jQuery.)
what I prefer to do until I see any reason not to and to use two
constructors.

Those aren't your only two choices.

HTH
 
T

Thomas 'PointedEars' Lahn

Lasse said:
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.

Property, not variable. Of an object in the scope chain.


PointedEars
 
T

Thomas 'PointedEars' Lahn

RobG said:
Ah yes, of course - it fails with a reference error. A falsey test
would be:

if (window.Element && ... ) { ... }

This would be based on a false assumption.
or perhaps:

var global = (function(){return this})();
if (global.Element && ... ){ ... }

That would still cause warnings.

Your signature delimiter was borken by Google Groups.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Henry said:
On Jul 31, 5:46 am, RobG wrote:

<snip>

Which is correct W3C Core DOM conforming behaviour. The ECMAScript
biddings define the ELEMENT_NODE, ATTRIBUTE_NODE, etc properties as
properties of "The Node class" and list them as "Node. ELEMENT_NODE"
etc. suggesting that if there were a Node constructor it would be the
object that had the properties.

Further research shows that
<http://www.w3.org/TR/DOM-Level-3-Core/ecma-script-binding.html> eventually
observes that implementations of ECMAScript Ed. 3 do not support classes and
asserts instead that `ELEMENT_NODE' and so forth would be "Properties of the
Node Constructor function", in contrast to "Properties of objects that
implement the Node interface" defined elsewhere. So e.g. the Gecko DOM
implementing the former as the latter is indeed a proprietary feature.


PointedEars
 
D

Dan Evans

Right. Is the inconsistancy really that bad as to cause problems with an
example like this ?


Yes this is the sort of behaviour that I was trying to avoid, hence adding
in the 'typeof o.tagName === string' to make the test stricter.

I think this could then be fooled by:

isElement({ nodeType: 1, tagName: 'div' })

It seems to me the that a major point here is that any test you do
like this will be trivial to fool because you're looking for
properties of this object which are not directly related to what you
want to do with the object. As you can see if you keep testing the
same way, it is trivial to construct a new property on the object
which passes the test. So the test will only become "stricter" in the
same sense that adding 1 to an integer makes it closer to infinity.

I think the suggestion is that you either use "duck typing" to find
out if whatever you're working with is capable of doing what you want
to do with it, or that you track the object itself (if it's JSON then
you either created it or used AJAX to pull it in, if it's a DOM
element then you either queried the DOM or used a DOM method to create
it). In either case you knew what the JSON/DOM object was when it was
created, so as Henry said, you already had that information and threw
it away/lost it. If you can just retain that information you won't
need to do this imprecise test.

I'm aware that I'm echoing what others have said, but I hope that I've
said it in a way that helps foster understanding.
 
A

Aaron Gray

Henry said:
It is hardly going to be a common case to create a javascript object
with a - nodeType - property with the value 1 and a string type -
tagName - property, unless you are creating something that you want to
be treated like an Element in which case the making the discrimination
is pointless.

Yes, I am inclined to agree with you.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<head>
<title></title>
</head><body>
<div>
<script type="text/javascript">

function testObj1(){
}
testObj1.prototype = document.createElement('DIV');

var o = new testObj1();

alert((o instanceof Element));

</script>
</body>

Okay that looks a good method. They have to make that much effort, and does
not allow Object Notation to fake it; which I did not want. But it can be
faked on IE leading to difference in behaviour. Horrrible :(
Javascript's - instanceof - is not about object types, but rather
about runtime relationships between object on a prototype chain and
the objects referred to by the - prototype - properties of
constructors.

Yes.

May have to agree with you on this one and go for a member and value based
test then :(

Thanks Henry, you have convinced me, shame, but there you go, I'll post a
new single pronged attempt tommorow.

Aaron
 
A

Aaron Gray

Right. Is the inconsistancy really that bad as to cause problems with an
example like this ?


Yes this is the sort of behaviour that I was trying to avoid, hence adding
in the 'typeof o.tagName === string' to make the test stricter.
I think this could then be fooled by:

isElement({ nodeType: 1, tagName: 'div' })

Only on IE which shows IE is BAD !!!
created, so as Henry said, you already had that information and threw
it away/lost it. If you can just retain that information you won't
need to do this imprecise test.

Yes this is the case but it does not go for really nice libraries and kind
of transparent sparation you can get with static typing and overloading.
I'm aware that I'm echoing what others have said, but I hope that I've
said it in a way that helps foster understanding.

Yes, I have came round to this argument only because IE is BAD and did not
follow the other browsers behaviour.

Aaron
 
A

Aaron Gray

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

function isElement( o)
{
return o instanceof Element
}

This fails because it cannot be used universally, and will result in
different behaviours accross browsers if used as the general case with a sub
case for |IE.
It works for FF, Opera and Safari.

What prototype does is this :-

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

As Henry says this will fail with a typing error if object is not an object.
My version used Browser sniffing :-

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

This fails too for the above reasons.

Duck Typing wins out :-

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


Test case :-

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

A bit of a verbose and stringent test but I think it is for the best. It
allow overriding behaviour to be done realatively safely.

Any comments, cristisms, or general flac welcome :)

Aaron
 

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,772
Messages
2,569,593
Members
45,105
Latest member
sheetaldubay7750ync
Top