I have an external javascript file that I want to, upon include, write some HTML to the end of the web page.
Upon doing so though I get the error Missing } in XML expression on the line that uses dropdownhtml.
Here is my code
var dropdownhtml = '<div id="dropdown"></div>';
$(document).ready(function(){
//$(document).append(dropdownhtml);
alert(dropdownhtml);
});
The XHTML webpage that includes this file does so like this:
<script type="text/javascript" src="/web/resources/js/dropdownmenu.js"></script>
Doing either append or alert throws up the same error, what is going wrong?
I got this error because I called an external JavaScript within an existing JavaScript, so ended up with:
<script type="text/javascript">
<script type="text/javascript">
code
</script>
code
</script>
Edit Your update changes the question a bit. :-)
There's nothing wrong with your quoted Javascript or with the script tag that includes it, the problem must lie elsewhere on the page.
The old answer:
If you're including Javascript inside an XML document, you must wrap it up in a CDATA section, or you'll run into trouble like this because the XML parser neither knows nor cares about your Javascript quotes, and instead seems markup (your <div>s in the string).
E.g.:
<foo>
<bar><![CDATA[
var dropdownhtml = '<div id="dropdown"></div>';
$(document).ready(function(){
//$(document).append(dropdownhtml);
alert(dropdownhtml);
});
]]></bar>
</foo>
Naturally you need to ensure that the ]]> sequence never appears in a string (or comment, etc.) your script, but that's quite easy to do (for instance: "Be sure to interrupt the end sequence with a harmless backslash like this: ]]\>; that escape just resolves to > anyway.")
There's definitely a missing ); at the end of your code sample. Don't get where there may be a missing } though.
I have empty script on my page
<script src=""></script>
And this leads to such error
Related
I've just run into a pathological case with HTML parsing. I've always thought that a <script> tag would run until the first closing </script> tag. But it turns out this is not always the case.
This is valid:
<script><!--
alert('<script></script>');
--></script>
And even this is valid:
<script><!--
alert('<script></script>');
</script>
But this is not:
<script><!--
alert('</script>');
--></script>
And neither is this:
<script>
alert('<script></script>');
</script>
This behavior is consistent in Firefox and Chrome. So, as hard as it is to believe, browsers seem to accept an open+close script tag inside an html comment inside a script tag. So the question is how do browser really parse script tags?
This matters because the HTML parsing library I'm using, Nokogiri, assumed the obvious (but incorrect) until-the-first-closing-tag rule and did not handle this edge case. I imagine most other libraries would not handle it either.
After poring over the links given by Tim and Jukka I came to the following answer:
after the opening <script> tag, the parser goes to data1 state
if <!-- is encountered while in data1 state, switch to data2 state
if --> is encountered while in any state, switch to data1 state
if <script[\s/>] is encountered while in data2 state, switch to data3 state
if </script[\s/>] is encountered while in data3 state, switch to data2 state
if </script[\s/>] is encountered while in any other state, stop parsing
All the examples are invalid as per the HTML 4.01 specification: the content of script is declared as CDATA, and the description of CDATA says:
“Although the STYLE and SCRIPT elements use CDATA for their data model, for these elements, CDATA must be handled differently by user agents. Markup and entities must be treated as raw text and passed to the application as is. The first occurrence of the character sequence "</" (end-tag open delimiter) is treated as terminating the end of the element's content. In valid documents, this would be the end tag for the element.”
As you have observed, browsers might not enforce this rule but instead recognize pairs of start and end tags, in some situations. From the spec perspective, this is handling of invalid documents, i.e. error processing. It is not clear what exactly they are doing here and why. It seems to depend on the presence of <!--, which should not have any effect on HTML 4.01 parsing (it is not a comment opener in CDATA content).
In XHTML, partly different rules apply, because in XHTML, <!-- opens a comment within the content of a script element.
As an aside, all the examples are invalid HTML 4.01 and invalid XHTML due to the lack of the type attribute in script. The attribute is not needed (browsers default to treating the content as JavaScript), but it’s required by those specs.
In HTML5, other rules apply. They are rather complicated, and they are supposed to describe browser behavior. In addition to imposing restrictions on content (forbidding e.g. <!-- without matching -->), HTML5 also specifies parsing rules.
Content of tags is still HTML, unless you mark it as not being HTML. In HTML, <word> is taken to be a tag, < needs to be written as < to avoid this behaviour. Alternately, you want to make the contents of <script> a text node; use this formula:
<script type="text/javascript">
//<![CDATA[
// your code, with < and & and "", woohoo!
//]]>
</script>
<![CDATA[ ... ]]> delineates a part of the document as pure text, without markup. Slashes are there so JavaScript wouldn't get confused; the first set of slashes is outside CDATA, but they're HTML-safe, so there's no problem.
EDIT: Just realised the question is about parsing, not writing HTML. Oops.
Hypothetically, if the tags are parsed first and the comments are parsed later, the HTML parser would give you those results.
(I don't mean this is necessarily the case, just a possible explanation only.)
1st case
<script><!--
alert('<script></script>');
--></script>
There is a set of <script></script> inside another <script></script>. The parser may ignore the name of the tags first and just checks for proper opening and closing of those tags. Then it parse the comments.
<script><!--
--></script>
So this is valid.
2nd case
<script><!--
alert('<script></script>');
</script>
There is a set of <script></script> inside another <script></script>. Then it parse the comments.
<script><!--
The comment extends all the way to the end of the document. This is not strictly valid, but the browser handles it correctly.
3rd case
<script><!--
alert('</script>');
--></script>
There is a single closing tag inside the set of <script></script>. It is invalidated before it parse out the </script> as comments.
4th case
<script>
alert('<script></script>');
</script>
There is a set of <script></script> inside another <script></script>, and there are no comments. The first pass is valid but then it really looks into the tags to see what they are. It may not accept a pair of <script> tags inside another one so it invalidates the case.
I would like to print the first link in the page with JavaScript. But when I use the following code, it doesn't work:
<html>
<head><title></title></head>
<body>
<a id="mylink" href="http://google.com">Google</a><br />
<script>
a=$('mylink').href;
document.write(document.links[0]);
</script>
</body>
</html>
Then I commented out the code "a=$('mylink').href", it suddenly worked, why? How come the varable a has any influence on the next statement?
Any answers are appreciated.
There's a few possibilities:
The object $ is not defined and caused a JavaScript error preventing your 2nd statement to execute
The $ object does not know what to do with the string passed in and errors
The returned value from $ does not have a value (ie - it returns undefined) which wont have a property href, causing a JavaScript error
the code is not working because in your example the $ object does not exist and will cause an error. It seems that you were trying to use a JavaScript framework like jQuery ($ object) but you forgot to include it.
Try to add the following script-Tag:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
If you wanted to use jQuery, you should also access attributes via the .attr("attrname") function. E.g.
var a = $('#mylink').attr('href');
Again if you wanted to use jQuery, you have to alter the selector from "mylink" to "#mylink" to specify that you were searching for the element with the id "mylink".
I am a bit confused as to what you are trying to do, but couldn't you just write the whole link in js? Ex:
<script>
<!--
document.write('Google');
-->
</script>
<noscript>
Google
</noscript>
The comment tag in the script is ignored and is only there so browsers that don't support javacript won't print it in the document. The <noscript> is so browsers that don't support javascript have alternate content.
It doesn't work because a=$('mylink').href fails to execute and stop executing of following code. The code document.write(document.links[0]) is correct. When you call it without previous wrong line, it just works.
I think in the first line you're trying to use jQuery library. If you want to do it, you need to include jQuery library using <script> tag, then use the following code:
document.write($('a:first').attr("href"));
Just remove the jQuery stuff, you don't need it. As you've already discovered, there is a document.links collection so if you want to print the herf value of the first link in the document:
document.write(document.links[0].href)
and you're done.
My script function is defined inside a JSF2 form on a XHTML page and the following part of the code causes a problem:
<script type="text/javascript">
function myFunction(){
var checkboxes = document.getElementsByName('mycheckboxes');
if(checkboxes.length > 0){
for (var i=0; i<checkboxes.length; i++){
var checkbox = checkboxes[i];
}
}
}
</script>
When trying to access the page in FireFox 8 it prints the exception:
Element type "checkboxes.length" must be followed by either attribute specifications, ">" or "/>"
what does this error mean?
Another question: is the script is executed before the pages is rendered? Because my checkboxes is loaded in the page during render phase of page (using JSF <ui:repeat>), so my guess is that I must make a condition to execute the code when the variable checkboxes is not null, am I right?
It looks like you are using XHTML and embedding scripts in the page.
XML doesn't have intrinsic CDATA sections, so < means "start of tag" even inside a script element.
If you were using real XHTML then you could represent it as <, but then you couldn't serve the document as text/html.
Instead you can wrap the script content with an explicit CDATA section as described in the specification while also taking care to follow the relevant text/html compatibility guidelines.
You would probably be better off keeping your script in an external file and loading it with <script src="..."></script>.
For that matter, you are likely to be better off using HTML instead of writing XHTML and then jumping through hoops to make it work in browsers that expect HTML.
If you write this script directly in the JSF source - you have to escape symbols like >, <, & etc. with >, & ...
Another way is to use CDATA section:
<script language="javascript">//<![CDATA[
/* here your script */
//]]>
</script>
See also:
http://www.w3schools.com/xml/xml_cdata.asp
What characters do I need to escape in XML documents?
"This error comes from not closing an attribute tag".
http://www.judahfrangipane.com/blog/2006/12/31/element-type-must-be-followed-by-either-attribute-specifications/
Is it possible to get in some way the original HTML source without the changes made by the processed Javascript? For example, if I do:
<div id="test">
<script type="text/javascript">document.write("hello");</script>
</div>
If I do:
alert(document.getElementById('test').innerHTML);
it shows:
<script type="text/javascript">document.write("hello");</script>hello
In simple terms, I would like the alert to show only:
<script type="text/javascript">document.write("hello");</script>
without the final hello (the result of the processed script).
I don't think there's a simple solution to just "grab original source" as it'll have to be something that's supplied by the browser. But, if you are only interested in doing this for a section of the page, then I have a workaround for you.
You can wrap the section of interest inside a "frozen" script:
<script id="frozen" type="text/x-frozen-html">
The type attribute I just made up, but it will force the browser to ignore everything inside it. You then add another script tag (proper javascript this time) immediately after this one - the "thawing" script. This thawing script will get the frozen script by ID, grab the text inside it, and do a document.write to add the actual contents to the page. Whenever you need the original source, it's still captured as text inside the frozen script.
And there you have it. The downside is that I wouldn't use this for the whole page... (SEO, syntax highlighting, performance...) but it's quite acceptable if you have a special requirement on part of a page.
Edit: Here is some sample code. Also, as #FlashXSFX correctly pointed out, any script tags within the frozen script will need to be escaped. So in this simple example, I'll make up a <x-script> tag for this purpose.
<script id="frozen" type="text/x-frozen-html">
<div id="test">
<x-script type="text/javascript">document.write("hello");</x-script>
</div>
</script>
<script type="text/javascript">
// Grab contents of frozen script and replace `x-script` with `script`
function getSource() {
return document.getElementById("frozen")
.innerHTML.replace(/x-script/gi, "script");
}
// Write it to the document so it actually executes
document.write(getSource());
</script>
Now whenever you need the source:
alert(getSource());
See the demo: http://jsbin.com/uyica3/edit
A simple way is to fetch it form the server again. It will be in the cache most probably. Here is my solution using jQuery.get(). It takes the original uri of the page and loads the data with an ajax call:
$.get(document.location.href, function(data,status,jq) {console.log(data);})
This will print the original code without any javascript. It does not do any error handling!
If don't want to use jQuery to fetch the source, consult the answer to this question: How to make an ajax call without jquery?
Could you send an Ajax request to the same page you're currently on and use the result as your original HTML? This is foolproof given the right conditions, since you are literally getting the original HTML document. However, this won't work if the page changes on every request (with dynamic content), or if, for whatever reason, you cannot make a request to that specific page.
Brute force approach
var orig = document.getElementById("test").innerHTML;
alert(orig.replace(/<\/script>[.\n\r]*.*/i,"</script>"));
EDIT:
This could be better
var orig = document.getElementById("test").innerHTML + "<<>>";
alert(orig.replace( /<\/script>[^(<<>>)]+<<>>/i, "<\/script>"));
If you override document.write to add some identifiers at the beginning and end of everything written to the document by the script, you will be able to remove those writes with a regular expression.
Here's what I came up with:
<script type="text/javascript" language="javascript">
var docWrite = document.write;
document.write = myDocWrite;
function myDocWrite(wrt) {
docWrite.apply(document, ['<!--docwrite-->' + wrt + '<!--/docwrite-->']);
}
</script>
Added your example somewhere in the page after the initial script:
<div id="test">
<script type="text/javascript"> document.write("hello");</script>
</div>
Then I used this to alert what was inside:
var regEx = /<!--docwrite-->(.*?)<!--\/docwrite-->/gm;
alert(document.getElementById('test').innerHTML.replace(regEx, ''));
If you want the pristine document, you'll need to fetch it again. There's no way around that. If it weren't for the document.write() (or similar code that would run during the load process) you could load the original document's innerHTML into memory on load/domready, before you modify it.
I can't think of a solution that would work the way you're asking. The only code that Javascript has access to is via the DOM, which only contains the result after the page has been processed.
The closest I can think of to achieve what you want is to use Ajax to download a fresh copy of the raw HTML for your page into a Javascript string, at which point since it's a string you can do whatever you like with it, including displaying it in an alert box.
A tricky way is using <style> tag for template. So that you do not need rename x-script any more.
console.log(document.getElementById('test').innerHTML);
<style id="test" type="text/html+template">
<script type="text/javascript">document.write("hello");</script>
</style>
But I do not like this ugly solution.
I think you want to traverse the DOM nodes:
var childNodes = document.getElementById('test').childNodes, i, output = [];
for (i = 0; i < childNodes.length; i++)
if (childNodes[i].nodeName == "SCRIPT")
output.push(childNodes[i].innerHTML);
return output.join('');
I see so many things like this:
S = "<scr" + "ipt language=\"JavaScript1.2\">\n<!--\n";
Why do they do this, is there an application/browser that messes up if you just use straight "<script>"?
Have a look at this question:
Javascript external script loading strangeness.
Taken from bobince's answer:
To see the problem, look at that top
line in its script element:
<script type="text/javascript">
document.write('<script src="set1.aspx?v=1234"
type="text/javascript"></script>');
</script>
So an HTML parser comes along and sees
the opening <script> tag. Inside
<script>, normal <tag> parsing
is disabled (in SGML terms, the
element has CDATA content). To find
where the script block ends, the HTML
parser looks for the matching
close-tag </script>.
The first one it finds is the one
inside the string literal. An HTML
parser can't know that it's inside a
string literal, because HTML parsers
don't know anything about JavaScript
syntax, they only know about CDATA. So
what you are actually saying is:
<script type="text/javascript">
document.write('<script src="set1.aspx?v=1234"
type="text/javascript">
</script>
That is, an unclosed string literal
and an unfinished function call. These
result in JavaScript errors and the
desired script tag is never written.
A common attempt to solve the problem
is:
document.write('...</scr' + 'ipt>');
This wouldn't explain why it's done in the start tag though.
The more appropriate way to append scripts is to use the DOM.
Create an element of type <script>. See documentation for document.createElement.
Set its attributes (src, type etc.)
Use body.appendChild to add this to the DOM.
This is a much cleaner approach.