So I did a little work on a colour picker module adding the ability to parse human readable colours. I leveraged .getComputedStyle() to perform the conversion.
I implemented detection of the feature (should be IE 9+) with:
window.hasOwnProperty('getComputedStyle')
This is when I noticed some strange behavior. In Chrome and FF this reported true as expected. However in IE 11 (which does support it) it reported false.
I'm a little stumped as to why this is happening. I've performed other ways of checking its support. I'm stumped however as to why IE reports false whilst it does support it.
Not too sure if this is overkill but this fiddle simply logs the response so you can see for yourself. https://jsfiddle.net/xrgrgrhe/
Don't perform feature detection in this way; browsers aren't always consistent about where certain properties and methods are defined on the prototype chain. Instead, simply access the property:
if ( window.getComputedStyle ) {
/* Proceed to use window.getComputedStyle */
}
Functions are truthy, while undefined is falsy. As a result, this test will pass if the method is defined anywhere on the prototype, rather than directly on the Window instance object.
For what it's worth, the original test in the question also returns true in Microsoft Edge.
Related
TDLR:
The central question is:
Is it true that with IE, a Web test object´s .Object method and all "sub-methods" are directly providing access to the original DOM objects, while with Chrome and FireFox, UFT does not return the original DOM object instances, but clones, copies, or some other representative instances?
Details:
Playback steps that work in conjunction with IE, and that should work with all supported browsers according to the documentation, fail in conjunction with Chrome and Firefox for no good reason.
An analysis shows that this is caused by an unexpected behavior or bug in UFT.
Therefore I describe the problem in a general way. (Sorry, don´t have a SSCE yet.)
In particular:
If I use T T.Object.parentNode on the UFT level of a test object to reference the parent element, and then call .childNodes there to determine the immediate children of the parent element, then the following applies:
Expected:
The list provided by childNodes contains among other possible instances also the instance T.Object.
Actual:
The list supplied by childNodes contains (among other possible children) the T.Object only if IE is the browser in which the DOM is located.
If Chrome or Firefox is used, the list provided by T.Object.parentNode.childNodes contains an element that represents exactly the same DOM element as T.Object.parentNode, but it is not the same object instance, i.e. there is no object instance E in the list provided by childNodes for which the UFT VBScript expression "E is T.Object.parentNode" evaluates to true.
On JavaScript level it can be shown that the assumption "A.parentNode.childNodes returns a list containing the object instance A" is always fulfilled (where A is any element (except the root) with sub-elements).
On the UFT level, however, it can be understood that the assumption only applies in connection with IE, but not in connection with Chrome or Firefox.
But the documentation of UFT claims to make the native DOM object directly accessible via .object, and from this we conclude that calls and return values of methods of such DOM objects have to be passed through directly.
Instead, for Chrome and Firefox UFT seems to have a mechanism so that, for example, lists (like the NodeList provided by childNodes) are not passed through unaltered as a function result, but the list elements are cloned, or something similar, so that it is not the native instances that are provided, but only copies/representatives/clones.
Is this true? Then I consider this a bug, either in the documentation, or in UFT's Chrome and Firefox support.
Is there a workaround?
I am just now starting to address Chrome and Firefox as target browsers for various reasons, and due to above circumstances, central generic code that is used breaks, quite unexpectedly for me, and I don't know of any generic workaround, so I would have to develop a case-specific individual workaround for each affected use, of which I have literally hundreds.
Any suggestions?
It's true that in IE UFT exposes the actual COM object used by IE via the .Object property. It is also true that for all other browsers (Firefox, Chrome, Edge, Mobile) UFT creates a wrapper object which forwards invocations to the browser's native DOM element (since those browsers don't expose their DOM as COM objects).
If I understand your question correctly you're depending on COM's object identity rules. In a previous (professional) life I worked on the COM wrappers for UFT and I don't recall our considering this as a use-case. You may open a defect for UFT, I'm not sure how feasible it is to fix this, more likely they will say it's a limitation and update the documentation.
If you want to check if a .Object object refers to the same underlying DOM element I would suggest setting some property yourself, something like:
counter = 0
Function UniqueValue(obj)
If TypeName(obj.unique_value) = "Empty" Then
obj.unique_value = "unique_value_" & counter
counter = counter + 1
End If
End Function
' After this The element will have "unique_value_0" and the link "unique_value_1"
UniqueValue Browser("Example Domain").Page("Example Domain").WebElement("Example Domain").Object
UniqueValue Browser("Example Domain").Page("Example Domain").WebElement("Example Domain").Object
UniqueValue Browser("Example Domain").Page("Example Domain").Link("More information...").Object
I'm in the process of refactoring some code that someone else wrote. There is function that uses:
!!('ontouchstart' in window)
I've seen this used in other projects: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js#L40
And in a Stackoverflow answer: https://stackoverflow.com/a/4819886/1127635
But it seems like it could be slower than alternatives: http://jsperf.com/hasownproperty-vs-in-vs-undefined/12
So why use this possibly slower alternative? What browsers don't support other solutions?
Both of your alternative tests are flawed in some way:
window.ontouchstart !== null tests for a non-null listener. Testing the value of ontouchstart is a risky approach because libraries or other code might change the value of ontouchstart. Testing the value is a bad approach; it would be much better to test for the existence of the property itself, which brings us to your next proposed test...
window.hasOwnProperty('ontouchstart') tests if the window object has its own ontouchstart property. In some browsers (I've just confirmed this on Chrome 37 and IE9), window doesn't have its own on-event properties; instead, they are properties of window.__proto__.
We shouldn't test for a value (because previous code may have changed the value before we run our code) and we can't test for window's own property, because browser differ in their implementation of where event listener properties exist in window's prototype chain. So, our most consistent option is to test whether the property exists (regardless of value) anywhere in window's prototype chain. This is exactly what we do with the in operator.
Of course, if someone else's code runs before our test, they could add an ontouchstart property where there originally wasn't one. Testing support for events with absolute rigor simply isn't possible and it's an awful business.
I recently faced a problem with determining browsers' support for certain DOM features. One of them was Element.children feature, which is still causing me headache. I have the following line in my code:
var NATIVE_CHILDREN = Element.prototype.hasOwnProperty('children');
It is supposed to check if the browser supports Element.children -feature [https://developer.mozilla.org/en/DOM/Element.children].
According to MDN and quick testing, all the major browsers support this feature.
On Firebug on Firefox, value of NATIVE_CHILDREN is expectedly true. Surprisingly, on Chrome, Safari and Opera the value is false (unfortunately I don't have accees to machine with Windows to check what IE thinks about it).
According to DOM4 - Free Editor's Draft 5 April 2012 [http://dom.spec.whatwg.org/#element], children should be part of Element object's prototype. Apperantly Chrome's, Safari's and Opera's Element object doesn't contain such a method!
I have tried checking the prototypes of HTMLCollection and Node (I also tested HTMLParagraphElement and HTMLBodyElement), but none of them seem to contain method called 'children' (except on Firefox). How can I make my test to work cross-browser? I don't want to use any external libraries for this, because this is for my own little library.
I think the reason why this test might return false on Chrome is that you're checking on the prototype. This is not the best way, for several reasons:
Different browsers can (and do) use different implementations of the prototype, some prototypes are not accessible in IE for instance. In this case, I'd say your issue is the result of chrome relying on the (non standard) __proto__ property rather then prototype. I can't remember when, but I had a similar issue with chrome, and this was the source if the problem.
AFAIK all browsers have a children property for their elements, though they behave differently in some cases, so I have some doubt as to the use of checking the existence of such a property.
If you still want to check this, why not use document.body.hasOwnProperty('children')? Returns true on FF, Chrome, Safari and IE.
That's because some engines only slap on the children attribute on element creation. A quick test in the Chrome console shows that:
Element.prototype.hasOwnProperty( 'children' ); //false
//however,
document.createElement( 'foo' ).hasOwnProperty( 'children' ); //true
//or even
!!document.createElement( 'foo' ).children; //true
Non-function properties often don't appear on the prototype, for a simple reason - they aren't set yet, and it doesn't make sense if they will be. Element.prototype doesn't have any children, because it's not an element, it's a prototype for elements.
It is safer to check if( 'children' in document.body) than to mess around with prototypes. Important to note the quotes, if not a variable children might be used/created...
According to QuirksMode, all browsers support children except Firefox 3 (which is a surprise to me, since it worked when I tested in that browser...), so there should be no need to test for this property.
There are lots of DOM/CSS inconsistencies between browsers. But how many core JS differences are there between browsers? One that recently tripped me up is that in Firefox, setTimeout callback functions get passed an extra parameter (https://developer.mozilla.org/en/window.setTimeout).
Also, now that browsers are implementing new functions (e.g. Array.map), it can get confusing to know what you can/can't use if you are trying to write code that must work on all browsers (even back to IE6).
Is there a website that cleanly organizes these types of differences?
I find QuirksMode and WebDevout to have the best tables regarding CSS and DOM quirks. You can bridge those incompatibilities with jQuery. There is also this great list started by Paul Irish which includes pretty much any polyfill you could ever need, including ones for ES5 methods such as Array.map.
There doesn't appear to be anything out there that clearly outlines all these issues (very surprising actually). If you use jQuery there is a nice browser compatibility doc section that outlines supported browsers and known issues. I just deal with issues as they come up (as you should be browser testing in all cases anyways) and you can document them if you want to make sure you are coding correctly or if you run into issues and need to know fixes. It's easy to find issues when you do a quick search on a particular topic.
Well, I'm going to open up a CW:
Prior to Firefox 4 Function.apply only accept an Array, not an array-like object. Ref MDC: Function.apply
Some engines (which ones?) promote the result of String.prototype methods from string to String. Ref a String.prototype's "this" doesn't return a string?
Firefox 4 may insert "event loops" into seemingly synchronous code. Ref Asynchronous timer event running synchronously ("buggy") in Firefox 4?
Earlier Firefox versions would accept a trailing , in object literals. Ref trailing comma problem, javascript (Seems "fixed" in FF6).
Firefox and IE both treat function-expression productions incorrectly (but differently).
Math.round/Math.toFixed. Ref Math.round(num) vs num.toFixed(0) and browser inconsistencies
The IE vs. W3C Event Model -- both are missing events/features of the other.
When using ECMAScripts typeof on plugin elements (i.e. embed or object), Safari & FireFox return "function":
typeof(window.document['myPlugin']) // "function"
This can't be influenced on the plugin side, as the browser doesn't call the plugin here. Funny enough, in IE the same line evaluates to "object".
Is that simply implementation dependent behaviour as per ECMAScript §11.4.3 or am i missing something here?
The specs are all very vague when it comes to how typeof should behave with a plugin object, since ECMAScript wasn't written with plugins in mind. Hence on IE with an activex control it will tend to respond with "object" because that's how they decided to deal with it; Firefox and I believe Safari both respond with "function" because that is how they determined to deal with it.
Both answers make sense; remember that when you access the plugin with document.getElementById("myPlugin"), you aren't just getting a reference to the plugin, you're getting a reference to the HTML element that hosts the plugin, which happens to proxy calls to the plugin. Being an HTML element, it has other properties and methods that you don't even know about.
It does seem like object would make more sense in this case, but an object generally does not, cannot have a default function, and so my guess is that firefox determined to respond that it is a function() because there is no way in the NPAPI to query to see if the default function exists, short of calling InvokeDefault. while you can call a default method on an ActiveX IDispatch interface as well, it really seems more like an incidental side-effect than a design feature.
Not a terribly scientific answer, but one that might help.