Feature testing DOM0 event interface

P

Peter Michaux

It seems people make a regular habit of testing for the
addEventListener and attachEvent methods but they just assume the DOM0-
type event interface exists like element.onclick.

At least recent versions of Opera, Safari, IE allow for a feature test
by all returning true for the following

javascript:alert(typeof document.documentElement.onclick !=
'undefined')

Unfortunately, Firefox returns false for the above.

Does anyone have a universal feature test for this?

Thanks,
Peter
 
D

David Mark

It seems people make a regular habit of testing for the
addEventListener and attachEvent methods but they just assume the DOM0-
type event interface exists like element.onclick.

This assumption won't hurt anything if the page is designed to degrade
gracefully.
At least recent versions of Opera, Safari, IE allow for a feature test
by all returning true for the following

javascript:alert(typeof document.documentElement.onclick !=
'undefined')

Unfortunately, Firefox returns false for the above.

Does anyone have a universal feature test for this?

This isn't ideal as it relies on setAttribute, but it does work for
Firefox. Alternatively, you could define the onclick attribute in the
markup.

var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
isHostMethod(el, 'setAttribute')) {
el.setAttribute('onclick', '(function() {})');
dom0 = (typeof el.onclick == 'function');
el.setAttribute('onclick', '');
}

If a design relies on DOM0 event support, the best solution is to call
its gateway function like this:

this.onload = myGateway;

Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick? I have thought about that one and I don't think it requires
a workaround. If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally. As long as the page
is designed to work without script, this should not cause an issue. I
know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.
 
P

Peter Michaux

This assumption won't hurt anything if the page is designed to degrade
gracefully.

I have a situation where it will hurt and this is a common situation.
A page arrives with its HTML for static viewing. I want to manipulate
the DOM structure and page CSS to create a widget. I think of the
point at which the DOM and CSS are manipulated as "the point of no
return". Before I make these manipulations I need to make sure the
widget will function completely. If the DOM structure and CSS both
work, but the browser doesn't have correct event handling, then the
widget will be useless and some content will not be visible to the
user.

This isn't ideal as it relies on setAttribute, but it does work for
Firefox. Alternatively, you could define the onclick attribute in the
markup.

var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
isHostMethod(el, 'setAttribute')) {
el.setAttribute('onclick', '(function() {})');
dom0 = (typeof el.onclick == 'function');
el.setAttribute('onclick', '');

}

There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.

http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-F68F082

If a design relies on DOM0 event support, the best solution is to call
its gateway function like this:

this.onload = myGateway;

I agreed that this is a solid solution. What I'm actually trying to
determine if the above will work. Here is why...

When a page is built with progressive enhancement in mind, the page
looks like like the unenhanced version while the whole page loads.
When window.onload occurs, then the page can be enhanced and made to
look like the enhanced version. For the user with a browser that will
support the enhanced version, this is unsightly. For example, a huge
list of nested ul elements appear on the page for a while while the
page loads, when window.onload fires, the hander determines that the
nested ul elements can be converted into a drop-down menu or tree
menu. When this conversion happens the page appears to "jump" as the
CSS changes and the page is re-rendered. This is not really acceptable
as more users will be able to have the enhanced version than the
unenhanced version.

So what I want to do is the following...

When the page is loading, determine if the browser has enough features
to "get out of trouble". If that is the case then add a CSS file to
the page with document.write, that will give the page an acceptable
appearance while the page loads. When window.onload fires, determine
if the widget can fully function and enable that widget. If the widget
cannot fully function then execute the "get out of trouble" and revert
the styling of the HTML to something more pleasing for the unenlivened
version. It almost allows me to *tentatively* cross the point of no
return with just one foot so the page looks nicer during load.

Makes sense?
Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick?

Yes my question also impacts this.
I have thought about that one and I don't think it requires
a workaround.

You may be right that the workaround is not necessary. I need to think
about this more.

If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally. As long as the page
is designed to work without script, this should not cause an issue.

But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.

[1] http://domscripting.com/blog/display/41

I am completely aware this is a crappy example but my gut tells me
there must be situations where having the JavaScript run followed by
the normal navigation is bad.

I suppose the JavaScript could be run after a timeout and that way it
only runs if the normal navigation didn't work. Messy solution.

I know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.

I think the sniff is also a mistake. I use DOM0 listeners for all
click events. That way I treat all browsers equally and don't need the
sniff. I think it would be fine to use DOM0 listeners for all event
handlers. There really isn't a need for addEventListener and
attachEvent because a wrapper API around DOM0 can do it all anyway. My
problem is that right now I am not testing that DOM0 will work. This
is not a large practical problem for me but it is a large problem in
trying to write a widget properly.

