Document.createElement arity - javascript

This is related to but not exactly the same as this question. I recently had some code that broke on several browsers because it wrapped document.createElement with a decorator that curried the built-in function.
Opening up the chrome console and typing document.createElement.length yields 1, in FF/Safari, 2.
Why? Is it the optional type extension in mentioned in the other question (which oddly enough is a chrom(ium) thing for webcomponents' custom elements)? What does the standard say about this?
UPDATE
It is indeed (at least in FF) related to the typeExtension, document.createElement.toString() in the console returns
function createElement(tag, typeExtension) {
if (tag) {
tag = tag.toLowerCase();
}
if (typeExtension) {
typeExtension = typeExtension.toLowerCase();
}
var definition = getRegisteredDefinition(typeExtension || tag);
if (definition) {
if (tag == definition.tag && typeExtension == definition.is) {
return new definition.ctor();
}
if (!typeExtension && !definition.is) {
return new definition.ctor();
}
}
var element;
if (typeExtension) {
element = createElement(tag);
element.setAttribute("is", typeExtension);
return element;
}
element = domCreateElement(tag);
if (tag.indexOf("-") >= 0) {
implementPrototype(element, HTMLElement);
}
return element;
}
##UPDATE 2
github issue - see Jeremy's answer.

The source code you've quoted is from the Polymer team's Web Components polyfill, not the native browser implementation. If you try it in a page that doesn't include the polyfill, you'll get different results (Firefox on Ubuntu):
> document.createElement.length
1
> document.createElement.toString()
"function createElement() {
[native code]
}"
The reason you get 1 in Chrome is because it currently includes a native implementation of Web Components (which still has .length of 1), so the polyfill isn't being used.
The built-in function doesn't yet have .length of 2 in any browsers. However, the specification of createElement is going to change soon, so don't rely on this remaining true.

Related

Render react app in IE 11 gives me "you need to enable javascript to run this app"

I vent into internet options and enabled scripting.
I even added local host as a trusted site and every time the site render I see:
"root" is empty.
My app works in all other browsers but IE11.
What do I need to do to get this working?
EDIT:
Console is empty:
internet options:
This turned out to be something already know but in my scenario it was hard to figure out since no errors were present.
Something that was weird for me was even though the react part never rendered still my css got imported to the browser. I removed the css and once the site reloaded i got an error saying
"Object doesn't support property or method 'assign"
Soloution:
Added the following script just before the dist bundle got rendered
<script>
if (typeof Object.assign != 'function') {
Object.assign = function(target) {
'use strict';
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
target = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source != null) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
};
}
</script>
Found the answer here
Keeping the question and answer since the same error occured here (as in link above) but in a diffirent scenario, hopefully can guide others.
When targeting old browsers, you should always make sure you have the right polyfills.
You can follow React's official advice: https://create-react-app.dev/docs/supported-browsers-features
And use the official react-app-polyfill package.
This steps will make sure you won't run into random compatibility issues.

Internet Explorer: Ignore unknown ES6 syntax

To fix a bug that only occurs in Firefox, I need to use the loaded Promise of a FontFace. I currently use the following code for that:
if (document.fonts) {
for (var fontFace of document.fonts.values()) {
if (fontFace['family'] == fontFamily) {
fontFace.loaded.then(doStuff);
}
}
} else {
doStuff();
}
This works and only targets the browsers that support the font loading API. But because of the for .. of, Internet Explorer logs an error and stops the JS execution. Putting the code in a try .. catch block doesn't work, ignoring the error via window.onerror would probably work, but is quite hacky.
Is there a way to iterate over document.fonts.values that is also supported by IE or do you know a better way to use the loaded Promise in browsers that support it?
I'd recommend
const fontFace = Array.from(document.fonts).find(face => face.family === fontFamily);
if (fontFace) {
fontFace.loaded.then(doStuff);
}
Array.from creates an array from an iterable, and then you can use the normal Array.prototype.some to check for matches.
You could then simplify your whole check to
const fontFace = document.fonts &&
Array.from(document.fonts).find(face => face.family === fontFamily);
if (fontFace) fontFace.loaded.then(doStuff);
else doStuff();
assuming you want to run doStuff if not of the font faces match either.
Unfortunately, you can't use for-of iteration in your code, when it is supposed to run in an unsupported browser. The thing is that error occurs at the moment of parsing code before your condition will be executed and checked.
If you really want to use for-of, you will need to create a special JS-bundle for modern browsers or process your code with Babel to convert your code to es5-compatible.
I solved the problem with the following code:
if (document.fonts) {
var iter = document.fonts.values();
do {
var item = iter.next();
var fontFace = item.value;
if (fontFace && fontFace['family'] == fontFamilyStr) {
fontFace.loaded.then(doStuff);
}
} while (!item.done);
} else {
doStuff();
}
IE doesn't log an error anymore and the code works in Firefox / Chrome.

