Thursday, October 11, 2007

Nested-Nested Quotes

Larry Wakeman of Memory Pharmaceuticals recently sent me a reminder about a follow-up article that I forgot to write. Back in April, I addressed the concept of quotes within quotes within quotes and how to handle this phenomenon.

Any programmer who has ever had to write Greasemonkey scripts, as well as to perform any type of server-side or client-side programming has or will encounter this particular problem. For example, a JavaScript function that generates HTML that contains attributes that refer to JavaScript functions that take strings as parameters, will require some cleverness on the part of the developer, especially if the parameter passed into the JavaScript function is determined at the time the HTML is generated:


<html>
     <head>
     </head>
     <body>
         <script type="text/javascript">

    function alertParameter(stringParameter) {
         alert(stringParameter);
    }

    var topLevel = "test";

    function generateHTML(topLevel) {
         document.write("<a href=\"#\" onclick=\"alertParameter('"+topLevel+"')\">Click this link to see the result</a>");
    }

    generateHTML(topLevel);
        </script>

    </body>
</html>



Here is a breakdown of the above process: The text "document.write" is what I refer to as top level. This is the actual command that outputs the string of text included within the outer quotes. The outer quotes are the quotes that wrap the text inside the parenthesis of the document.write method.


document.write(" /* These quotes are the outer quotes */ ");


Note that there are two types of quotes: Single quotes and double quotes. We can use either to represent the outer quotes. For this example, I chose double quotes. The first set of inner quotes, like the outer quotes, can also be double or single quotes. However, if the inner quote is the same as the outer quote, then the inner quote must be escaped. Consider the following example:

Nested JavaScript Quotes Example 1



document.write("<div onclick=\"alert('ht')\">click me</div>");


The above example is the same as the following example:

Nested JavaScript Quotes Example 2



document.write("<div onclick='alert(\"ht\")'>click me</div>");


Also, to demonstrate that we can use single-quotes as the outer quotes, check out this example:

Nested JavaScript Quotes Example 3



document.write('<div onclick="alert(\'ht\')">click me<div>');


Take a careful look at the above three JavaScript nested quote examples. Understanding this concept is a prerequisite to understanding deeper JavaScript nested quotes.

As I mentioned, the outer quote can be either a double or single quote, and the first inner quote can be either a double or a single quote independent of the choice made for the outer quote. In Example 1, I used double quotes for both the outer and inner quote, whereas in Examples 2 and 3 I alternated between single and double quotes. For convenience, here is Example 1 again:

Nested JavaScript Quotes Example 1



document.write("<div onclick=\"alert('ht')\">click me</div>");


