Wednesday, April 30, 2008

Using JavaScript in blogs without <script> support

At one point, Blogger (which is used for this blog) did not allow the <script> tag in blog entries so some people came up with interesting work-a-rounds. This post will showcase two techniques which work well together to provide JavaScript capabilities to your blogs. The first technique supports inline JavaScript with a make-shift <script> tag, while the second technique supports loading external JavaScript files specific to each blog entry which can still be useful even with <script> support.

These techniques work well together and almost need to be used together. The inline technique is only handy for adding a line or two of code because Blogger will try to be smart regarding the formatting of your blog entry and add <br/> tags between all your lines of code making the JavaScript interpreter choke. Additionally, Blogger also tries to format any < or > symbols as &lt; and &gt; which don't work too well in your code. The second technique allows you to load as much JavaScript code as you want, however you'll probably want to use the first technique to invoke the included JavaScript functions.

Testing inline JavaScript: failed. (wait for page to complete loading)

Technique 1 - Allowing Inline JavaScript

That previous test line tested my ability to add inline JavaScript to my blog following these instructions from ecmanaut.

To use JavaScript in this blog without any <br/> tags, I added the following code to my blogger template:

<script type='text/javascript'>
Event.observe(window, 'load', function() {
var c = document.getElementsByTagName('code'), s, i;
var junk = /^\s*\46lt;\133\133CDATA\133|]]\46gt;\s*$/g;
for( i=0; i &lt; c.length; i++ ) {
s = c[i].getAttribute('style') || '';
if( s.match( /display[\s:]+none/i ) ) {
eval( c[i].innerHTML.replace( junk, '' ) );
}
}
});
</script>

I made a couple of slight variations to the original code snippet on the instruction page. First of all, before I added that code, I also included prototype.js in my template to have access to it's handy extensions. I was then able to utilize protype's Event.observe function to execute this code when the page is loaded without messing up and other onload actions. Executing this code during the onload is recommended in the instructions, but not explained how to do it. Finally, since the template requires valid XML, the i < c.length line isn't valid until you change the < to &lt;

Then, in order to run this JavaScript test, I added this line to the bottom of this blog entry:

<code style="display:none"> <[[CDATA[ $('jsTestResult').innerHTML = 'passed'; ]]></code>

which changes the word failed to passed in my test line above.
<[[CDATA[ $('jsTestResult').innerHTML = 'passed'; ]]>

Technique 2 - Loading External JavaScript Files

In order to load JavaScript files, you could edit your Blogger template to include all the files you want as I did for prototype.js and script.aculo.us. However, then every view of your blog will load ALL your JavaScript files. That's fine if you have some JavaScript library that you want to be able to utilize in many/all your blog entries, but if you're adding entries like mine which are just showcasing various JavaScript techniques in separate blog entries, you only want to load the specific blog entry JavaScript if it's being viewed. Enter Dynamically Loading External JavaScript Files.

Right after the previously mentioned code, I added the dhtmlLoadScript function. However, since I have prototype.js loaded, I modified it a little bit. Here's my version:

function dhtmlLoadScript(url) {
var e = new Element("script", {src: url, type: "text/javascript"});
document.getElementsByTagName("head")[0].appendChild(e);
}

So, in any blog entry which I want to use an external JavaScript file I simply add the line:

<code style="display:none"> <[[CDATA[ dhtmlLoadScript('http://some.domain.com/jsfile.js'); ]]></code>

and I can use any of the included function. Furthermore, since the inline script won't be evaluated until the onLoad event it triggered, I don't have to worry about registering it for an onLoad event itself.