Internet Explorer: Ignore unknown ES6 syntax - javascript

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.

Related

Ignoring "syntax errors" in IE11

I have an (ASP.Net) application that has the following client-side Javascript, to facilitate copying to clipboard:
var ua = window.navigator.userAgent;
var is_ie = /MSIE|Trident/.test(ua);
if (is_ie) {
var input = document.getElementById("inputcopy"); // select it
input.value = text;
input.select();
document.execCommand("copy");
this.focus();
}
else {
navigator.clipboard.writeText(text).then(() => {
writeLog('Copy successful');
if (showalert == true) alert('Copied to clipboard');
}).catch(() => {
writeLog('Copy failed');
if (showalert == true) alert('Copy to clipboard failed');
});
}
We need to be compatible with all "modern" browsers - Chrome, Firefox and, don't shoot the messenger, IE11. The first two are fine, but the latter ...
As IE doesn't support the navigator.clipboard I've got the if (is_ie) in there, which works fine. However, IE doesn't know about the Promise in the non-IE section, and complains horribly about "Invalid Syntax" at
navigator.clipboard.writeText(text).then(() => {
even though it'll never actually run it.
How can I tell IE to ignore that bit of code, or work around this issue? I've looked at conditionally loading a separate JS file based on browser, but that doesn't look like much fun. Is there a better option?
Adding my comment as an answer by OP's suggestion.
You can try eval() to get around this issue. What eval() does is parse a string into an executable code. This will literally make IE ignore this code since it's not gonna execute anyway.
Yes, we don't typically use eval(), but rare scenarios like this are perfect reasons to use it.
Here's some resource about eval: Why is using the JavaScript eval function a bad idea?
Or if possible, just use a promise polyfill.
As #Quentin explained, merely using the function expression should easily fix this without using eval().
You can't make a JavaScript parser skip over syntax in parts of a program it isn't going to run (to oversimplify the situation horribly: It still needs to parse that bit of the program to find the } that ends the else block.
Either write your JS to use syntax that is supported by IE (i.e. use a function expression instead of an arrow function) or transpile your JS to a version that IE will support (with a tool like Babel).

Document.createElement arity

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.

Trying to access this JSON attribute, but my method is not working (screenshot of JSON object provided)

For the sake of simplicity, and to make our lives easier, I just took a screen shot of the JSON object, here: http://i.imgur.com/zT4wUSc.png
Right now, I am trying to access 1's __text value. I use this code, which I am able to access other attribute:value pairs, like the title under 0.
entireJSON.TEI.text.body.div.forEach(function(entry) {
entry.p.forEach(function(dp) {
var dateCurr = dp.title; // this works fine
}
}
But where it goes wrong is when I try the following code (I've tried other variations too). I've tried many different kinds of syntax, and I just can't get it. How do I access the attribute:value (__text:text's value) under 1?
entireJSON.TEI.text.body.div.forEach(function(entry) {
entry.p.forEach(function(dp) {
var dateCurr = dp.title; // this works fine
dp.persName.forEach(function(test) { // DOESN'T WORK
console.log(test.__text); //DOESNT'T WORK
});
});
});
I would try using a conditional since the first index p[0] doesn't have a property for persName in the screenshot and would throw an error like Cannot reach property forEach of undefined
entireJSON.TEI.text.body.div.forEach(function(entry) {
entry.p.forEach(function(dp) {
var dateCurr = dp.title; // this works fine
// if it's an array
if (Object.prototype.toString.call(dp.persName) === '[object Array]') {
dp.persName.forEach(function(test) { // DOESN'T WORK
console.log(test.__text); //DOESNT'T WORK
});
} else {
// everything else
console.log(dp.persName);
}
});
});
You can still use Array.isArray if you just want to support modern browsers.

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.

Is it possible to test for IE in javascript without browser sniffing and conditional comments?

I'm curious to know if it's possible to switch between Javascript insertRule and addRule methods in Javascript without using browser sniffing/conditional comments to assign addRule to IE.
In CSS, I can use properties that aren't supported on specific devices (IE ::pseudo-selector for example) to write rules for specific browsers. Is there a similar trick in Javascript?
Thanks for some insights!
if(sheet.addRule) {
sheet.addRule(...);
}
else
{
sheet.insertRule(...);
}
Use feature detection instead:
var abstractedInsertOfRule = function (rule, stylesheet){
//if (object) is a quick way to test if the object is defined
//in browsers that do not implement "insertRule",
//if (insertRule) returns "falsy"
if (insertRule) {
insertRule(rule, stylesheet);
} else if (addRule) {
addRule(rule, stylesheet);
} else {
//call other rule insertion methods here
}
};

Categories

Resources