I ran into an interesting JavaScript issue yesterday. A user reported a bug with ColdFusionBloggers and searching. If he searched for one word, like 'object', then he could navigate through multiple pages of results just fine. If he search for two words, 'object oriented', then as soon as he clicked next the page would break.
This immediately made me think that I had forgotten a urlEncodedFormat() call somewhere. I'm very good about remembering that but it was certainly possible I could have made a mistake. (Although I'd probably blame Microsoft somehow.) When I opened the file though I could plainly see that I was indeed urlEncodeFormating the string before passing it to my JavaScript code to handle navigation.
I wanted to blame jQuery for this. When you use jQuery to do an Ajax-based content load into a div, they support loading a URL and optionally filtering to a div. What I mean is, you can say: I want you to load the contents of foo.cfm into my div called content, however, I don't want you to load all of foo.cfm, I want you to load the goo div's content only.
Here is an example that does a plain load of foo.cfm, and then another one that loads the goo div from foo.cfm:
$("content").load('foo.cfm')
versus...
$("content").load('foo.cfm goo')
Notice that the syntax uses a space to signify that the goo div is what we want from foo.cfm. My code was passing a URL that looked something like this:
content.cfm?start=11&search=foo%20goo (a search for foo goo)
Notice the escaped space. I thought perhaps jQuery was un-encoding the string somehow and getting confused. However, as I dug more, it seemed more as if that the string was being undecoded as it arrived to the function. I was able to recreate this bug without any jQuery at all:
<cfset s = "frank and beans">
<cfset u = "test3.cfm?x=1&foo=#urlEncodedFormat(s)#">
<script>
function testit(s) { console.log(s); }
</script>
<cfoutput>
<a href="javaScript:testit('#u#')">Test1</a><br />
<a href="" onClick="testit('#u#');return false">Test2</a><br />
</cfoutput>
So get this. Notice how the first link uses a javaScript: style link while the second uses an onClick. When you run this test, the first one will log test3.cfm?x=1&foo=frank. No beans! Edit: I had a brain fart there. The console shows frank and beans, but w/o the %20, the escape. End Edit The second test will correctly show the entire string with the proper escape values. I also tested with an input type=button and it worked fine as well.
So for some reason, when the string was passed using the href property of the anchor, the encoded part of the string is lost somehow. Now I'm still getting used to JavaScript after my long, dark, cold war with it, and I believe that I've read the using the event handler way is 'more proper', but this is the first time I've been bitten by something like this.
Does anyone know of any place where this behavior is documented?
Archived Comments
While I couldn't find anything specific to this, have you tried escape() and unescape()?
If you don't go the event handler approach that should solve your problem.
Hi Ray,
If you replace console.log(s) by alert(s), you'll see the difference.
I agree, it's weird.
I'm not seeing a difference. But I made a mistake in my blog entry (going to correct it after this comment).
The console.log DOES show frank and beans, but WITHOUT the escape. Brain fart on my part. Still wrong for sure.
Anyway, the alert shows the same as the console msg for me.
Guys, I've edited the blog entry a bit. Hopefully it clears up the confusion (although I'm still confused by the behavior in general!)
@JD - For CFB, I just used replace and switched spaces to +. I didn't notice the 'nicer' event handler fix till later.
Hi Ray,
Have you tried to see if setting the encoded var into a js var makes a difference before adding it to the href?
<cfset s = "frank and beans">
<cfset u = "test3.cfm?x=1&foo=#urlEncodedFormat(s)#">
<cfoutput>
<script>
function testit(s) { alert(s); }
var s = '#s#';
var u = '#u#';
</script>
<a href="javaScript:testit(u)">Test1</a><br />
<a href="" onClick="testit(u);return false">Test2</a><br />
</cfoutput>
Tim's method works perfectly, also using encodeURI() produces the proper result.
I haven't found any mention of a problem such as this before, the only thing I can think of is that the string is getting parsed improperly when it's first initialised in the JS engine, although it's strange that putting it into a javascript variable works.
Seems to show the same behaviour in different browsers.
Just a thought,
Its probably the browsers HTML engine since the string is being added to the href property. that would make since because its being evaluated before its passed to the JS engine.
Tim
yeah possibly.. although it seems to do fine in the page source..
Not sure what else we can do to get to the bottom of it really.. it's strange it's not documented anywhere?
I think Tim is probabaly right. Href is a DOM attribute, defined as a string, while click is a DOM event, defined as function reference.
Definitely have seen this and related issues before that seemed to only show up when using href="javascript:foo();", but I've made a habit of using href="javascript:void(null);" onclick="foo();" for so long now that I don't recall whether I ever knew *why* things would error using the first way. I'm guessing Nathan has it right: the events like onclick can even string several functions all together, with spaces between, in, and around, as necessary.
If you load up the page and put this is the browser address box it acts like the href link
javaScript:testit('-%20-')
Its automatically unencoding the url for use.