IE does not parse any JS after unsupported code? - javascript

I want to use certain JS code, but only according to whether the browser supports the #supports (CSS) and CSS.supports() (JS) properties/functions.
Now, Internet Explorer does not support CSS.supports() or #supports. This is fine - the idea is to build the JS code so that, if the browser doesn't support either of them, then it falls back to a working version.
This works flawlessly on all other browsers, even those which do not support #supports / CSS.supports().
But in IE, it parses the JS code all the way up to the first mention of CSS.supports(), and then it breaks entirely and does not parse a single character after that.
I'm working with IE 11, and here's the JS code I'm using (I'm also using jQuery):
$(function() {
var port = $(window);
var container = $('html, body');
if( !CSS.supports("-webkit-overflow-scrolling", "touch") & CSS.supports("perspective", "1px")) {
var port = $('.prllx');
var container = $('.prllx');
};
// OTHER CODE GOES HERE
});
However, as I said, IE does not read ANY code after the IF check.
I then tried to make a test code, to see what happens with a more simple example:
var helloworld = !CSS.supports("-webkit-overflow-scrolling", "touch");
$("#test").html(helloworld);
if (helloworld == true) {
$("#test2").html("HELLO WORLD FROM WITHIN IF");
};
But IE doesn't parse ANYTHING after the first line (I've been inserting $("#div").html("HELLO WORLD"); lines every other line to see at which point IE stops parsing JS, and it literally happens right after the first mention of CSS.supports.
I can't seem to get my head around this: In all other browsers which do not support CSS.supports(), the IF (in my first example) obviously fires false, and the variables remain unchanged. But they continue to parse the remaining code after that instance, even if they don't/can't parse CSS.supports() because they don't support the function. But IE stops parsing entirely.
I have to say that I am not particularly fluent with javascript, so I might be missing something really simple here - or it might actually be a bug, in which case, how do I go around it? How can I check whether the browser supports CSS.supports() without breaking the entire code in case it is IE?
Thanks a lot in advance!

Any browser that doesn't have a CSS object with a supports property that refers to a function will throw an exception where indicated below:
$(function() {
var port = $(window);
var container = $('html, body');
// vv Exception on this line
if( !CSS.supports("-webkit-overflow-scrolling", "touch") & CSS.supports("perspective", "1px")) {
var port = $('.prllx');
var container = $('.prllx');
};
// OTHER CODE GOES HERE
});
When an unhandled exception is thrown in a function, control is tranferred out of the function, and no code following the place where the exception occurred will run. This is not an IE thing, it's how JavaScript and most other languages with exceptions work.
Instead, put in a guard checking whether CSS exists and if so whether it has a supports that's a function:
$(function() {
var port = $(window);
var container = $('html, body');
if( typeof CSS === "object" &&
typeof CSS.supports === "function" &&
!CSS.supports("-webkit-overflow-scrolling", "touch") &&
CSS.supports("perspective", "1px")) {
var port = $('.prllx');
var container = $('.prllx');
}
// OTHER CODE GOES HERE
});
Some host-provided functions report typeof incorrectly. In that case, you may need to change the === "function" above to !== "undefined". That's a weaker guard, because not everything that's not undefined is callable, but sometimes host-provided functions don't report "function" as they should.
Side note: The logical "and" operator in JavaScript is && (as above), not &. The & is the bitwise "and" operator (coerces its operands to 32-bit integer numbers and combines them at the bit level). It matters primarily because && short-circuits, which means that if its left-hand operand is falsy (for instance, typeof CSS === "object" is not true), it doesn't evaluate its right-hand operand at all, so we can safely do something assuming the left-hand operand is truthy (such as looking at CSS's support property).
Side note 2: You don't put a ; after the end of the block attached to a control-flow statement. It's harmless, but pointless.

Related

Javascript - if(...) evaluating differently in Firefox and Safari