By the way, this problem is the last detail for an article I am
writing for my blog about writing progressive enhancement widgets (a
tabbed pane) for the general web. It uses the isHostMethod etc. If
anyone is interested in reviewing the article I can post it here for
debate. I've never seen such an article and I think such an article
has long been needed.

Peter
 
D

David Mark

I have a situation where it will hurt and this is a common situation.
A page arrives with its HTML for static viewing. I want to manipulate
the DOM structure and page CSS to create a widget. I think of the
point at which the DOM and CSS are manipulated as "the point of no
return". Before I make these manipulations I need to make sure the
widget will function completely. If the DOM structure and CSS both
work, but the browser doesn't have correct event handling, then the
widget will be useless and some content will not be visible to the
user.









There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.

How so? It seems quite reasonable to me. Of course, if IE8 fails to
fix setAttribute and returns undefined for an unset onclick property,
you will need to use a suitable setAttribute wrapper.
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-...



I agreed that this is a solid solution. What I'm actually trying to
determine if the above will work. Here is why...

When a page is built with progressive enhancement in mind, the page
looks like like the unenhanced version while the whole page loads.
When window.onload occurs, then the page can be enhanced and made to
look like the enhanced version. For the user with a browser that will
support the enhanced version, this is unsightly. For example, a huge
Right.

list of nested ul elements appear on the page for a while while the
page loads, when window.onload fires, the hander determines that the
nested ul elements can be converted into a drop-down menu or tree
menu. When this conversion happens the page appears to "jump" as the
CSS changes and the page is re-rendered. This is not really acceptable
as more users will be able to have the enhanced version than the
unenhanced version.

I see. You want to test before you add a style rule to hide the
nested lists. Makes sense. Of course, what scripted browser supports
adding style rules, but not DOM0 events?
So what I want to do is the following...

When the page is loading, determine if the browser has enough features
to "get out of trouble". If that is the case then add a CSS file to
the page with document.write, that will give the page an acceptable
appearance while the page loads. When window.onload fires, determine
Right.

if the widget can fully function and enable that widget. If the widget
cannot fully function then execute the "get out of trouble" and revert
the styling of the HTML to something more pleasing for the unenlivened

Or, in some cases, something that won't completely break the page for
the odd browser that supports dynamic styles, but not DOM0 events
(e.g. un-hide the nested lists.)
version. It almost allows me to *tentatively* cross the point of no
return with just one foot so the page looks nicer during load.

Makes sense?
Yes.
Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick?

Yes my question also impacts this.
I have thought about that one and I don't think it requires
a workaround.

You may be right that the workaround is not necessary. I need to think
about this more.
If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally. As long as the page
is designed to work without script, this should not cause an issue.

But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.

Yes. But does that old Safari version even support Ajax? If so, you
already hinted at the solution: use a button.
[1]http://domscripting.com/blog/display/41

I am completely aware this is a crappy example but my gut tells me
there must be situations where having the JavaScript run followed by
the normal navigation is bad.

It's possible. I wonder how many people still use Safari 1.x though.
I suppose the JavaScript could be run after a timeout and that way it
only runs if the normal navigation didn't work. Messy solution.

Yes, that would be ugly.
I think the sniff is also a mistake. I use DOM0 listeners for all
click events. That way I treat all browsers equally and don't need the
sniff. I think it would be fine to use DOM0 listeners for all event

I'm not big on using DOM0 listeners to simulate addEventListener/
attachEvent (unless neither exists.) For one, it slows down event
handling significantly. And then there are the possibilities of
collisions with DOM0 listeners defined in the markup or attached by
other scripts.
handlers. There really isn't a need for addEventListener and
attachEvent because a wrapper API around DOM0 can do it all anyway. My

It can do it all, but is considerably slower in doing it. Responding
to user input is the last place I would want a bottleneck.
problem is that right now I am not testing that DOM0 will work. This
is not a large practical problem for me but it is a large problem in
trying to write a widget properly.

Large in what way? I can't think of a user agent that would add a
style rule with script, but not handle DOM0 events.
By the way, this problem is the last detail for an article I am
writing for my blog about writing progressive enhancement widgets (a
tabbed pane) for the general web. It uses the isHostMethod etc.

Sounds like a useful article. Every time I set out to write a tabbed
anything, I run into a problem when style is disabled, but scripting
is enabled. The only solution I have found for this is to make the
links in the tabs point to bookmarks in the page and to allow the
default click action to navigate to them. This obviously has an
unwanted side effect when style and scripting are both enabled (the
tabs scroll off the page after switching panes.) How did you handle
this odd case?