Issue with retrieving object data on IE 8 on Windows XP or 2003

This is an interesting problem that I am facing with JavaScript and IE8 on Windows XP and Windows 2003. I create an object on the page and then retrive information about that object (for example, its version). When trying to get the version, I am running this code:
var myObject = document.getElementById(objectId);
console.log(myObject.version);
What is interesting is that this code works on every single browser except IE8 on Windows XP and 2003. I've done some debugging and this is where things get interesting.
myObject is not null but myObject.version is undefined. So what I did is I added an alert in between so the code is now as follows:
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
The alert results in "undefined", however, the console.log is now resulting in the actual version. If I add an alert before this alert of anything (let's say alert("something")) then the second alert has the actual version now. I am assuming this is a timing issue (for some reason the object needs sometime to be able to provide the data stored in it?) but I am not sure what kind of timing issue this is or how to approach it.
Sorry for the long description but any help is appreciated.
document.getElementById doesn't return an object. It returns a DOM element. So, you expect to see a .version property in a DOM element, which by the official W3C specification is missing (or at least I don't know about this).
I'm not sure what you are expecting to see in .version, but if it is something custom then you should create a custom object like that:
var o = { version: "..." }
console.log(o);
You said that this may be a time issue. If that's true then I'll suggest to try to access the .version property after the DOM is fully loaded. You can use jQuery for the purpose:
$(document).ready(function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
});
You can add a setTimeout in your function till the .version property is there.
var f = function(callback) {
var check = function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
if(typeof myObject.version !== "undefined") {
callback(myObject.version);
} else {
setTimeout(check, 1000);
}
}
setTimeout(check, 1000);
}
What happens if you put the <script>...</script> tag with the js code at the end of the html file? In my opinion, the code is executed when the DOM is not ready. If you put it in the end, then it will be executed after it's loaded.

'Date' is undefined in IE9 in javascript loaded by FacePile

