Cross-browser setOpacity() without browser sniffing?

P

petermichaux

Hi,

Does anyone have a a cross-browser setOpacity function that does not
use browser sniffing? I looked at the Yahoo! UI function and it detects
IE by looking for window.ActiveXObject. I also looked at Scriptaculous
and it uses navigator.userAgent.

Thanks,
Peter


Roughly the Yahoo! UI bit of relavent code

var el = document.getElementById('foo');

if (window.ActiveXObject && typeof el.style.filter == 'string') { //
in case not appended
el.style.filter = 'alpha(opacity=' + val * 100 + ')';

if (!el.currentStyle || !el.currentStyle.hasLayout) {
el.style.zoom = 1; // when no layout or cant tell
}
} else {
el.style.opacity = val;
el.style['-moz-opacity'] = val;
el.style['-khtml-opacity'] = val;
}



The bit of Scriptaculous code

Element.setOpacity = function(element, value){
element= $(element);
if (value == 1){
Element.setStyle(element, { opacity:
(/Gecko/.test(navigator.userAgent) &&
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : null });
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element, {filter:
Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
Element.setStyle(element, {opacity: value});
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element,
{ filter:
Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
}
 
J

Joshie Surber

Hi,

Does anyone have a a cross-browser setOpacity function that does not
use browser sniffing? I looked at the Yahoo! UI function and it detects
IE by looking for window.ActiveXObject. I also looked at Scriptaculous
and it uses navigator.userAgent.

Thanks,
Peter


Roughly the Yahoo! UI bit of relavent code

var el = document.getElementById('foo');

if (window.ActiveXObject && typeof el.style.filter == 'string') { //
in case not appended
el.style.filter = 'alpha(opacity=' + val * 100 + ')';

if (!el.currentStyle || !el.currentStyle.hasLayout) {
el.style.zoom = 1; // when no layout or cant tell
}
} else {
el.style.opacity = val;
el.style['-moz-opacity'] = val;
el.style['-khtml-opacity'] = val;
}



The bit of Scriptaculous code

Element.setOpacity = function(element, value){
element= $(element);
if (value == 1){
Element.setStyle(element, { opacity:
(/Gecko/.test(navigator.userAgent) &&
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : null });
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element, {filter:
Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
Element.setStyle(element, {opacity: value});
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element,
{ filter:
Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
}

Never played with opacity, but it seems it would work if you did
something like:
el.style.filter = 'alpha(opacity=' + val * 100 + ')';
el.style.opacity = val;
el.style['-moz-opacity'] = val;
el.style['-khtml-opacity'] = val;
 
R

Richard Cornford

Does anyone have a a cross-browser setOpacity function
that does not use browser sniffing? I looked at the Yahoo!
UI function and it detects IE by looking for
window.ActiveXObject. I also looked at Scriptaculous and
it uses navigator.userAgent.
<snip>

You should not need to ask this question. The general principle of
feature detection is to devise a test that is as closely related
(preferably directly related) to the question that needs to be answered.
If your question is whether a browser supports IE-style filters then
test for the manifestation of filters; the - filters - collection of the
element:-

if(el.filters){
... //has filters collection.
}

- or the - filters - property of the style object:-

if(typeof el.style.filters == 'string'){
...//has filters property on style object..
}

(the latter being the most direct test)

Richard.
 
P

petermichaux

Hi Richard,

Richard said:
<snip>

You should not need to ask this question.

It seems like in the browser scripting world there are always suprises
due to bugs. I don't know about all the bugs. Also the Yahoo! UI guys
do know a thing or two and the fact that they double up with the
following two checks makes me wonder if something else is going on.

if (window.ActiveXObject && typeof el.style.filter == 'string')

The general principle of
feature detection is to devise a test that is as closely related
(preferably directly related) to the question that needs to be answered.
If your question is whether a browser supports IE-style filters then
test for the manifestation of filters; the - filters - collection of the
element:-

if(el.filters){
... //has filters collection.
}

- or the - filters - property of the style object:-

if(typeof el.style.filters == 'string'){
...//has filters property on style object..
}

(the latter being the most direct test)

Richard, you have helped me much in the past months. I do appreciate it
greatly. Due to your persistance, I am currently trying to get myself
off the Yahoo! UI code and also ultimately rid my Rails code of
Prototype.js. I am moving to the layered library approach that you
described previously.

Thank you,
Peter
 
R

Richard Cornford

It seems like in the browser scripting world there are always
suprises due to bugs. I don't know about all the bugs.

That does not men that most feature detection tests can be directly
deduced from pinning down the question that needs answering.
Also the Yahoo! UI guys
do know a thing or two

Whatever they may know it is not preventing their code form manifesting
non-joined up logic.
and the fact that they double up with the
following two checks makes me wonder if something
else is going on.

if (window.ActiveXObject && typeof el.style.filter == 'string')

Voodoo. Too many browsers have a global ActiveXObjet constructor (real
or object inference defeating dummy) for testing it in any context other
than a desire to instantiate an ActiveX object to be rational.

<snip>

This should have been:-

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

Richard.
 
R

Richard Cornford

Richard said:
(e-mail address removed) wrote:

That does not men that most feature detection tests can be
directly deduced from pinning down the question that needs
answering.
<snip>

Should have read:-

"That does not mean that most feature detection tests cannot be
directly
deduced from pinning down the question that needs answering."

Richard.
 
M

Matt Kruse

Richard said:
"That does not mean that most feature detection tests cannot be
directly deduced from pinning down the question that needs answering."

Most, yes.

But it must be acknowledged that feature-detection is not
behavior-detection.

If a specific browser has a known bug or odd behavior associated with a
feature, simply detecting the feature won't let you fix its faulty behavior.
Although you can write (sometimes complex) code to verify the behavior of a
feature, doing such tests for every feature which might potentially have a
bug in some browser somewhere is wasteful. Sometimes the best way to support
browsers with bugs or quirks is to check for a specific browser in the user
agent string and code accordingly, from WITHIN the proper feature-detection
block.

For example,

if (document.feature && document.feature.method) {
if (user agent is Browser 1.5.6.7 on Mac) {
// Do something specific
}
else {
document.feature.method();
}
}

This way, browser detection will never be executed unless the browser passes
all feature detection tests. If the browser is actually some other browser
with a fake user agent string, it may not pass the feature tests and will
never go into the wrong block to begin with. If it does pass the tests, and
is pretending to be the browser with the bug, then surely the user can
expect code to behave as if they are actually using that browser and deal
with the results.

Alternatively, you could re-define the feature to behave like
standards-compliant browsers so that user agent tests are not required
within the core code at all, and feature detection will work correctly.
 
R

Richard Cornford

Matt said:
Most, yes.

But it must be acknowledged that feature-detection is not
behavior-detection.

Behaviour can be a feature and can often be detected (or worked
around/sidestepped when it cannot).
If a specific browser has a known bug or odd behavior associated with a
feature, simply detecting the feature won't let you fix its faulty behavior.
Although you can write (sometimes complex) code to verify the behavior of a
feature, doing such tests for every feature which might potentially have a
bug in some browser somewhere is wasteful.

How does a known bug in a specific browser become "doing such tests for
every feature which might potentially have a bug in some browser
somewhere"?
Sometimes the best way to support browsers with bugs or
quirks is to check for a specific browser in the user
agent string

No, always the worst thing to do is to start being interested in the
user Agent string. It is not specified as a source of information so
there is no technical foundation for using it as one, and it has
historically been used to mislead software attempting to identify web
browsers so it can be expected to be insufficient for the task.

In all cases where feature detection cannot be applied (and there are
many fewer of those than some people seem to think) object inference
based on a set of features is most likely to give the best indication
on the type of browser.
and code accordingly, from WITHIN the proper feature-detection
block.

For example,

if (document.feature && document.feature.method) {
if (user agent is Browser 1.5.6.7 on Mac) {

You can only say "user agent is Browser 1.5.6.7 on Mac" if you can
actually determine that, which you cannot. the best you can say from a
UA string test is that the UA string conforms (in some way) with the
normal/default/expected UA string of "Browser 1.5.6.7 on Mac".
// Do something specific
}
else {
document.feature.method();
}
}

This way, browser detection will never be executed unless the browser
passes all feature detection tests. If the browser is actually some other
browser with a fake user agent string, it may not pass the feature
tests and will never go into the wrong block to begin with. If it does
pass the tests, and is pretending to be the browser with the bug, then
surely the user can expect code to behave as if they are actually
using that browser and deal with the results.
<snip>

So once again you are blaming the user because a strategy with no
technical foundation results in code that only 'mostly works'.

Richard.
 
M

Matt Kruse

Richard said:
[snip]
In all cases where feature detection cannot be applied (and there are
many fewer of those than some people seem to think) object inference
based on a set of features is most likely to give the best indication
on the type of browser.

I disagree. Object inference might return the same results between version
1.5 and 1.51 of a browser, but version 1.5 might have a very specific bug in
a feature. If you rely on that feature, and the bug can be "fixed" by code,
then you can use browser sniffing but not object inference. In fact, object
inference fails as time goes on - for example, more browsers supporting
document.all and breaking old scripts which assumed things based on
document.all existing.
You can only say "user agent is Browser 1.5.6.7 on Mac" if you can
actually determine that, which you cannot. the best you can say from a
UA string test is that the UA string conforms (in some way) with the
normal/default/expected UA string of "Browser 1.5.6.7 on Mac".

Within that code, what you're saying is "all my object detection code has
determined that the browser behaves consistently with what I expect of
Browser 1.5.6.7 on Mac, and it's telling me that that's what is is." So
trust it!

Instead, you're suggesting we ignore what it's teling us and the
feature-detection evidence that supports it, and instead look at unrelated
features and guess at whether or not the feature we want is broken. That
seems much more subject to failure.

If a browser has document.all, and supports ActiveX, and claims to be IE6,
then that's a pretty convincing argument. Otherwise, it's someone who is
really trying to mess with things on purpose, and if that's the case then
they probably expect problems. Typical users will probably never have these
kidns of issues.
 
R

Richard Cornford

Matt said:
Richard said:
[snip]
In all cases where feature detection cannot be applied (and there are
many fewer of those than some people seem to think) object inference
based on a set of features is most likely to give the best indication
on the type of browser.

I disagree. Object inference might return the same results between
version 1.5 and 1.51 of a browser, but version 1.5 might have a very
specific bug in a feature.

But UA strings are nether required to include browser version
information nor are they observed to be accurate in the 'version
information' they do provide".
If you rely on that feature, and the bug can be "fixed" by code,
then you can use browser sniffing but not object inference. In fact, object
inference fails as time goes on - for example, more browsers supporting
document.all and breaking old scripts which assumed things based on
document.all existing.

I was not thinking of half-witted object inference, which would be as
bad as UA string based browser detection. There are almost no single
object from which it would be possible to infer that a browser was a
particular type, but finding a browser that had a large set of features
believed to be unique to a particular browser, and did not have
features common to other browsers but known not to be supported on the
browser in question would be a better indicator that the test subject
was that browser than making deductions from what is no more than a
user-configurable sequence of characters. And in many cases precise
version of browsers can be inferred from a sufficient set of object
tests, as most actual browser development represents a sequence of
additions.
Within that code, what you're saying is "all my object detection code has
determined that the browser behaves consistently with what I expect of
Browser 1.5.6.7 on Mac, and it's telling me that that's what is is." So
trust it!

Was that supposed to mean something?
Instead, you're suggesting we ignore what it's teling us and the
feature-detection evidence that supports it, and instead look at unrelated
features and guess at whether or not the feature we want is broken. That
seems much more subject to failure.

If a browser has document.all, and supports ActiveX, and claims to
be IE6, then that's a pretty convincing argument.

And if a browser has _all_ off; global - ActiveXObject -, - attachEvent
-, - CollectGarbage -, - createPopup -, - ScriptEngine -, -
showModalDialog - and - showModelessDialog - properties that are of
fucntion type, - external -, - onafterprint - and - clientInformation'
properties of object type, activeElement -, - createStyleSheet -, -
execCommand -, - expando -, - fireEvent -, - onbeforedeactivate -, -
onbeforeeditfocus -, - ondatasetchanged -, - recalc -, - Script - and -
uniqueID - proprties on its document, and - addBehavior -, -
behaviorUrns -, - children -, - currentStyle -, - filters -, -
isContentEditable -, - oncellchange -, - onerrorupdate -, -
parentElement -, - removeBehavior -, - runtimeStyle - and -
setExpression - prperties on its elements, is reading a UA string that
says "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)" really a more
reliable indicator that the browser is Windows IE 6?
Otherwise, it's someone who is really trying to mess with things
on purpose, and if that's the case then they probably expect problems.

Users are allowed to "mess" with their User Agent strings. If they were
not allowed to then it would not be possible, when it certainly is not
only possible but a direct part of the browser's configuration in some
cases. As the User Agent header/string is not specified as a source of
information they should be no harm in the user changing it (as no
modification could make it mean less then the nothing it is specified
as meaning).
Typical users will probably never have these kidns of issues.

Ah, so user agent string based browser detection 'mostly works', and if
it does not then it's the user's fault?

Richard.
 
P

petermichaux

Richard said:
Voodoo. Too many browsers have a global ActiveXObjet constructor (real
or object inference defeating dummy) for testing it in any context other
than a desire to instantiate an ActiveX object to be rational.

I asked about this on the Yahoo! UI group as well. The author of the
code seems very friendly and responded that it was a bit of defensive
coding because of the audience they are targeting with their YUI
scripts. Interesting reasoning and I imagine writing library scripts
for widespread use is quite challenging: all the browser
incompatibilities and bugs and all the programmer incompatibilities and
bugs too.

<URL: http://groups.yahoo.com/group/ydn-javascript/message/4585>

So it looks like I can safely drop the "window.ActiveXObject &&" part
of the conditional.

- Peter
 
V

VK

Does anyone have a a cross-browser setOpacity function that does not
use browser sniffing? I looked at the Yahoo! UI function and it detects
IE by looking for window.ActiveXObject. I also looked at Scriptaculous
and it uses navigator.userAgent.

That is close related to your other question about Safari 1.x feature
test.
The "pure feature detection" is only possible for the ideal situations
where either the feature is presented and it works as documented across
all UA's with such feature - or the feature is not presented which is
easy to detect by say
if (typeof someObject.someMethodOrProperty == 'undefined')
or similar.

But as you are forced to act in the imperfect real word, the above
situation will be a rare exception rather than a rule.
Most of the time you have the twilight situation then the feature is
"presented" but
1) it is not actually implemented. It is just a loophole to say True to
trick possible feature tests. Many wannabes (especially older ones) are
bad on it.
or
2) it is implemented but the implementation is broken and requires
extra procedure to compensate the failure.
or
3) it is implemented but in the way UA's producers decided to go, not
in the standard way.

Say Safari 1.x generously covers the situations 2) and 3) and this
"coverage" differs dramatically from one minor version to another. So
say to really deal with Safari 1.x, a simple browsing sniffing is not
enough - you also have to sniff the version.

In the relevance to the opacity and IE: the ActiveXObject feature test
is not relevant to the opacity as IE's filters are not activated over
new ActiveXObject().
It must be a general combo test to branch onto IE-specific procedures
and workarounds.
 
R

Richard Cornford

I asked about this on the Yahoo! UI group as well. The author of
the code seems very friendly and responded that it was a bit of
defensive coding because of the audience they are targeting with
their YUI scripts.

As I said, it is voodoo. Of the browsers that I know have a global -
ActiveXObject - constructor only Winnows IE 5+ actually supports
filters. Preceding a test for the - filter - string on the - style -
object with a test that will not even pin the browser down to a
Microsoft product is just pointless.
Interesting reasoning

Hardly reasoning at all. You notice the author cannot be specific about
which browsers have which issues. It is just an excuse for doing
something that had no basis in reason in the first palce.
and I imagine writing library scripts
for widespread use is quite challenging: all the browser
incompatibilities and bugs and all the programmer
incompatibilities and bugs too.

Particularly challenging without a good grasp of javascript or a broad
experience of actual web browsers (both of which are evident in the
faults in the code).
<URL: http://groups.yahoo.com/group/ydn-javascript/message/4585>

So it looks like I can safely drop the "window.ActiveXObject &&"
part of the conditional.

It should never have been included in the first place; it was pure
voodoo.

Richard.
 
P

petermichaux

Richard said:
As I said, it is voodoo. Of the browsers that I know have a global -
ActiveXObject - constructor only Winnows IE 5+ actually supports
filters. Preceding a test for the - filter - string on the - style -
object with a test that will not even pin the browser down to a
Microsoft product is just pointless.


Hardly reasoning at all. You notice the author cannot be specific about
which browsers have which issues. It is just an excuse for doing
something that had no basis in reason in the first palce.

I think the reasoning makes sense for the Yahoo! UI audience. The
Yahoo! UI is integrated with legacy scripts which may assign directly
to style.filter without first checking it's existance. So for the
browsers that don't have ActiveXObject the YUI setOpacity() function
will still work. The check for ActiveXObject rescues the functionality
of setOpacity() in at least some of the browsers.

I suppose you could argue that they should let the functionality break
in those cases as an indicator that the legacy JavaScript should be
modified. I guess they didn't want to do that.

Peter
 
R

Richard Cornford

I think the reasoning makes sense for the Yahoo! UI audience.
The Yahoo! UI is integrated with legacy scripts which may
assign directly to style.filter without first checking it's
existance. So for the browsers that don't have ActiveXObject
the YUI setOpacity() function will still work. The check for
ActiveXObject rescues the functionality of setOpacity() in
at least some of the browsers.

That sounds like someone casting around for any excuse for doing
something when they have realised that they should not have been doing
it in the first place. But even if the true reasoning behind the
'decision' it is a poor approach.

It would be enough to observe that in browser supporting -
style.filter - the TITLE element is accessible, has a - style - object
and that object has a testable - filter - property that is a string. It
would be insane for anyone to directly set a filter on an element that
has no visible manifestation in a page so testing that property may be
the test closet to the issue where the possibility of other scripts
assigning to - style.filter - exists.

It is the mindset that thinks resorting to hit and miss browser sniffing
is an option that should be considered that keeps people from trying to
pin down their problem to the point where a direct feature test is
obvious.
I suppose you could argue that they should let the
functionality break in those cases as an indicator that
the legacy JavaScript should be modified. I guess they
didn't want to do that.

It might be better argued that if a browser has downloaded one mechanism
for assigning to - filter - it should using only that and not be
downloading two. That is exactly the sort of bloat due to needless
repetition that is an argument against monolithic general purpose
libraries as a concept in javascript.

On the other hand I suppose there is a possibility that a separate
sub-system may have been assigning other filters (say drop-shadows), but
then the Yahoo opacity mechanism would screw that up anyway by wiping
the other filter as it assigned its opacity filter. So the though
process that supposedly considered the behaviour of other scripts was
not actually carried through to an appreciation of all the applicable
possibilities.

Richard.
 
P

petermichaux

Hi Richard,

Richard said:
That sounds like someone casting around for any excuse for doing
something when they have realised that they should not have been doing
it in the first place. But even if the true reasoning behind the
'decision' it is a poor approach.

It would be enough to observe that in browser supporting -
style.filter - the TITLE element is accessible, has a - style - object
and that object has a testable - filter - property that is a string. It
would be insane for anyone to directly set a filter on an element that
has no visible manifestation in a page so testing that property may be
the test closet to the issue where the possibility of other scripts
assigning to - style.filter - exists.

Great solution. Of course I didn't know about that trick. Where is the
list of all these feature tests?

I doubt they thought of that test but perhaps some documents they are
considering don't even have a title element.
It is the mindset that thinks resorting to hit and miss browser sniffing
is an option that should be considered that keeps people from trying to
pin down their problem to the point where a direct feature test is
obvious.


It might be better argued that if a browser has downloaded one mechanism
for assigning to - filter - it should using only that and not be
downloading two. That is exactly the sort of bloat due to needless
repetition that is an argument against monolithic general purpose
libraries as a concept in javascript.

On the other hand I suppose there is a possibility that a separate
sub-system may have been assigning other filters (say drop-shadows), but
then the Yahoo opacity mechanism would screw that up anyway by wiping
the other filter as it assigned its opacity filter.

I mentioned this to the author and he wants to change this eventually.
They are actually quite interested in the thoughts of others and want
the library to be good. I think they might be developing under time
pressure and not be able to revist the code they want to.

Peter
 
R

Richard Cornford

Richard Cornford wrote:

Great solution. Of course I didn't know about that trick.
Where is the list of all these feature tests?

There is (and could never be) a list of feature detection tests. Feature
detection is a strategy with a method; you analyse the problem to pin
down the question that needs to be answered and the test that answers
that question in the most direct way (preferably with a one-to-one
relationship to the issue) will then usually be obvious. So, you want to
know when applying - style.filters - is meaningful so you test for
manifestations of - style.filters -, and you also want to put up other
scripts writing to - style.filters - on arbitrary elements so you test
the feature on an element that nobody (sane) would assign a filter to.
I doubt they thought of that test

So do I, but I think that they failed to think of that test because
instead of learning to do browser scripting well they have decided to
hide behind browser sniffing and 'mostly works'.
but perhaps some documents they are
considering don't even have a title element.

The TITLE element is the only element that is _required_ in a valid HTML
document. And they are error-corrected into existence if omitted on
actual browsers (including IE).

I mentioned this to the author and he wants to change this
eventually.

So I was correct in assuming that he had not thought through the issues
he was claiming to be acting to mitigate.
They are actually quite interested in the thoughts of others
and want the library to be good.

Even if the thoughts of others are that the style of library manifest is
inappropriate for browser scripting and so can never be 'good'? I think
Yahoo's libraries are in fact only a self-promotion exercise leveraged
off work they would otherwise have to do for themselves internally, so
the only actual intention is to avoid being subject to criticism for
creating faulty code.

Remember that because yahoo use 100% of their code themselves for them
it is an application specific framework not a general purpose library.
I think they might be developing under time
pressure and not be able to revist the code they want to.

Or it already does what Yahoo want it to do internally so additional
development is only worthwhile when it is to remove obvious subjects for
the criticism that would diminish its potential as a (cheep) promotional
exercise.

Richard.
 

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,996
Messages
2,570,237
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top