If
anyone is interested in reviewing the article I can post it here for
debate. I've never seen such an article and I think such an article
has long been needed.

Post it. We've dealt with the issue of turning a list and a series of
div's into tabbed panes before, but never the issue of missing DOM0
event support (AFAIK.)
 
P

Peter Michaux

[snip]
There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.

How so? It seems quite reasonable to me. Of course, if IE8 fails to
fix setAttribute and returns undefined for an unset onclick property,
you will need to use a suitable setAttribute wrapper.

The spec says that if the attribute doesn't exist, it will create it.
It doesn't say that the fact that an on* attribute can be created
means it will be used an an event handler.

[snip]
I see. You want to test before you add a style rule to hide the
nested lists. Makes sense. Of course, what scripted browser supports
adding style rules, but not DOM0 events?

I've never encountered one but that would be feature inference based
on two very unrelated features: style and events.

[snip]
But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.

Yes. But does that old Safari version even support Ajax? If so, you
already hinted at the solution: use a button.

I know someone with Safari 1.9 because she has OS X 10.3 and so can't
upgrade.

Early point releases of Safari 2 also had this problem so the problem
was still occurring in a browser versions released only about a year
or so ago.


[snip about using DOM0 event instead of newer]
It can do it all, but is considerably slower in doing it. Responding
to user input is the last place I would want a bottleneck.

It can't be that much slower. It is only a matter of running one loop
in the JavaScript rather than in the C/C++/Java of the browser. Each
individual handler will probably do something that will eclipse the
time taken to run this loop.

[snip]
Large in what way?

I didn't even need the word "large". It isn't a practical problem for
me at all. Because...
I can't think of a user agent that would add a
style rule with script, but not handle DOM0 events.

True but it is unrelated feature inference.
Sounds like a useful article. Every time I set out to write a tabbed
anything, I run into a problem when style is disabled, but scripting
is enabled. The only solution I have found for this is to make the
links in the tabs point to bookmarks in the page and to allow the
default click action to navigate to them. This obviously has an
unwanted side effect when style and scripting are both enabled (the
tabs scroll off the page after switching panes.) How did you handle
this odd case?

I didn't need to. I'll post it and you will see why.
If


Post it. We've dealt with the issue of turning a list and a series of
div's into tabbed panes before, but never the issue of missing DOM0
event support (AFAIK.)

I haven't finished writing the article but I can post the code for the
example later today.

Thanks,
Peter
 
D

David Mark

It seems people make a regular habit of testing for the
addEventListener and attachEvent methods but they just assume the DOM0-
type event interface exists like element.onclick.
[snip]




Does anyone have a universal feature test for this?
This isn't ideal as it relies on setAttribute, but it does work for
Firefox.  Alternatively, you could define the onclick attribute inthe
markup.
var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
    isHostMethod(el, 'setAttribute')) {
  el.setAttribute('onclick', '(function() {})');
  dom0 = (typeof el.onclick == 'function');
  el.setAttribute('onclick', '');
}
There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.
How so?  It seems quite reasonable to me.  Of course, if IE8 fails to
fix setAttribute and returns undefined for an unset onclick property,
you will need to use a suitable setAttribute wrapper.

The spec says that if the attribute doesn't exist, it will create it.
It doesn't say that the fact that an on* attribute can be created
means it will be used an an event handler.

The test checks if the property is a function. An implementation that
does not support DOM0 isn't likely to pass.
[snip]
I see.  You want to test before you add a style rule to hide the
nested lists.  Makes sense.  Of course, what scripted browser supports
adding style rules, but not DOM0 events?

I've never encountered one but that would be feature inference based
on two very unrelated features: style and events.

No question.
[snip]
But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.
Yes.  But does that old Safari version even support Ajax?  If so, you
already hinted at the solution: use a button.

I know someone with Safari 1.9 because she has OS X 10.3 and so can't
upgrade.

Early point releases of Safari 2 also had this problem so the problem
was still occurring in a browser versions released only about a year
or so ago.

Do those auto-upgrade?
[snip about using DOM0 event instead of newer]
It can do it all, but is considerably slower in doing it.  Responding
to user input is the last place I would want a bottleneck.

It can't be that much slower. It is only a matter of running one loop
in the JavaScript rather than in the C/C++/Java of the browser. Each
individual handler will probably do something that will eclipse the
time taken to run this loop.

You've got to find the events for the element in question and then
loop through them. The overhead may not be significant when compared
to the code in the listeners, but then those should also be as light
as possible. It could cause a palpable slowdown in events like
mousemove, resize, scroll, etc.
[snip]
Large in what way?