So I have come across a very strange issue in my code whereby equality is being deemed false in safari (despite the variables genuinely being equal) whilst in Firefox the same equality evaluation is being assessed as true. After a few hours using debugging tools, alas I arrive here...
I have this simple function:
function determineChosenLevel(){
var x = document.getElementById("level").children[1].innerText;
return x;
}
Then I use it in this place:
var level = determineChosenLevel();
console.log(level);
if(level === "GCSE"){
// do some things
}else if(level === "A Level"){
// do some things
}
Now I run this code on Firefox with determineChosenLevel returning GCSE on Firefox and the if statement if(level === "GCSE") evaluates to true and corresponding code evaluates successfully.
However.... on Safari, this is not the case. I add a breakpoint on Safari's tools on the first line of code in the second snippet and the console.log(level) indeed logs GCSE, but the computer decides to evaluate the if statement to false and skip over the //do some things.
The image attached shows the level in the browser debugging tools of Safari. The only glimmer of hope is that GCSE appears to have some strange arrow next to it which may be why equality is not holding, but I am not really sure.
Any help would be really appreciated.
The element have a line break, like this:
<div id="el">text
</div>
Which is always invisible in the output, but in Safari the innerText property have the line break on it, and on chrome haven't. To get rid of it, just use trim() or replace all instances with .replace(/[\r\n]/g, "")
var text = document.getElementById("el").innerText;
console.log(text);
console.log(text.trim());
<div id="el">text
</div>

Cannot bind Javascript Symbol to HTMLElement.style

Why can I bind a JavaScript Symbol to everything except the style property of an HTMLElement?
https://jsfiddle.net/elgs/ftj9zx42/3/
Here's the HTML:
<div id='a'></div>
Here's the Javascript:
const a = document.querySelector('#a');
a.classList._someSymbol = Symbol('some_symbol');
console.log(1);
a.style._someSymbol = Symbol('some_symbol');
console.log(2);
And here's the output:
1
(index):35 Uncaught TypeError: Cannot convert a Symbol value to a string
at window.onload ((index):35)
Update:
The whole story is I'm writing a proxy that will attach a Symbol to every property as identifier in the get trap, this unintentionally included the style. I will eventually include only the properties that I'm interested in, that won't include the style. But now I got this error and I'm curious why this happened.
It's browser dependent. As HTML is a standard (it is a series of guidelines on how a document should be interpreted by a user agent).
So, your above example does work in FireFox, but not in Google Chrome.
If you want to understand why, I recommend looking into the source code of your browser, which appears to be converting your symbol toString
🎈 Try this one:
const a = document.querySelector('#a');
a.classList._someSymbol = {};
a.classList._someSymbol = Symbol('some_symbol');
console.log(1);
a.style._someSymbol = {};
a.style._someSymbol = Symbol('some_symbol');
console.log(2);
I don't know why it works in this way πŸ€ͺ.

Unexpected token: operator (>) when trying to minify the javascript?

I'm trying to minify my javascript code using an online tool but everytime I try to do that I get this error:
// Error : Unexpected token: operator (>)
// Line : 1
// Col : 41
and this is on line 1:
var result = parsedObject.filter( audio => audio.filename === ''+audioFile+'' );
Could someone please advice on this issue and how to resolve it?
Thanks in advance.
Apparently, your minifier doesn't understand arrow functions, or it needs some option to be set to know you're doing ES2015+ ("ES6+") stuff. Your options are:
If it has an option, turn the option on; or
(you've now told us that you tried both https://jscompress.com/ and https://javascript-minifier.com/. jscompress.com has an "ECMAScript 2018 (via Babili)" tickbox in the upper right-hand corner that, when ticked, minifies your example code. I didn't find an option on javascript-minifier.com.)
If it doesn't, switch to a mninifier that does understand them; or
Don't use arrow functions. In this particular case that would look like:
var result = parsedObject.filter(function(audio) {
return audio.filename === ''+audioFile+'';
});
Use arrow function, but turn them into non-arrows before minifying by using a transpiler like Babel.
If you need to support any version of IE, you need to not send arrow functions to the browser (by using option 3 or 4 above). If you don't have to support IE, just modern browsers like Edge, Chrome, Firefox, and Safari, sending arrow functions to the browser is just fine.
Side note: You don't need those '' on either side of audioFile. If it's already a string, just remove them (=== audioFile). If it isn't already a string, just do one or the other, or use String(audioFile) to convert it, and do it once before the filter loop:
var audioFileString = String(audioFile); // or `'' + audioFile` or `audioFile + ''`
var result = parsedObject.filter(function(audio) {
return audio.filename === audioFileString;
});
The tool you are using does not support arrow functions (which are a relatively new feature).
You can:
Find a minifying tool which supports modern JS
Not use arrow functions in the first place
Use a tool to transform your JS to ES5 before minifying it

