Archive for the 'javascript' Category
Superstah!
Matthew claims that I am a superstar, but it’s really less glamorous.
Part of the BarCamp Boston 2 conference was a programming contest.
During the whole first day, people wrote words on a blackboard. At the end of the day, some 11 words were chosen. From these words participants needed to build software which used at least four.
My entry, Pixels to Penguins was one of about seven different entries from different teams of different sizes. All the presentations were very well done, and all quite light-hearted; a great end to the weekend.
Pixels to Penguins took user input, performed a flickr search for related tags, then performed another flicker API call to find images which matched those related tags. The images were mapped onto an ASCII-art rendering of Tux, the Linux penguin; JavaScript made these images appear gradually. To accompany this beautiful scene, all of the data was converted into an integer stream and a 4-track MIDI file was generated. What beautiful music!
The judges named Pixels to Penguins the winning contest entry. The top two teams (three of us in total) won a helicopter ride with Phil Greenspun. I’ve never been in a helicopter!
The (Ruby on Rails) code for Pixels to Penguins is here. I’ve found that it works best under the FireFox browser. My presentation is here.
Update: I’ve had reports that this hack hangs some browsers, so buyer beware. I have been using it successfully on Firefox/MacOSX, but Safari hangs. Firefox/Windows seems to also have problems. If you figure it out, please add a comment to this post!
Update2 I think the browser hangs have been resolved. I was toggling visibility by altering opacity from 0 to 1. A number of browsers were not happy with so much opacity changing so quickly. Now, I’m simply changing the visibility CSS attribute. Please let me know if you still experience troubles with my Pixels to Penguins hack. (Really, it’s nothing more than a cute hack.)
1 commentjavascript script script gotcha script
Today’s javascript lesson is about the wonderfully helpful <script> tag. Unfortunately, it is not all roses when you use <script>. For example, the following will produce a syntax error in most browsers:
<script type="text/javascript">
<!--
document.write("Here is some text.<script type=\"text/javascript\"><!--nalert(\"done!\");n--></script>");
-->
</script>
Usually, the error will say something about an unterminated string. The cause isn’t obvious, especially if you are dealing with a much, much longer string. Here is a smaller example which will cause the same error:
<script type="text/javascript">
<!--
var foo = "<script></script>";
-->
</script>
Maybe you’re like me. Maybe you’re staring at this late at night as the caffeine is wearing off. Maybe you won’t see the problem until it’s made even simpler:
<script type="text/javascript">
<!--
var bar = "</script>";
-->
</script>
At this point, I had an “A-Ha!” moment (I even believed that this javascript was singing “Take On Me”; it was quite late). On a whim, I tried:
<script type="text/javascript">
<!--
var bar = "</" + "script>";
-->
</script>
It worked! It worked! Here’s the rub: Browsers aren’t paying attention to the contents of your <script> tag when they’re searching for the matching end </script>. They’ll grab the first </script> they see, even if it’s embedded in a javascript string. As a result, the script tag is closed prematurely, and terminates your string at the point of the </script>. The syntax error is due to the truncated string.
You can workaround this problem by splitting your strings whenever you see a </script> tag. Something like the following will work (using ERB syntax):
<script type="text/javascript">
<!--
<% content.gsub! /</script>/, '</"' + '"script>' -%>
document.write("<= content %>");
</script>
We are inserting close-quotes for the first part of the string, open-quotes for the second part of the string, and a plus sign to join them. Make sure your replacement quotes match the quoting where the string is embedded in your JS.
No commentsUsing Ruby-generated JSON with Prototype.js
We are using JSON to return snippets of data from the server back to the requesting web browser. This post will explain the various steps needed to get this all working. Note: I am using Rails 1.1.6; some of this has improved in the beta releases for Rails 2.0.
First, we need an action. In my case, I’m doing a bit of data cleaning with a Ruby library, but you could use anything. The action sends a hash back to the client browser as a JSON object.
class WorkController < ApplicationController
def do_work
# Populate the hash however you choose, or use any
# object that supports to_json
result = {
:data => @params[:data],
:message => “the data is #{@params[:data]}”
}
render_json(result.to_json)
end
end
The render_json call doesn’t exist in Rails 1.1.6. In Rails 2.0, there will be a way to directly render for JSON. Until then, add the following method to your application.rb.
class ApplicationController < ActionController::Base
Mime::JSON = Mime::Type.new "application/json", :json, %w( text/x-json )
Mime::LOOKUP["application/json"] = Mime::JSON
Mime::LOOKUP["text/x-json"] = Mime::JSON
def render_json(json, callback = nil, status = nil) #:nodoc:
json = "#{callback}(#{json})" unless callback.blank?
headers["Content-Type"] = Mime::JSON
# Prototype.js will automatically evaluate X-JSON headers.
headers["X-JSON"] = json
#response.content_type = Mime::JSON
render_text(json, status)
end
end
Note that we are including the JSON twice in our rendered document. We include the JSON text in the document body but also in a X-JSON header. The X-JSON header is important, as it allows the Prototype.js JavaScript library to automatically build an object from the JSON text.
All the server-side work is now done. On the client, you will need to request and then process the result. We accomplish this with an asynchronous AJAX call:
/* Pass in some content id that will be updated with the result
obtained from the server */
function setup_work(contentid) {
/* Define a handler to process the result data.
The Ajax.Request call passes the XmlHttpRequest object
and an object created by evaluating the JSON data */
function handler(request,object) {
if (object) { // null if it couldn't be evaluated
document.getElementById(contentid).innerHTML = object.message;
}
}
new Ajax.Request("/work/do_work", {
parameters: "data=rails",
asynchronous:true,
onComplete:handler // Call our handler when the request completes.
});
}
For more help, you will want to reference the Prototype.js developer notes. The Ajax.Request method is well-documented along with its options and handlers.
2 commentsRewriting content with Javascript
I wanted some Javascript which, upon some trigger, would replace the content in a named HTML element. At first, I thought it would be simple:
document.getElementById("js-example").innerHTML = "the new content“;
This works in Safari and Internet Explorer, but encounters a bug in Firefox. In particular, each time the content is replaced via innerHTML, an additional newline (vertical whitespace) is inserted into the document.
My next attempt was to use the W3C DOM model by removing all of the children of our div and then adding the new text:
var theDiv = document.getElementById("js-example");
var divChildren = theDiv.childNodes;
var i = divChildren.length;
while (i-->0) {
theDiv.removeChild(divChildren[i]);
}
var textNode = document.createTextNode("the new content“);
theDiv.appendChild(textNode);
This has two problems:
- Markup isn’t interpreted by createTextNode
- The additional newline still appears!
All that extra code for nothing.
Next, I tried to clone the div with surprisingly better luck:
var theDiv = document.getElementById("js-example");
var newDiv = theDiv.cloneNode(false); /* shallow copy */
theDiv.parentNode.replaceChild(newDiv,theDiv);
var textNode = document.createTextNode("the new content“);
newDiv.appendChild(textNode);
This solution uses the W3C DOM to perform a shallow copy of the div and then add our new content. It’s better than our previous iteration because the newline is finally gone! We still can’t support markup in our replacement fragment, however.
Let’s try a unification of our original innerHTML approach and our DOM mangling:
var theDiv = document.getElementById("js-example");
var newDiv = theDiv.cloneNode(false); /* shallow copy */
theDiv.parentNode.replaceChild(newDiv,theDiv);
newDiv.innerHTML = the new content“;
Hooray! It works! With a shallow clone and the innerHTML technique, we avoid the newline bug in Firefox and Mozilla. Further, we can allow rich markup in our replacement fragments.
Rejoice!
Note: The Firefox problem goes away if the div doesn’t have a display style of ‘table’.
No comments