I didn't even need the word "large". It isn't a practical problem for
me at all. Because...
I can't think of a user agent that would add a
style rule with script, but not handle DOM0 events.

True but it is unrelated feature inference.
Sounds like a useful article.  Every time I set out to write a tabbed
anything, I run into a problem when style is disabled, but scripting
is enabled.  The only solution I have found for this is to make the
links in the tabs point to bookmarks in the page and to allow the
default click action to navigate to them.  This obviously has an
unwanted side effect when style and scripting are both enabled (the
tabs scroll off the page after switching panes.)  How did you handle
this odd case?

I didn't need to. I'll post it and you will see why.

I'm interested to see what you came up with.
 
P

Peter Michaux

It seems people make a regular habit of testing for the
addEventListener and attachEvent methods but they just assume the DOM0-
type event interface exists like element.onclick.
Does anyone have a universal feature test for this?
This isn't ideal as it relies on setAttribute, but it does work for
Firefox. Alternatively, you could define the onclick attribute in the
markup.
var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
isHostMethod(el, 'setAttribute')) {
el.setAttribute('onclick', '(function() {})');
dom0 = (typeof el.onclick == 'function');
el.setAttribute('onclick', '');
}
There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.
How so? It seems quite reasonable to me. Of course, if IE8 fails to
fix setAttribute and returns undefined for an unset onclick property,
you will need to use a suitable setAttribute wrapper.
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-...
The spec says that if the attribute doesn't exist, it will create it.
It doesn't say that the fact that an on* attribute can be created
means it will be used an an event handler.

The test checks if the property is a function. An implementation that
does not support DOM0 isn't likely to pass.

It is an interesting test and thanks for sharing it. I hoped there was
something a little more straight forward.

[snip]
But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.
Yes. But does that old Safari version even support Ajax? If so, you
already hinted at the solution: use a button.
I know someone with Safari 1.9 because she has OS X 10.3 and so can't
upgrade.
Early point releases of Safari 2 also had this problem so the problem
was still occurring in a browser versions released only about a year
or so ago.

Do those auto-upgrade?

Version 1.9 does not auto-upgrade because version 2 that requires OS X
10.4.

Version 2 does auto-upgrade all the way into version 3.

[snip about using DOM0 event instead of newer]
It can do it all, but is considerably slower in doing it. Responding
to user input is the last place I would want a bottleneck.
It can't be that much slower. It is only a matter of running one loop
in the JavaScript rather than in the C/C++/Java of the browser. Each
individual handler will probably do something that will eclipse the
time taken to run this loop.

You've got to find the events for the element in question and then
loop through them.

There is no need to find them. Here is some pseudo code...

function addListener(element, type, callback) {
if (!(element['on'+type])) {
element['on'+type] = function(e) {
var callbacks = arguments.callee.callbacks;
for (var i=0; i<callbacks.length; i++) {
callbacks(e);
}
};
element['on'+type].callbacks = [];
}
element['on'+type].push(callback);
};

[snip]

Peter
 
D

David Mark

It seems people make a regular habit of testing for the
addEventListener and attachEvent methods but they just assume the DOM0-
type event interface exists like element.onclick.
[snip]
Does anyone have a universal feature test for this?
This isn't ideal as it relies on setAttribute, but it does work for
Firefox.  Alternatively, you could define the onclick attribute in the
markup.
var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
    isHostMethod(el, 'setAttribute')) {
  el.setAttribute('onclick', '(function() {})');
  dom0 = (typeof el.onclick == 'function');
  el.setAttribute('onclick', '');
}
There doesn't seem to be anything in the Element.setAttribute spec
that make me think this is a particularly robust test.
How so?  It seems quite reasonable to me.  Of course, if IE8 fails to
fix setAttribute and returns undefined for an unset onclick property,
you will need to use a suitable setAttribute wrapper.
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-...
The spec says that if the attribute doesn't exist, it will create it.
It doesn't say that the fact that an on* attribute can be created
means it will be used an an event handler.
The test checks if the property is a function.  An implementation that
does not support DOM0 isn't likely to pass.

It is an interesting test and thanks for sharing it. I hoped there was
something a little more straight forward.

[snip]




But it might cause an issue if the JavaScript does something that
shouldn't be followed by the normal navigation. I know we aren't
supposed to use <a> links to add items to a shopping cart but suppose
someone did. And then suppose they used hijax[1] to make the link add
the item to the cart with Ajax. If the Ajax is initiated an then the
link is followed, the item will be added to the cart twice.
Yes.  But does that old Safari version even support Ajax?  If so, you
already hinted at the solution: use a button.
I know someone with Safari 1.9 because she has OS X 10.3 and so can't
upgrade.
Early point releases of Safari 2 also had this problem so the problem
was still occurring in a browser versions released only about a year
or so ago.
Do those auto-upgrade?