It may seem confusing at first as to how this is possible, until you see the output in the browser source (of the generated HTML, that is. I'll go into more detail on this later):


<div onclick="alert('hi')">click me</div>


The outer quotes don't appear in the generated HTML. Those were top level quotes. They served only to wrap what was being generated. As you can see, the escaped double-quotes render as actual quotes, and cause absolutely no conflict.

Nested JavaScript Quotes Example 4



document.write('<div onclick="alert(\'ht\')">click me</div>');


Example 4 produces the same output! Except in example 4 it is the single quotes that must be escaped, as this time single quotes represent the outer quotes.


JavaScript Inner, inner quotes



By this point, it should be quite apparent that there are several combinations of outer and inner quotes that can be combined without causing a non-terminated string literal error. However, after choosing an outer and an inner quote, choices become much more limited. Instead, from this point forward, nested quotes must alternate between single and double quotes. Below is an example:


<html>
     <head>
     </head>
     <body>
         <script type="text/javascript">

    function alertParameter(stringParameter) {
         alert(stringParameter);
    }

    var topLevel = "test";

    function generateHTML(topLevel) {
         document.write("<a href=\"#\" onclick=\"alertParameter('Say \\'Hello World\\'')\">Click this link to see the result</a>");
    }

    generateHTML(topLevel);
    </script>

    </body>
</html>


You may have noticed that the previous paragraph contains a sentence with strikethrough styling. After trying the example I was going to try, I realized that the rule that the quotes must alternate is not necessarily true. I'll explain, but first, here is the output when clicking the link:

Say 'Hello World'

Definition of literal quote: This is a quote that represents a quote as text in a string. A quote that marks the beginning or end of a string is a non-literal quote, and it is not part of the actual text. A non-literal quote can be represented as a literal quote by escaping it with a backslash (\) character.

Since the outer quote is a double quote, the inner quotes are literal quotes in the string. If the outer quotes were single quotes, then double quotes would be literal. As a result, I can place as may single quotes in a row as I want without affecting the String. However, this will affect not what occurs with the output, but what occurs with the output of the output:

(" ... onclick=\"alertParameter('Say \\'Hello World\\'')\"> ... ")

Outer quote = " (This marks the beginning and end of the string)
Inner quote = \" (Escaped as to not flag "beginning/end of string")
Third-tier quote = ' (Literal quote)
Fourth-tier quote = \\' (Literal quote that will be generated as an escaped outer quote)

This is what really makes this JavaScript nesting quote process complicated. As I mentioned, the single quote is a literal quote. This means the string treats it as a normal character. However, in the HTML -- in the output that is -- the single quote becomes an outer quote. The single quotes around 'Hello World' are literal quotes in the JavaScript code, but in the HTML, these single quotes are inner quotes that must be escaped. However, using a single \' would simply result in a '. So the trick is to escape the slash. Thus, a document.write("\\'"); would produce a \' in the HTML, and this would be resolved to a ' if processed again.

Essentially, you must take two things into consideration when dealing with escaping a quote in JavaScript. If it needs to be escaped in the inner string, then you must think one level deep. It helps to move backwards. Write your desired output first, and then go through and "wrap it" with the tools that would generate it:


<script type="text/javascript">document.write("<a href=\"#\" onclick=\"alert('Say \\'Hello World\\'');\">click here</a>");</script>


I want to dynamically generate this embedded JavaScript from the server with PHP. So I'll first verify that this works in between the body HTML tags of a PHP document. It does indeed work. Next, I'll wrap it with a PHP echo statement:


<? echo "<script type=\"text/javascript\">document.write(\"<a href=\\\"#\\\" onclick=\\\"alert('Say \\\\'Hello World\\\\'');\\\">click here</a> \");</script>'"; ?>


Escaping JavaScript quotes is recursive. After wrapping the PHP echo around the JavaScript with double quotes as outer quotes, I then had to escape the previous outer quotes. I then had to escape the inner-inner double quotes not once, but twice. Once to escape the backslash used to escape the quote, and once to escape the quote so that PHP wouldn't treat it as the end of the string. What I found most interesting is that the single quote used as the outer wrapper of the alert text was ignored during the PHP wrapping process because it is a literal quote. However, the innermost single quote had to have two backslashes added -- once to add another backslash and once to escape that backslash! Each time a wrapper is added, an escaped special character must be escaped again because during each iteration, a backslash escapes a backslash!

In summary, I'm not sure if this little exercise will be of any practical use. Perhaps it would be a good problem solving exercise during an employment interview to see how good a programmer is at dealing with complex layers of abstraction. Perhaps a situation will arise where one actually needs to generate different JavaScript functions based on different criteria. I can't think of any examples other than the Multiple HTML Reply Signatures Embedder for Gmail Greasemonkey script that I wrote, and sometimes I think that there may have been an easier way to generate a dynamic-drop down list in Gmail without having to nest quotes in JavaScript. Bottom line -- I'm not sure. However, if this has proved helpful, please share your story with me!

23 comments:

Anonymous said...

Omg I'm having a quotation nightmare! I'm re-creating a select box using javascript so that I can have onclick/onmouseover events on the options.

The creation process of these options and the subsequent usage of the parameters are impossible, man.

It's killing me!!!

Kudos to you for getting somewhere with it

James Mortensen said...

I was using this method of quotes within quotes to manipulate the DOM, that is until I discovered Taconite.

Taconite lets you use special XML tags to wrap an AJAX response, and these XML tags instruct a parser on where to place the response in the HTML DOM.

It's LGPL code, so you can take the XML parser and use it without the AJAX portion with some hacking.

Although there are tools to help eliminate the need to use strings within strings, I still find that this is indeed theh only solution in some cases.

James

Ramesh said...

Hi,

I am facing a nesting quote problem in the below scenario
...
<td onclick="f_myFunction('someStringContaining\'SingleAnd\"DoubleQuote');" >
...

Any suggestions here..Thanks!

James Mortensen said...

<td onclick="f_myFunction('someStringContaining\'SingleAnd\"DoubleQuote');" >

The top level quote is a single quote. This means that any single quotes inside the string must be escaped with a '\'. The double quotes will be treated as regular characters.

Therefore, for the above example to function properly, you would only escape the inner single quotes, not the double quotes.

James

Anonymous said...

<td onclick="f_myFunction('someStringContaining\'SingleAnd\"DoubleQuote');" >


Therefore, for the above example to function properly, you would only escape the inner single quotes, not the double quotes.

James - thank for that info


But if we are not going to escape the double quotes then it is going end the onClick attribute def!

Anubhav said...

Thank you,your piece was pretty helpful. I am personally using PHP right now so I'd like to add that instead of using the escape sequence in '/' we can also use the dot operator for the purpose.

Gilberto said...

And how to make literal quotes non-literal O_O that's what I'm looking for: this is my code, can you help me?

onmouseout=\"javascript: document.getElementById('inp_".$fila['id']."').onclick='location.href='inbox?setla=true&lang=".$lenguaje."&ver=mensaje&id=".$fila['id'].$leer."''; if (document.getElementById('chec_".$fila['id']."').checked == true){ document.getElementById('chec_".$fila['id']."').value = '1'; } else if (document.getElementById('chec_".$fila['id']."').checked == false){ document.getElementById('chec_".$fila['id']."').value = 'null'; }\">";

Gilberto said...

I managed to get through it, in the onclick string I used function(){ }
Thanks though

Mark said...

Great post!

This was really helpful in a recent update to our web site.

On the site, server side ASP.NET code needs to generate javascript code that contains a document.write statement which passes quoted string parameters to a function to 'flip' an image (the image is an ambigram.

The nesting became quite silly, but it was actually in the simplest form it could be to get the job done!

Anyway, thanks for taking the time to do such a nice write-up.

James Mortensen said...

I'm glad to hear that people are getting use out of this. I had a lot of fun writing this article; thanks for the feedback!

James

Unknown said...

Hi,

I am using some Javascript tracking code to track clients emailing me; however, I am having a slight problem. Here is my code:


<form action="" method="post" onsubmit="MM_callJS('pageTracker._trackEvent(\'Email\', \'Quote\', \'document.getElementById(\&quot;name\&quot;).value\')')">


The problem is with the onsubmit method. It all works fine and tracks properly; however, I am trying to get the users name from the javascript form in the third argument:

\'document.getElementById(\&quot;name\&quot;).value\')'

However, it just returns the actual words document.getElementById("name").value') rather than giving me what I want (the name of the user entered into the "name" text field.

What am I doing wrong?

Thanks,

Ryan

James Mortensen said...

Hi Ryan,

You inspired me to write a very simple test case for solving this problem.

In the code below, I replace Google Analytics pageTracker namespace with my own. I include a function called _trackEvent that does nothing but output the third parameter, which is the one you're having problems with.

I then create a single textbox to contain a "name". I include two divs, one represents the original code you submitted to me. The other represents a modified version. Instead of onsubmit, I use onclick. What matters is what's inside the attribute.

Click either link, and an alertbox will appear. One will show you that the function "document.getElementById" is never evaluated while the other link will clearly show the name from the textbox.

Using the second link, I could swap out my pageTracker namespace with Google Analytics, click on the second link, and know that the test data is being tracked by the analytics.

This is intended to demonstrate how you can write a simple, easy test case in JavaScript to help you verify that the results of one module of your code are correctly passing the values as the proper inputs to another module.

To run the code below, copy to a file on your desktop called "test.html", save, and run in Mozilla Firefox.

<html>
<head>
<style>
.test {
color:blue;
text-decoration:underline;
cursor:pointer;
cursor:hand;
padding:0 0 10px 0;

}

</style>
<script type="text/javascript">

// In the author's code, I'm not sure what this function is supposed to do, but it doesn't matter. All I need to do is see the input values passed to the final function call.
function MM_callJS(strFunct) {
alert(strFunct);
eval(strFunct);

}

// I replace Google Analytics's pageTracker namespace with my own, which includes a function that just outputs what I expect the value I pass in to be. If the data I'm passing in doesn't look like what I expect, I can use this tool to help me debug it.
var pageTracker = {
_trackEvent: function(strEmail,strQuote,strValue) {

alert('You know your code is working if you see the value of the third argument here: ' + strValue);
}
};

/*

When you get your code to the point it is in the second example, you'll then know it is passing in the correct inputs, then you can swap your pageTracker namespace out with Google Analytics.

*/

</script>
</head>
<body>
<div id="test1" class="test" onclick="MM_callJS('pageTracker._trackEvent(\'Email\', \'Quote\', \'document.getElementById(\&quot;name\&quot;).value\')')">Test1: First attempt, you'll see the function name alerted instead of the value.</div>
<div id="test2" class="test" onclick="MM_callJS('pageTracker._trackEvent(\'Email\', \'Quote\', \'' + document.getElementById('name').value + '\')')">Test1: First attempt, you'll see the function name alerted instead of the value.</div>
<br /><br />
<p>Below is the "name" from the form:</p>
<input id="name" value="James"></input>

</body>
</html>

Unknown said...

Great post. I learned some new things. Thanks for sharing.

web designing training in chennai

Zhou Meisen said...

I think this is a charming issue, I expect you would surely post on it again sometime near the future. Thanks guys!
Pay Per Click

Gokulavani said...

Digital Marketing has grown to be the hottest way of marketing nowadays. Many firms are thinking to improve their online marketing budget.
Therefore why Digital Marketers are in demand today and will be in future also. The future of Digital Marketing is extremely bright as revolutionary changes may be in internet technology and smartphones. Lots of People are also becoming Digital day to day.
Digital marketing course in chennai

akshaya said...

I heartily thank you for sharing this instructive blog. It is undeniable that your commitment to excellence has inspired readers. Web Designing Course Training in Chennai | Web Designing Course Training in annanagar | Web Designing Course Training in omr | Web Designing Course Training in porur | Web Designing Course Training in tambaram | Web Designing Course Training in velachery

kenwood said...

References were considered to be an important aspect of evaluating a Salesforce.com consulting company. But a more thorough screening process is required to identify the best Salesforce Salesforce training in Hyderabad

Vietnam Airline said...

Aivivu - đại lý vé máy bay, tham khảo

vé máy bay đi Mỹ

vé máy bay tết

mua vé máy bay đi Pháp giá rẻ

từ Việt Nam bay sang Anh mất bao lâu

web đặt vé máy bay giá rẻ

gia ve may bay di san francisco

vé máy bay đi Los Angeles giá rẻ 2020

combo movenpick phú quốc

combo intercontinental đà nẵng

ve may bay tet said...

Mua vé máy bay tại Aivivu, tham khảo

vé máy bay đi Mỹ hạng thương gia

vé mỹ về việt nam

vé máy bay từ anh về việt nam

mua vé máy bay giá rẻ từ pháp về việt nam

aishu said...

I read your article it is very interesting and every concept is very clear, thank you so much for sharing. AWS Certification Course in Chennai


Marty Sockolov said...

Online slots (Slot Online) is the release of a gambling piece of equipment. Slot computer As pointed out Used create electrical games known as web-based slots, as a result of the development era, folks have turned to gamble through computer systems. Will achieve slot games making online gambling games Via the world wide web network device Which players can play throughout the slot plan or will play Slots through the service provider's website Which online slots games are actually on hand around the form of playing policies. It is similar to playing on a slot piece of equipment. Both realistic images as well as audio are likewise thrilling as they go to living room in the casino in the world.บาคาร่า
ufa
ufabet
แทงบอล
แทงบอล
แทงบอล

Maradona Jons said...

pgslot ซึ่งเกมคาสิโนออนไลน์เกมนี้เป็นเกมที่เรียกว่าเกม สล็อตเอ็กซ์โอ คุณรู้จักเกมส์เอ็กซ์โอหรือไม่ 90% ต้องรู้จักเกมส์เอ็กซ์โออย่างแน่นอนเพราะในตอนนี้เด็กนั้นเราทุกคนมักที่จะเอาก็ได้ขึ้นมา สล็อต เล่นเกมส์เอ็กซ์โอกับเพื่อนเพื่อนแล้วคุณรู้หรือไม่ว่าในปัจจุบันนี้เกมส์เอ็กซ์โอนั้นกลายมาเป็นเกมซะลอสออนไลน์ที่ให้บริการด้วยเว็บคาสิโนออนไลน์คุณสามารถเดิมพันเกมส์เอ็กซ์โอกับเว็บคาสิโนออนไลน์ได้โดยที่จะทำให้คุณนั้นสามารถสร้างกำไรจากการเล่นเกมส์เดิมพันออนไลน์ได้เราแนะนำเกมส์ชนิดนี้ให้คุณได้รู้จักก็เพราะว่าเชื่อว่าทุก

tech said...
This comment has been removed by the author.
Google