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.
Related
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.
doing some JavaScript learning and playing around with the syntax. In JavaScript semicolons are optional. It is recommended to use them, but it seems the specification does not impose them in some cases.
In particular it is said that when there is no ambiguity, then it does not make a difference whether you use them or not.
I found this example (an exercise from a blog I googled), but no explanation. I think the semicolon should not make a difference here, but it does. I don't see an ambiguity here, why is it supposed to be different. I tried to play around with some examples, it really seems different. Can't wrap my head around that :-(
var y = x + f
(a+b).toString()
and
var y = x + f;
(a+b).toString();
Would be cool if some guru could shed light on this one.
The f followed by whitespace and a ( can be interpreted as a function call. Placing a semicolon will prevent this.
The ECMAScript Standard Specification mentions the rules of Automatic Semicolon Insertion in section 11.9.1, and the second rule described there is what we need to read:
When, as the Script or Module is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript Script or Module, then a semicolon is automatically inserted at the end of the input stream. emphasis mine
So there we go. In over-simplified layman language, the semi-colon is inserted if continuation of parsing doesn't give us valid code.
In your case, if we continue parsing the code, we do get valid ECMAScript code as follows: (since JavaScript isn't white-space sensitive)
var y = x + f(a+b)...
Thus, a semicolon will not be inserted.
Exceptions to this are described in the third rule (like return and yield keyword usage), but they don't apply in your code.
Whitespace in JavaScript (including line breaks) are not syntaxical. So in the above, with a little bit of rearranging the difference should become clear.
Is this:
var y = x + f(a+b).toString()
The same as this:
var y = x + f;
(a+b).toString();
Nope. In the first example, f being called as though it is a function with the variable a+b being passes as a parameter of that function.
NOTE
There is one case I'm aware of where the above isn't exactly true; return statements.
A linebreak character does act in a syntaxical way for return statements, so this:
return {
tomatoes: 'are yummy'
}
would return an object with the property tomatoes, whereas this:
return
{
tomatoes: 'are yummy'
}
would return undefined
I have been unable to find any reference to this statement in any book, manual, or site. As far as I can tell, it functions exactly as a // comment. For example:
console.log("1");
--> console.log("2");
console.log("3");
will print
1
3
What I'm curious about is exactly what the difference between --> and // is, if any exists, and also why --> seems to completely absent from all the JavaScript references I've seen - the only reason I found out about it at all was because I accidentally typed it into Adobe Dreamweaver, and it was highlighted as a comment. Thanks!
Edit: Here is a functional jsFiddle demonstrating the behavior.
Edit 2: I've tested further, and I've discovered a few things.
This only works if there are exactly two dashes. -> and ---> will throw errors.
This only works with a "greater than" symbol. --< will throw an error.
This will not work in the middle of or at the end of a line. console.log("1"); --> console.log("2"); will throw an error.
My guess is that the JavaScript engine, forgiving as always, is silently ignoring the line because it looks like the end of an HTML comment (<!-- -->). JavaScript inlined in HTML has historically been wrapped in an HTML comment so that browsers that don't support JavaScript would not try to parse it.
Edit:
I've done some research, and this is indeed the case.
From V8's scanner.cc:
If there is an HTML comment end '-->' at the beginning of a
line (with only whitespace in front of it), we treat the rest
of the line as a comment. This is in line with the way
SpiderMonkey handles it.
From Rhino's Comment.java:
JavaScript effectively has five comment types:
// line comments
/* block comments */
/** jsdoc comments */
<!-- html-open line comments
^\s*--> html-close line comments
The first three should be familiar to Java programmers. JsDoc comments
are really just block comments with some conventions about the formatting
within the comment delimiters. Line and block comments are described in the
Ecma-262 specification.
Note that the --> comment is not part of the Ecma-262 specification.
This is not a comment. If you see it as a comment inside of code, then its probably showing as some kind of hanging chad HTML comment instead of the normal "//".
What's happening in this case is you are comparing using the greater than and then subtracting 1. Here are some examples...
var x = 3; var y = 3; console.log(x --> y);
false and x is now 2
If you place the normal comment characters "//", you'll get a syntax error (which you should)
The reason this is working as a comment is because it is returning as undefined. This is not safe to use as a comment. Oddly enough, it works fine in Chrome and you can throw all kinds of characters at it if you start the line with "-->".
--> something for nothing ~!####%$#%^$&%^&*()_+_=-=[]\];'\||?>;';;; alert('test'),,143187132458
However, within IE 11, it is always a syntax error. Very cool find
I recently had to fix a bug that manifested sometimes in Internet Explorer. The bug was that, sometimes, the parser choked on code like
<script type="text/javascript">
<!-- // comments -->
/*...*/
</script>
Which we fixed by correcting the comment.
My question is: is "<!--" valid javascript code, or not? I tried testing it with firebug, and it only says" Undefined". JSFiddle didn't faze. IE only choked on it some of the times (reloading the page in question would show up the result of the script involved).
While knowing that, for historical reasons, an HTML comment inside js could be valid depending on its exact position and the phase of the moon is indeed useful, I was more interested in answers like "var <!-- foo is valid js code, but <!-- on its own is not. Here's why:..."
I did some analysis in firebug:
var x = 2;
var y = 3;
var z = 0;
console.log(x);
console.log(y);
y<!--x;
console.log(x);
console.log(y);
z = y<! --x;
console.log(x);
console.log(y);
console.log(z);
resulting in the following:
2
3
2
3
1
3
false
The difference between the first and second tries is interesting.
I then tried
z = (y <!--x);
console.log(z);
Which failed with
SyntaxError: missing ) in parenthetical
First to answer your question on Is <!-- valid JavaScript: No, it is not, in none of the forms you gave in your question. This is because it is not valid according to the JavaScript BNF grammar which you can find here: http://tomcopeland.blogs.com/EcmaScript.html
If you are interested, here's why you do see it inside script blocks: It is the HTML comment character. You do see it very often within script tags like this:
<script>
<!--
.. JavaScript code...
// -->
</script>
The reason is that old browsers (and with "old" I mean "stone age" like Netscape 1.0) that do not even support JavaScript and would otherwise just show the code on the screen. By doing it this way those older browsers treat the JavaScript as HTML comments and do not show it. Newer browsers ignore this and just run the JavaScript.
This is how it actually works (from http://www.w3.org/TR/html401/interact/scripts.html#h-18.3.2): The JavaScript engine allows the string <!-- to occur at the start of a SCRIPT element, and ignores further characters until the end of the line. JavaScript interprets // as starting a comment extending to the end of the current line. This is needed to hide the string --> from the JavaScript parser.
Because all browsers nowadays support JavaScript (even if it is turned off) you do not need to do this anymore. It is actually bad practice to do so because of these reasons (from http://www.javascripttoolbox.com/bestpractices/#comments):
Within XHTML documents, the source will actually be hidden from all browsers and rendered useless
-- is not allowed within HTML comments, so any decrement operations in script are invalid
An even deeper explanation and all the cons and pro's can be found here: http://lachy.id.au/log/2005/05/script-comments
In older browsers, it was required to have these comments inserted because the browsers couldn't parse the javascript properly. it would try to literally parse the javascript as html which caused script execution failures.
Today, browsers don't need this.
It's not.
If you go to the standard, you'll see that a < can only exist in a RelationalExpression, which <!-- is not as it hasn't got anything on the left-hand side.
Comments in Javascript are like other C-type languages (// for single line, /* */ for blocks.
I forked the excellent zen-coding project, with an idea to implement DOM ascension using a ^ - so you can do:
html>head>title^body>h1 rather than html>(head>title)+body>h1
Initially I implemented with rather shoddy regex methods. I have now implemented using #Jordan's excellent answer. My fork is here
What I still want to know
Are there any scenarios where my function returns the wrong value?
Disclaimer: I have never used zen-coding and this is only my second time hearing about it, so I have no idea what the likely gotchas are. That said, this seems to be a working solution, or at least very close to one.
I am using Zen Coding for textarea v0.7.1 for this. If you are using a different version of the codebase you will need to adapt these instructions accordingly.
A couple of commenters have suggested that this is not a job for regular expressions, and I agree. Fortunately, zen-coding has its own parser implementation, and it's really easy to build on! There are two places where you need to add code to make this work:
Add the ^ character to the special_chars variable in the isAllowedChar function (starts circa line 1694):
function isAllowedChar(ch) {
...
special_chars = '#.>+*:$-_!#[]()|^'; // Added ascension operator "^"
Handle the new operator in the switch statement of the parse function (starts circa line 1541):
parse: function(abbr) {
...
while (i < il) {
ch = abbr.charAt(i);
prev_ch = i ? abbr.charAt(i - 1) : '';
switch (ch) {
...
// YOUR CODE BELOW
case '^': // Ascension operator
if (!text_lvl && !attr_lvl) {
dumpToken();
context = context.parent.parent.addChild();
} else {
token += ch;
}
break;
Here's a line-by-line breakdown of what the new code does:
case '^': // Current character is ascension operator.
if (!text_lvl && !attr_lvl) { // Don't apply in text/attributes.
dumpToken(); // Operator signifies end of current token.
// Shift context up two levels.
context = context.parent.parent.addChild();
} else {
token += ch; // Add char to token in text/attribute.
}
break;
The implementation above works as expected for e.g.:
html>head>title^body
html:5>div#first>div.inner^div#second>div.inner
html:5>div>(div>div>div^div)^div*2
html:5>div>div>div^^div
You will doubtless want to try some more advanced, real-world test cases. Here's my modified source if you want a kick-start; replace your zen_textarea.min.js with this for some quick-and-dirty testing.
Note that this merely ascends the DOM by two levels and does not treat the preceding elements as a group, so e.g. div>div^*3 will not work like (div>div)*3. If this is something you want then look at the logic for the closing parenthesis character, which uses a lookahead to check for multiplication. (Personally, I suggest not doing this, since even for an abbreviated syntax it is horribly unreadable.)
You should look for Perl's Text::Balanced alternative in the language that you're using.