Version 1.9 does not auto-upgrade because version 2 that requires OS X
10.4.

Version 2 does auto-upgrade all the way into version 3.

Then it is pretty much a Safari 1.x issue. Until OS X 10.3 dies out,
the issue will have to be considered on a case by case basis (I don't
see a general solution that is practical, unless you want to use DOM0
exclusively.)
[snip about using DOM0 event instead of newer]
It can do it all, but is considerably slower in doing it.  Responding
to user input is the last place I would want a bottleneck.
It can't be that much slower. It is only a matter of running one loop
in the JavaScript rather than in the C/C++/Java of the browser. Each
individual handler will probably do something that will eclipse the
time taken to run this loop.
You've got to find the events for the element in question and then
loop through them.

There is no need to find them. Here is some pseudo code...

function addListener(element, type, callback) {
  if (!(element['on'+type])) {
      element['on'+type] = function(e) {
        var callbacks = arguments.callee.callbacks;
        for (var i=0; i<callbacks.length; i++) {
            callbacks(e);
        }
      };
      element['on'+type].callbacks = [];
  }
  element['on'+type].push(callback);

};


That's not too bad, but clearly more is needed to emulate
addEventListener (e.g. preserve the "this" reference, handle callbacks
that return false, etc.) I recently added similar code to my
library. Rather than looping, I "chained" the (normalized) listeners
together. The biggest issue that came up was how to allow for
alternate contexts (i.e. setting "this" to something other than the
element.) I finally came up with a normalization/storage scheme that
works for attachEvent and DOM0 without leaking memory in IE. Thinking
about it now, starting off with something like the above may have led
to a simpler solution, though I think what I came up with will be
faster.

We should revisit this when the CWR addEventListener wrapper comes up
for discussion.
 
P

Peter Michaux

On Feb 12, 12:46 pm, Peter Michaux <[email protected]> wrote:
This isn't ideal as it relies on setAttribute, but it does work for
Firefox. Alternatively, you could define the onclick attribute in the
markup.

var el = getAnElement(), dom0 = true;
if (typeof el.onclick == 'undefined' &&
isHostMethod(el, 'setAttribute')) {
el.setAttribute('onclick', '(function() {})');
dom0 = (typeof el.onclick == 'function');
el.setAttribute('onclick', '');

}


The setAttribute spec specifically states that the second argument is
not parsed and is treated like a string literal

<URL: http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-F68F082>

I don't like to use a test that will break if a browser becomes _more_
standards compliant. If Firefox starts to follow the standard the
above test will stop working.

---------

Here is a test that checks inline handlers are supported. Not related
to my initial question exactly. If they are not then I imagine that,
at best, the onclick attribute of the firstChild would be a string.

var el = document.createElement('div');
el.innerHTML = '<span onclick="alert(\'hi\');">hi</span>';
typeof el.firstChild.onclick;


It works in IE5+ and recent FF, S and O.

Peter
 
D

David Mark

The setAttribute spec specifically states that the second argument is
not parsed and is treated like a string literal
Absolutely.


<URL:http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-....>

I don't like to use a test that will break if a browser becomes _more_
standards compliant. If Firefox starts to follow the standard the
above test will stop working.

I don't follow you there. If you add an onclick attribute, the DOM
should return a function when the onclick property is evaluated
(assuming it supports the DOM0 event model.)
---------

Here is a test that checks inline handlers are supported. Not related
to my initial question exactly. If they are not then I imagine that,
at best, the onclick attribute of the firstChild would be a string.

var el = document.createElement('div');
el.innerHTML = '<span onclick="alert(\'hi\');">hi</span>';
typeof el.firstChild.onclick;

That's roughly the same thing, but it uses the non-standard innerHTML
property.
 
P

Peter Michaux

I don't follow you there. If you add an onclick attribute, the DOM
should return a function when the onclick property is evaluated
(assuming it supports the DOM0 event model.)

setAttribute takes two string arguments. The second string argument is
not supposed to be parsed in anyway. So if the first string argument
is "onclick" and the second string argument is '(function() {})' then
the value of the element's onclick attribute should be a string
'(function() {})'. That is, the second string argument should not have
been converted to a function. That is how I read the section of spec
that I linked. The key bit being "This value is a simple string".

I think I'm interpreting the spec correctly but even if my
interpretation is wrong, the spec does not say anything along the
lines of "if the string can be evaluated as JavaScript and the
attribute name starts with 'on' then it is converted to a javascript
function." I would want to see something like that before I depended
on that behavior.