I'm currently getting an error within Facebook's FacePile code, and I'm baffled by the cause.
facepile.php loads a script which, among other things, has these lines (when pretty-printed):
...
o = document.createElement('script');
o.src = l[n];
o.async = true;
o.onload = h;
o.onreadystatechange = function() {
if (o.readyState in c) {
h();
o.onreadystatechange = null;
}
};
d++;
a.appendChild(o);
...
(a == document.body, d++ is irrelevant here)
This code loads a script with src = http://static.ak.fbcdn.net/rsrc.php/v1/yW/r/pmR8u_Z_9_0.js or something equally cryptic (the filename changes occasionally).
In that script, there are these lines at the very top (also when pretty-printed):
/*1331654128,176820664*/
if (window.CavalryLogger) {
CavalryLogger.start_js(["\/8f24"]);
}
window.__DEV__ = window.__DEV__ || 0;
if (!window.skipDomainLower && document.domain.toLowerCase().match(/(^|\.)facebook\..*/))
document.domain = window.location.hostname.replace(/^.*(facebook\..*)$/i, '$1');
function bagofholding() {
}
function bagof(a) {
return function() {
return a;
};
}
if (!Date.now)
Date.now = function now() {
return new Date().getTime();
};
if (!Array.isArray)
Array.isArray = function(a) {
return Object.prototype.toString.call(a) == '[object Array]';
};
...
And I'm getting an error which says "SCRIPT5009: 'Date' is undefined", right at the if (!Date.now) portion. Debugging near that point reveals that Date, Array, Object, Function, etc are all undefined.
Er... how? window exists, as does document (though document.body is null) and a handful of others, but plenty of pre-defined objects aren't. Earlier versions of IE don't seem to have this problem, nor do any other browsers, but multiple machines running IE9 (including a clean VM) all have the same issue.
I doubt I can do anything about it, but I'm very curious how this is happening / what the underlying problem is. Does anyone know, or can they point me to something that might help?
-- edit:
Prior to posting this question, I had found this site: http://www.guypo.com/technical/ies-premature-execution-problem/
While it seemed (and still does) like it might be the source of the problem, I can't replicate it under any smaller circumstances. All combinations I've tried still have Date, etc defined ; which isn't too surprising, as otherwise I'm sure others would be seeing many more problems with IE.
If you step through with a javascript debugger at the first point any JS gets run. At the same time add a watch for Date/Array etc. and note when it goes to null. Might be slow and laborious but I can't see why it wouldn't work.
You may want to try adding the script in a document.ready function. In other words, insure that the FB script is processed only after the DOM is ready. But, based on the link you give to Guy's Pod (great article, by the way), it seems you're right in the assertion that IE is downloading and executing the script pre-maturely (hence my suggestion to add a wrapper so that it only executes after the DOM ready event). IE9 is probably sandboxing the executing script (outside the document/window scope).

Failure to override Element's addEventListener in Firefox

I'm trying to override the Element object's addEventListener method, in a cross-browser manner. The purpose is so that I can load some 3rd party scripts asynchronously, and those scripts call this method prematurely.
I created an HTML file that works perfectly in Chrome, but on Firefox I get this exception:
"Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)"
If you comment out the lines in the file that change the INSTANCE methods, it works. But I need to do it on the "class type" (i.e. prototype).
Any suggestions would be appreciated.
Thanks,
Guypo
Here's the file I created
<html><body>
<img id="testImg" src="http://www.blaze.io/wp-content/themes/Blaze/images/header_logoB.png">
<script>
function myLog(msg) { "undefined" != typeof(console) && console.log("Log: " + msg); }
function customListener(type, event, useCapture) {
// Register the event
myLog('Registering event');
this._origListener.apply(this, arguments);
}
// Also tried HTMLImageElement
Element.prototype._origListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = customListener;
var img = document.getElementById("testImg");
// Uncommenting these lines works - but in the real case I can't access these objects
//img._origListener = img.addEventListener;
//img.addEventListener = customListener;
img.addEventListener('load',function() { myLog('load callback'); }, false);
</script>
</body>
</html>
Like Marcel says, this has to do with the way host objects are exposed. The problem is that if you override a predefined property of an interface, it doesn't get changed in the interfaces that inherit from it (at least in Firefox).
Although I agree with Marcel's remarks, there is in fact a way to do this. You should override the property of the lowest possible interface of the object you want to do this for. In your case this would be the HTMLImageElement.
This will do the trick:
(function() {
var _interfaces = [ HTMLDivElement, HTMLImageElement /* ... (add as many as needed) */ ];
for (var i = 0; i < _interfaces.length; i++) {
(function(original) {
_interfaces[i].prototype.addEventListener = function(type, listener, useCapture) {
// DO SOMETHING HERE
return original.apply(this, arguments);
}
})(_interfaces[i].prototype.addEventListener);
}
})();
Please use a valid Doctype, preferably one that cast browsers into (Almost) Standards mode (<!DOCTYPE html> is fine).
typeof is an operator, you don't need the parentheses (but it doesn't hurt)
Most important: don't try to manipulate DOM Element's prototype: those are host objects and you can't rely on host objects exposing any of the core JavaScript functionality of an object. Worse, there may be a performance penalty and you might break other things. Stay away from that technique. For more information, read What’s wrong with extending the DOM.
What is it that you want to achieve, that you want your event listeners to be called automatically when using 3rd party scripts?

Categories

Resources