Why is the HTML comment open block valid JavaScript?

In some old code, I found a JavaScript file with it's contents surrounded by HTML comments.
I understand the reasons for doing that in old browsers, but not how it is valid JavaScript in any way.
The expression <!-- is undefined in Chrome and IE's console.
Is this a special case handled by the interpreter (http://javascript.about.com/library/blhtmcmt.htm) still defined in the ECMAScript standards and working in modern browsers, or does the combination of these symbols happen to result in something that's undefined?
I read this as something like "less-than NOT decrement", which seems nonsensical with no operands. Any of these by themselves return a syntax error.
I get why things like "use strict"; are valid, but do nothing, but I can't tell what this code actually does.
I'm probably overthinking it, but would like to understand what's going on
This is a non-standard feature that browsers and JavaScript engines have always implemented. Nowadays, it cannot be removed from the Web platform, as that would break backwards compatibility. It’s detailed in the JavaScript / Web ECMAScript spec:
<!-- must be treated as the start of a SingleLineComment β€”
equivalent to //.
var x = true;
<!-- x = false; // note: no syntax error
x; // true
--> at the start of a line, optionally preceded by whitespace or
MultiLineComments, must be treated as a SingleLineComment β€”
equivalent to //.
var x = true;
--> x = false; // note: no syntax error
x; // true
var x = 1;
/*
multiline comment!
x = 2;
*/ --> x = 3;
x; // 1
<!-- begins a single-line Javascript comment, believe it or not.
This is used to allow <script> blocks to hide themselves from browsers that don't recognize the <script> tag (eg, Netscape 2), by wrapping all of the script contents in an HTML comment.
This should no longer be used.

Get argument expression before evaluation

I'm trying to create an assert method in Javascript. I've been struggling with arguments.callee.caller and friends for a while, but I can't find a way to reliably get the full text of the calling function and find which match in that text called the current function.
I want to be able to use my function like this:
var four = 5;
function calculate4() { return 6; }
assert(4 == 2 + 3);
assert(4 == four);
assert(4 == calculate4());
assert(4 != 3 && 2 < 1)
and get output like this:
Assertion 4 == 2 + 3 failed.
Assertion 4 == four failed.
Assertion 4 == calculate4() failed.
Assertion 4 != 3 && 2
Right now, I can't get much beyond Assertion false failed. which isn't very useful...
I'd like to avoid passing in extra parameters (such as this) because I want to keep the assert code as clean as possible and because it will be typed many, many times. I don't really mind making it a string, but I'm concerned about issues of scoping when trying to eval() that string. If I have no other options, or if my concerns are ill-founded, please say so.
I'm running this in an .hta application on Windows, so it's really jscript and I have full access to the filesystem, ActiveX etc. so system specific solutions are fine (as long as they don't require Firebug etc.). However, I'd prefer a general solution.
There's no reliable way you can do this passing only a single argument. Even with eval, the variables used would be out of scope. Parsing arguments.caller would work if arguments.caller made only one call to assert, by searching for it and parsing the argument expression. Unfortunately, none of the proprietary tools available to you will help.
I ended up using the following function, which allows me to optionally duplicate the text of the assertion as a second argument. It seemed simplest.
function assert(expression, message)
{
if (!expression) {
if (message + "" != "undefined" && message + "" != "") {
document.write("<h2>Assertion <pre>" +
message +
"</pre> failed.</h2><br>");
} else {
document.write("<h2>Assertion failed.</h2><br>");
}
}
}
Maybe that helps someone. There are probably better methods available, but this worked for me.
Note that I've only been programming in Javascript for three days, so there's probably a number of improvements that could be made.
It is actually possible, at least in browsers and Node.js. I don't know about .hta applications.
Modern browsers, Node.js and hopefully your environment put a stack property on error objects, containing a stack trace. You can construct a new error, and then parse out the file path to the file containing the assert() call, as well as the line number and column number (if available) of the call. Then read the source file, and cut out the assert expression at the given position.
Construct an error
Parse error.stack, to get filepath, lineNumber and columnNumber
Read the file at filepath
Cut out the bits you want near lineNumber and columnNumber in file
I've written such an assert function, called yaba, that might get you going.

Categories

Resources