That's roughly the same thing, but it uses the non-standard innerHTML
property.

I don't think it is the same thing because the onclick attribute in
HTML is supposed to contain script. You can see in the following table
that the type of the value of an on* attribute is %script

<URL: http://www.w3.org/TR/html401/index/attributes.html>

Peter
 
D

David Mark

setAttribute takes two string arguments. The second string argument is
not supposed to be parsed in anyway. So if the first string argument
is "onclick" and the second string argument is '(function() {})' then
the value of the element's onclick attribute should be a string
'(function() {})'. That is, the second string argument should
not have

Certainly the attribute value will be a string.
been converted to a function. That is how I read the section of spec

It isn't converted to a function. The DOM simply interprets it as a
function (in implementations that support DOM0 events.)
that I linked. The key bit being "This value is a simple string".

If you set a disabled attribute to the string "disabled", the DOM will
interpret that the element is disabled and will return a boolean
(true) for the disabled property. AFAIK, IE is the only browser that
mixes up the document tree with the DOM (which requires a previously
discussed workaround to make it behave properly.)
I think I'm interpreting the spec correctly but even if my
interpretation is wrong, the spec does not say anything along the
lines of "if the string can be evaluated as JavaScript and the
attribute name starts with 'on' then it is converted to a javascript
function." I would want to see something like that before I depended
on that behavior.



I don't think it is the same thing because the onclick attribute in
HTML is supposed to contain script. You can see in the following table

Both versions use DOM manipulation to mutate the underlying document
(or fragment in your case.) AFAIK, the DOM will reflect the changes
to the document the same for each. The difference is that yours uses
innerHTML, which has no standard specification. The only possible
benefit I can see to using innerHTML for this is that it will work in
IE without a setAttribute workaround.
 
P

Peter Michaux

not have

Certainly the attribute value will be a string.


It isn't converted to a function. The DOM simply interprets it as a
function (in implementations that support DOM0 events.)


You are right and this is not completely intuitive to me (obviously
since I was wrong).

I did a little firebug console session
"string"

"(function(){})"
"function"
onclick(event)


And then with an error in the string
"string"

"foobar*#$)"
illegal character
foobar*#$)
illegal character
foobar*#$)


It is interesting that the error comes only when trying to access the
el.onclick attribute directly. The JavaScript engine does the
conversion at that time which seems like a good idea.

If you set a disabled attribute to the string "disabled", the DOM will
interpret that the element is disabled and will return a boolean
(true) for the disabled property. AFAIK, IE is the only browser that
mixes up the document tree with the DOM (which requires a previously
discussed workaround to make it behave properly.)

I'll think about this some more for the original problem.

Thank you,
Peter
 
P

Peter Michaux

[snip]
Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick? I have thought about that one and I don't think it requires
a workaround. If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally. As long as the page
is designed to work without script, this should not cause an issue. I
know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.

If a workaround for Safari is required then I think all browsers
should use it and it should be the DOM0 style of work around.

However, I did think up a test for the Safari problem. I don't
actually have a broken Safari around but I think the following idea
should work. This is just the idea and needs its own feature testing
before it runs. This is an onerous test for the browser to run and
cannot run until the DOM is ready.


var iframe = document.createElement('iframe');
document.body.appendChild(iframe);

// get the iframe as a window object
var iframe = window.frames[window.frames.length-1];
var d = iframe.document;
d.open();
d.write('<html><head><title>test</title></head>' +
'<body><h1>hi</h1><a id="fooLink" href="#foo">' +
'click me</a></body></head>');
d.close();

var link = d.getElementById('fooLink');
// comment out the next line to see the test fail
link.addEventListener('click', function(e) {e.preventDefault()});

var e = d.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, iframe, 1, 0, 0, 0, 0,
false, false, false, false, false, null);
link.dispatchEvent(e);

setTimeout(function() {
if (/foo/.test(iframe.location)) {
alert('failed');
}
else {
alert('passed');
}
}, 100);



Peter
 
P

Peter Michaux

Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick? I have thought about that one and I don't think it requires
a workaround.


I think the workaround is tiny and doesn't require using DOM0
listeners how it is done in YUI. They basically have two entire Event
libraries in one.

Below is the idea for click events. I suppose a user cannot cancel the
bubble for this to work. I just thought this up. Does it have any
merit?


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>

<title>safari click test</title>

<script type="text/javascript">

var prevented = false;

var preventDef = function(e) {
if (e.preventDefault) {
prevented = true;
e.preventDefault();
}
else {
e.returnValue = false;
}
};

var check = function() {
var result = !prevented;
prevented = false;
return result;
};

// use only one DOM0 handler
document.documentElement.onclick = check;

var handleClick = function(e) {
preventDef(e);
};

window.onload = function() {
document.getElementById('foo'
).addEventListener('click',
handleClick, false);
};

</script>


</head>
<body onclick="check(event);">

<p><a id="foo" href="#foo">click</a></p>

</body>
</html>
 
D

David Mark

[snip]
Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick?  I have thought about that one and I don't think it requires
a workaround.  If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally.  As long as the page
is designed to work without script, this should not cause an issue.  I
know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.

If a workaround for Safari is required then I think all browsers
should use it and it should be the DOM0 style of work around.

However, I did think up a test for the Safari problem. I don't
actually have a broken Safari around but I think the following idea
should work. This is just the idea and needs its own feature testing
before it runs. This is an onerous test for the browser to run and
cannot run until the DOM is ready.

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);

// get the iframe as a window object
var iframe = window.frames[window.frames.length-1];
var d = iframe.document;
d.open();
d.write('<html><head><title>test</title></head>' +
                   '<body><h1>hi</h1><a id="fooLink"href="#foo">' +
                   'click me</a></body></head>');
d.close();

var link = d.getElementById('fooLink');
// comment out the next line to see the test fail
link.addEventListener('click', function(e) {e.preventDefault()});

var e = d.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, iframe, 1, 0, 0, 0, 0,
                 false, false, false, false, false, null);
link.dispatchEvent(e);

setTimeout(function() {
  if (/foo/.test(iframe.location)) {
    alert('failed');
 }
 else {
    alert('passed');
  }

}, 100);

The timeout bothers me and are you sure the Safari versions in
question support initMouseEvent?

All in all, I think this one to design out of the system (i.e. don't
rely on preventDefault working for click events.) Better still, try
to avoid using links as buttons. That reminds me, I really need to
dig up an old property sheet I wrote that used (dynamically created)
buttons for tabs. It didn't require a lot of CSS, looked just like a
strip of tabs and worked well in every browser I tested (NS6.2-
present, Windows Safari, Opera 9, FireFox 2, IE5-present.) Let me
know when you get to the point of styling the tabs for your example
widget...
 
D

David Mark

I think the workaround is tiny and doesn't require using DOM0
listeners how it is done in YUI. They basically have two entire Event
libraries in one.

Yes, I made DOM0 support an optional module for this reason. It
strikes me that some applications may need to know if addEventListener/
attachEvent are actually supported or not (without resorting to
detecting the individual methods.) So if DOM0 support is excluded,
attachListener (and the others) are created only if the DOM2 event
model is available. The exception is attachDocumentReadyListener,
which always uses a DOM0 load listener as a fallback (too many
unrelated features would be short-circuited in DOM0 browsers without
it.)
Below is the idea for click events. I suppose a user cannot cancel the
bubble for this to work. I just thought this up. Does it have any
merit?

You lost me on the "user cannot cancel the bubble" point.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>

  <title>safari click test</title>

  <script type="text/javascript">

    var prevented = false;

    var preventDef = function(e) {
      if (e.preventDefault) {

You want to check if e exists first.
        prevented = true;
        e.preventDefault();
      }
      else {
        e.returnValue = false;
      }
    };

    var check = function() {
      var result = !prevented;
      prevented = false;
      return result;
    };

    // use only one DOM0 handler
    document.documentElement.onclick = check;

    var handleClick = function(e) {
      preventDef(e);
    };

    window.onload = function() {
      document.getElementById('foo'
       ).addEventListener('click',
                   handleClick, false);
    };

  </script>

</head>
<body onclick="check(event);">

  <p><a id="foo" href="#foo">click</a></p>

</body>
</html>

It seems you are mixing up event bubbling with default actions. You
could test stopPropagation with this, but that method isn't broken in
any browser that I know of.
 
P

Peter Michaux

Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick? I have thought about that one and I don't think it requires
a workaround. If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally. As long as the page
is designed to work without script, this should not cause an issue. I
know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.
If a workaround for Safari is required then I think all browsers
should use it and it should be the DOM0 style of work around.
However, I did think up a test for the Safari problem. I don't
actually have a broken Safari around but I think the following idea
should work. This is just the idea and needs its own feature testing
before it runs. This is an onerous test for the browser to run and
cannot run until the DOM is ready.
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
// get the iframe as a window object
var iframe = window.frames[window.frames.length-1];
var d = iframe.document;
d.open();
d.write('<html><head><title>test</title></head>' +
'<body><h1>hi</h1><a id="fooLink" href="#foo">' +
'click me</a></body></head>');
d.close();
var link = d.getElementById('fooLink');
// comment out the next line to see the test fail
link.addEventListener('click', function(e) {e.preventDefault()});
var e = d.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, iframe, 1, 0, 0, 0, 0,
false, false, false, false, false, null);
link.dispatchEvent(e);
setTimeout(function() {
if (/foo/.test(iframe.location)) {
alert('failed');
}
else {
alert('passed');
}

The timeout bothers me

It bothers me a lot. The whole thing is awful but it is in the group
archives and if someone wants to make a test this may help them start.
and are you sure the Safari versions in
question support initMouseEvent?

I'm not sure.
All in all, I think this one to design out of the system (i.e. don't
rely on preventDefault working for click events.) Better still, try
to avoid using links as buttons. That reminds me, I really need to
dig up an old property sheet I wrote that used (dynamically created)
buttons for tabs. It didn't require a lot of CSS, looked just like a
strip of tabs and worked well in every browser I tested (NS6.2-
present, Windows Safari, Opera 9, FireFox 2, IE5-present.) Let me
know when you get to the point of styling the tabs for your example
widget...

I suppose for marketing purposes the tabbed pane should look pretty.
Then people will think the JavaScript is better :-S

Peter
 
D

David Mark

[snip]
Is this in regard to the Safari 1.x preventDefault bug on click/
dblclick?  I have thought about that one and I don't think it requires
a workaround.  If, for instance, a user of Safari 1.x clicks a link
that has a click event listener and preventDefault fails, the browser
will fire the event and then navigate normally.  As long as the page
is designed to work without script, this should not cause an issue.  I
know some libraries (e.g. YUI) sniff for the older Safari versions and
fallback to DOM0 for click/dblclick, but I think that is a mistake.
If a workaround for Safari is required then I think all browsers
should use it and it should be the DOM0 style of work around.
However, I did think up a test for the Safari problem. I don't
actually have a broken Safari around but I think the following idea
should work. This is just the idea and needs its own feature testing
before it runs. This is an onerous test for the browser to run and
cannot run until the DOM is ready.
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
// get the iframe as a window object
var iframe = window.frames[window.frames.length-1];
var d = iframe.document;
d.open();
d.write('<html><head><title>test</title></head>' +
                   '<body><h1>hi</h1><a id="fooLink" href="#foo">' +
                   'click me</a></body></head>');
d.close();
var link = d.getElementById('fooLink');
// comment out the next line to see the test fail
link.addEventListener('click', function(e) {e.preventDefault()});
var e = d.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, iframe, 1, 0, 0, 0, 0,
                 false, false, false, false, false, null);
link.dispatchEvent(e);
setTimeout(function() {
  if (/foo/.test(iframe.location)) {
    alert('failed');
 }
 else {
    alert('passed');
  }
}, 100);
The timeout bothers me

It bothers me a lot. The whole thing is awful but it is in the group
archives and if someone wants to make a test this may help them start.
and are you sure the Safari versions in
question support initMouseEvent?

I'm not sure.
All in all, I think this one to design out of the system (i.e. don't
rely on preventDefault working for click events.)  Better still, try
to avoid using links as buttons.  That reminds me, I really need to
dig up an old property sheet I wrote that used (dynamically created)
buttons for tabs.  It didn't require a lot of CSS, looked just like a
strip of tabs and worked well in every browser I tested (NS6.2-
present, Windows Safari, Opera 9, FireFox 2, IE5-present.)  Let me
know when you get to the point of styling the tabs for your example
widget...

I suppose for marketing purposes the tabbed pane should look pretty.
Then people will think the JavaScript is better :-S

LOL. But seriously, this was as "pretty" as a strip of tabs can be.
IIRC, my aim at the time was to make it look like a Windows property
sheet.
 
P

Peter Michaux

I think the workaround is tiny
[snip]
Below is the idea for click events. I suppose a user cannot cancel the
bubble for this to work. I just thought this up. Does it have any
merit?

You lost me on the "user cannot cancel the bubble" point.

The function called "check" needs to run as a DOM0 event handler and
return false to actually make the prevent default happen in the broken
Safari browsers.

I think there could be workarounds so that cancel bubble would still
be allowed.

You want to check if e exists first.










It seems you are mixing up event bubbling with default actions.

In a broken Safari browser, at least one DOM0 handler for the event
needs to return false to prevent the default behavior. The normal
solution is to use legacy handlers for click and dblclick in so the
actual handler that wants to prevent default will return false.
However why not just let the event bubble up the dom a bit and then
have the document.documentElement.onclick handler be the one to return
false. I think the above code might be good.
You
could test stopPropagation with this, but that method isn't broken in
any browser that I know of.

I don't follow. The above isn't a test. It is the workaround.

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top