Archive for the 'JavaScript' Category

YUI good for your performance

Sunday, April 1st, 2007

One of the cons of using any of the good and popular third-party JS libraries is the file size of the .js files associated with them. A lot has been done by the library developers to address this issue - providing minified versions, segregating the script files based on what they do and using loading-on-demand, among others. There's more good news - Yahoo is now offering hosting of their YUI library files.

How is it good for you?
- less on the bandwidth bill - you don't need to worry about hosting these files yourself
- high availability - well Yahoo is behind this, so you can rest assured that these files are delivered to your surfer
- small downloads - Yahoo hosts the minified versions and gzips them
- files already cached!

The last one is the topic I had in mind with the this blog's title. Since Yahoo will be using the same locations for the libraries you need and since Yahoo is the most popular site, chances are your visitors have already checked their Y! mail or their Y! finance page and searched or done anything on the Yahoo network of sites. This means they have already requested and (hopefully) cached these .js files. And as proved before, lowering the number of HTTP requests is top 1 performance optimization you can ever do.

Even better is that you can include only libraries you need (less transfer) or you can include several in a batch (less requests).

If you only need Event and DOM, include only yahoo-dom-event.js (8K)
If you need only Event, DOM, AJAX, include yahoo-dom-event.js and connection-min.js (8K + 4K)
If you also need more like Drag and Drop and animation and AJAX, why not include all utility classes at once, utilities.js. After all this file is 22K, probably less than two images that will appear on your page.

Note: all filesizes above are when files are gzipped, which is how Yahoo serves them.

So bottom line, your visitors hit your site and, lo and behold, they have all the JS already cached, resulting in your page loading as fast as a rocket ;) Sweet.

 

Two bookmarklets for debugging in IE

Tuesday, February 20th, 2007

Here are two bookmarklets that could make your life easier when trying to figure out why in IE a page behave as wrong as it behaves. For Firefox we have Firebug, so none of this is necessary. For IE we have also Firebug lite (see my post), but you need some setup before you can use it. With this thing here you can mess up any page you see on the web, not only yours :)

Bookmarklet 1 - Eval() textarea

I saw this bookmarklet here and it's beautiful. When you start it, it puts a textarea at the bottom of your page and you can type javascript in it, then eval()-uate it. Perfect! Only ... it doesn't work in frames. So I did the same thing but when you have frames (works without frames as well). The way mine works is - you first select some text in a frame, then you click the bookmarklet. A new textarea, ready to execute javascript will be placed in this frame (or iframe) that you selected. Also in this case when you type document.something, it refers to the document in the frame, not the frameset.
If you don't select any text and click the bookmarklet, it will place the textarea in the topmost document, so it will work for frame-free pages as well.

So here's the bookmarklet.

textarea eval

And here's a page where you can test.

Bookmarklet 2 - dump anything

After having my beautiful textarea, I wanted to be able to dump variables, like print_r() or var_dump() but for Javascript. I googled and I found this little script. All I did then is to make it a bookmarklet. How it works? You select the bookmarklet, it gives you a prompt, where you type whatever you want to dump, like document.location for example. Then it shows you an alert with all properties of this thing you typed. (Don't try to dump document though, or something else that recurses, because the script won't handle the recursion and will freeze).

Install it from here:

dump var

While this second bookmarklet will most likely work in FF as well, you don't need it, you have firebug!

 

JSON data island

Monday, February 12th, 2007

Here's a hacky thing, I called it JSON data island, referencing the XML data island, that MS came up with for their IE browser. The idea is to use a comment in your HTML that holds some data in JSON format. Then using DOM, you access the comment, eval()-uate it and there you go - you have the data as a Javascript object.

» Here's a little proof of concept.

What we have in the HTML is:

<div id="some-div"><!--
    {prop:'value', prop2:['value1','value2','value3']}
-->
    Some stuff in div
</div>

Then the JavaScript that will process the JSON data in the comment:

var island = document.getElementById('some-div').firstChild;
var the_data = eval('(' + island.data + ')');
alert(the_data.prop2);

In this case I added the comment island as the firstChild of some div, but it could be anywhere, as long as you know how to access it with DOM methods.

Q&A

OK, why?
Well, I need some data that comes from the database, so it needs some server-side processing before it appears in the JavaScript code.

Can't it simply be in an inline <script>?
Inline scripts are bad, they mix behaviour (JS) with content (HTML). Also, imagine how your client runs some SEO tool that reports inline scripts in the content, the client goes: "You used an inline script!? Gimme my money back this instant!"

In your external .js then?
I'd like to have external .js simple, with no server-side scripting involved. I don't want to make my beautiful, big, long script appear as something like script.js.php

One small additional little .js.php only for the data you need?
Look, I said "no"! .js.php will require a new database connection, since it's a seperate request. Performance will suffer. In addition it's a new HTTP request and those are expensive.

An AJAX request fired from your beloved clean external .js that will only return the JSON data you hacked into your island thing of yours?
Same as above. New HTTP request, new DB connection.

OK then, I give up!
It was about time!

Thanks: to Phil Renaud for the inspirational idea of making conversational posts, his are hysterical.

 

Check/toggle ‘em all

Saturday, February 10th, 2007

Recently I decided to clean up all the spam from an abandoned phpBB forum of mine, there was a lot to delete. In the phpBB version that I use there is no option to "check all" topics you want to moderate. So I came up with a little bookmarklet to do this for me. Here it is, only improved to work within frames and toggle (check if not checked, uncheck otherwise) all checkboxes in sight. Tested in FF and IE6/7.

To install, add this to your bookmarks/favourites:

Toggle 'em all

Here are
some
checkboxes
for
your
testing
pleasure.

The reading friendly code:

javascript:(function(){
function checkFrames(w) {

  try {
    var inputs = w.document.getElementsByTagName('input');
    for (var i = 0; i < inputs.length; i++) {
      if (inputs[i].type && inputs[i].type == 'checkbox'){
        inputs[i].checked = !inputs[i].checked;
      }
    }
  } catch (e){}
  if(w.frames && w.frames.length>0){

    for(var i = 0; i < w.frames.length;i++){
      var fr = w.frames[i];
      checkFrames(fr);
    }
  }
}
checkFrames(window);
})()
 

AJAX-MCV in Russian

Monday, January 29th, 2007

Boris of http://www.ajaxplanet.ru/ has published a translation of my article on the little AJAX/MVC framework I came up with, this is trully flattering, thanks a lot!

If you speak Russian check the post here.

The translation is by Gennady Potapov, sposibo Gennady!

 

Dynamic SCRIPT and STYLE elements in IE

Friday, January 26th, 2007

So you know how to add external scripts and styles, using the DOM, after the page is loaded. And what if you don't have external files, but have some style definitions and some JS code as text and you want it inserted and executed into a page.

The DOM way

"Ha! An easy one", you'd say and then go like:

var ss = document.createElement('script');
var scr = 'alert("bah");';
var tt = document.createTextNode(scr);
ss.appendChild(tt);
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

"Ha!" in turn says IE, "No way!"

The IE way for SCRIPT

The above won't work in IE, but you can use the text property instead of creating a text node. Interestingly enough, this also works in Firefox.

var ss = document.createElement('script');
var scr = 'alert("bah");';
ss.text = scr;
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

The IE way for STYLE

STYLE, SCRIPT, what's the difference, they are merely elements of the DOM tree. For the normal browsers, yes, so creating a text node with the stylesheet body will work in Firefox. For IE, you need a workaround.

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
ss1.styleSheet.cssText = def;
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);

Note that while in the SCRIPT case I took the liberty of skipping the type attribute, it's absolutely required here.

So with a bit of object sniffing, we can get a cross-browser solution:

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);
if (ss1.styleSheet) {   // IE
    ss1.styleSheet.cssText = def;
} else {                // the world
    var tt1 = document.createTextNode(def);
    ss1.appendChild(tt1);
}

Update: note that it's important for IE that you append the style to the head *before* setting its content. Otherwise IE678 will *crash* is the css string contains an @import. Go figure!

 

User stylesheet in IE

Saturday, January 20th, 2007

Let's say you want to quickly try out some small stylesheet changes, but you don't want to (or prefer not to, or for some reason temporarily you just can't) modify your application's CSS file(s). In FF it's easy - you have Firebug and you can play with styles until blue in the face. And in case you do get blue in the face and start making so many changes that you get lost, you can create a new clean and tidy CSS file, place it on your hard drive and use Web Developer extension to load it (Menu CSS->Add User Style Sheet). With WebDev Extension you can also Edit CSS right there, although unfortunatelly it's not always working when you have frames.

OK, there are options for Firefox. But how about IE?

In IE you have IE Developer Toolbar, definitelly helpful, but you can only modify element styles, not the stylesheet rules. So? A tiny little bookmarklet to the rescue!

My bookmarklet assumes you have a file called C:\user.css and loads this stylesheet on demand in your page, in every frame, just in case you use frames. Simple, yet useful, I hope. Here's the (readable) code:

javascript:
var css_file = prompt('Which CSS file you want to load today?','c:/user.css');
function addcss(w) {
    var html_doc = w.document.getElementsByTagName('head')[0];
    var css = w.document.createElement('link');
    css.setAttribute('rel', 'stylesheet');
    css.setAttribute('type', 'text/css');
    css.setAttribute('href', css_file);
    html_doc.appendChild(css);
}
var errors = 0;
function checkFrames(w) {
  if(w.frames && w.frames.length>0){
    for(var i=0;i<w.frames.length;i++){
      var fr=w.frames[i];
      try {
        addcss(fr);
      } catch (e) {
        errors++;
      }
      checkFrames(fr);
    }
  }
}
checkFrames(window);
addcss(window);
if (errors > 0) {
    alert('Could not access ' + errors + ' frame(s)');
}

To install and play around

Right-click this link and add it to your favourites:

Add User StyleSheet

Have in mind that this is IE-only (tested IE7). I don't think FF will allow you to access files on your local drive like this. But for FF you have the tools to do this anyway.

Another option to load local stylesheets in IE is to use the user CSS capability built in the browser, you can find it under Tools/Internet Options/Accessibility, but this will load your user CSS first (as opposed to last as the case is with my bookmarklet), so the "real" style definitions will overwrite yours, unless you always use !important and the "real" styles don't.

Thanks!

Have fun with the custom CSS and let me know how you find it.

 

Firebug console for IE

Wednesday, December 6th, 2006

Update: A better version of what I was trying to do is here. It works around the cross-domain permission problems in IE by not loading a page in the frame, but putting there the actual content.

Firebug - no words to describe how cool it is, really. After the recent new release (1.0. beta) the number of features is overwhelming. I for one can't live anymore without it, seriously.

One of the things I noticed on the website is the ability to use the Firebug console in other browsers than Firefox. I don't know if this existed before version 1.0 but if it did, it was the best kept secret. I am so addicted to the console in Firefox, I use it all the time to tweak a few things here and there when I'm working on a page. Recently I was looking for something similar for IE, but couldn't find it. Lo and behold, it was right under my nose.

So, here's the page that describes how to use Firebug in IE (and others). Basically you unzip the Firebug Lite files somewhere on your server and then you include firebug.js in your pages. But why stop there? And isn't it possible to avoid including this script on every page (and forgetting to remove once you're done, or removing it prematurelly since a page, just like a painting, is never really finished). Bookmarklets to the rescue!

I wanted to host the Firebug files on my hard-drive and then use a javascript dynamic include to load firebug.js via a bookmarklet. This way I would be able to load the firebug console every time I want it, on any page. Unfortunatelly IE's security policy won't allow it. Then?

Solution

The solution I came up with is:

  1. you copy the Firebug Lite files somewhere on your server
  2. you call a bookmarklet that will load firebug.js
  3. you hit F12 and you have a console!

This procedure has to be repeated for every domain you're working on, because of the security policy that won't allow cross-domain frame scripting. You can have one copy for your http://localhost and one for every domain. To ease the creation of bookmarklets that load firebug.js, I came up with a Firebug bookmarklet generator.

In action

  1. I copied Firebug Lite files (get the .zip) on this server (phpied.com), they are here.
  2. I (and you can try the same) generate a bookmarklet, using the bookmarklet tool
  3. Add the generated bookmarklet to the favorites
  4. Go to any page on phpied.com
  5. Click the new favorite
  6. Hit F12 to show/hide the console

Here's how (a readable version of) the generated code looks like:

javascript:(function(){
  var firebug_js = document.createElement('script');
  firebug_js.setAttribute('type', 'text/javascript');
  firebug_js.src = 'http%3A//www.phpied.com/files/firebug/firebug.js';
  document.getElementsByTagName('head')[0].appendChild(firebug_js);
  firebug_js.onreadystatechange = function () {
    if (firebug_js.readyState == 'complete') {
      console.open()
    }
  }
})()

Minor improvement to the console

The Firebug Lite console executes the code you type, but doesn't show it again when you use the up/down arrows, the way it does in Firefox. So I added this feature (copying from myself), you can replace the firebug.js you download with my version.

Not sold yet?

Here's a screenshot of the console in action, I used it to change my photo on the homepage.

firebug-ie-console.png

Go ahead, please

I strongly encourage everyone to try this out. Firebug is a beautiful thing and using even a bit of it in IE is great.

 

Y! homepage – CSS sprites in action

Friday, December 1st, 2006

Have you looked at the HTML markup of the new Yahoo homepage? Then you should. The markup (although it won't validate) is a piece of semantic art. Lists are lists, tabs are lists, only one table to be seen (obviously plugged-in coming from a different site)

The total number of markup elements on the page (document.getElementsByTagName(*).length) is 662, which is not bad for such a busy page. Compare that with Google search results page, which is semantically pretty much nothing but a just a list and uses 468 elements to display the content, among which there are 22 tables. Amazon's home page has the stunning 1469 elements.

Anyway, the thing that I saw and liked, was the use of the so-called CSS sprites (tut, tut, demo). It's a technique where instead of creating multiple images (10 little icons for example), you create one image file that has them all. Then you use CSS's background-position to only show the part of the big image that contains the icon you want. This may look like too much of a hassle, but when you think about the number of HTTP requests you save by getting one image instead of ten, then it starts making sense. Y! proves this point by using this technique on its homepage. Since the technique is used on what is probably one of the top visited pages on the web, I would considered it production-ready :)

You can get an idea of how Y! homepage works with its image assets by using Firefox's Web Developer extension: "Images -> View Image Information". In case you don't browse with Firefox packed with Web Developer extension (then you should!), then you can check the copy that I made - copy is here. Get a load of this guy for example.

Updated Dec 02, 2006:
Just deleted one comment by mistake (I got so much spam comments), pointing out that the correct syntax is:
document.getElementsByTagName("*").length
where * is quoted.
This is true, a typo on my part.

Thank you very much Philip, I'm so sorry I deleted your comment :(

 

JSON renderer for Text_Highlight

Friday, October 27th, 2006

Text_Highlighter is one of my favourite PEAR packages, seems like I'm addicted to highlighting source code. After adding BB code and simple HTML renderers and an ABAP code syntax definition, today I played with adding a JSON renderer. Useful in case you want to get highlighted source code in your new shiny AJAX app.

Array renderer

After I did the JSON renderer, I said, OK, what if I want to tweak the JSON output just a bit (or the output from any renderer for that matter)? Add more options? Nah, I had a better idea, I scrapped the whole thing and did an Array renderer first. If you have the array output from the renderer, it's trivial to format it as JSON, or XML, or HTML, or anything. I believe even the exisitng Text_Highlighter renderers should be rewritten, to extend an Array renderer. Anyway, back to JSON.

Demo

To see the JSON renderer in action, you can go to my hiliteme.com site and check the JSON tab.

Source

The source code is available here - JSON.phps which extends Array.phps. To test, you need to add the two renderers to your PEAR repository under Text/Highlighter/Renderer

Example

So let's say you need to highlight the PHP code

<?php
    echo "Hello Highlighted World!";
?>

You create an instance of Text_Highlighter and Text_Highlighter_Renderer_JSON and call the highlight() method, assuming that the code you need highlighted is in $source

<?php
// dependencies
require_once 'Text/Highlighter.php';
require_once 'Text/Highlighter/Renderer/JSON.php';

// instance
$json_options = array();
$json_options['tabsize'] = 4;
$json_options['enumerated'] = true;
$renderer =& new Text_Highlighter_Renderer_JSON($json_options);
$highlighter =& Text_Highlighter::factory($_POST['language']);
$highlighter->setRenderer($renderer);

// do the highlighting
$json_result = $highlighter->highlight($source);
?>

Now $json_result will look like:

[["inlinetags","&lt;?php"],["code"," \\n    "],["reserved","echo"],["code"," "],["quotes","&quot;"],["string","Hello Highlighted World!"],["quotes","&quot;"],["code","; \\n"],["inlinetags","?&gt;"]]

As you see the JSON output is an array, one element per highlighted keyword, and in this array there is a sub array - class/keyword. If you want to display this in your page (let's say you got it from an AJAX call), you can do a loop through the array and surround the keywords with span tags of the selected style:

// say your ajax call returned var src 
// var src = xmlhttp.responseText;
var data = eval(src); 

var res = '';
for (var i in data) {
    res += '<span class="hl-' + data[i][0] + '">';
    res += data[i][1];
    res += '</span>';
}

var el = document.getElementById('some-div').
el.innerHTML = '<pre>' + res + '</pre>';

Here I used innerHTML, you can also use DOM, but in this case you need a special case for the "\n" so that you can create a br element to address IE's habit of ignoring line feeds in a DOM-generated pre tag.

BTW, if you don't set the enumerated option to true, you'll get objects inside the main array, check hiliteme.com's JSON tab for an idea of how this would look like.

 

JS includes – the saga continues…

Wednesday, October 25th, 2006

The problem in question is how to find out a dynamically included JavaScript file is actually loaded. The concept of JavaScript includes is here, the IE-only solution is here. The IE solution is to use the onreadystatechange event that is fired when a new script is included. It also works for dynamically loaded CSS files using a new link DOM element. Thanks to the comment from Björn Graf, I tried using onload event to test if the new script is included using Firefox. It worked!

The code

What we have here (demo) is trying to include a .js file and an .css file, creating new script and link DOM elements. Then I'm attaching event listeners to those new elements - one onload and one onreadystatechange. The script that is included (jsalert.js) has one alert().

var css;
function include_css(css_file) {
    var html_doc = document.getElementsByTagName('head')[0];
    css = document.createElement('link');
    css.setAttribute('rel', 'stylesheet');
    css.setAttribute('type', 'text/css');
    css.setAttribute('href', css_file);
    html_doc.appendChild(css);

    // alert state change
    css.onreadystatechange = function () {
        if (css.readyState == 'complete') {
            alert('CSS onreadystatechange fired');
        }
    }
    css.onload = function () {
        alert('CSS onload fired');
    }
    return false;
}

var js;
function include_js(file) {
    var html_doc = document.getElementsByTagName('head')[0];
    js = document.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', file);
    html_doc.appendChild(js);

    js.onreadystatechange = function () {
        if (js.readyState == 'complete') {
            alert('JS onreadystate fired');
        }
    }

    js.onload = function () {
        alert('JS onload fired');
    }
    return false;
}

Results

As you can probably guess, the results are different in IE and FF.

  • CSS inclusion - IE fires both events, onload first, then onreadystatechange. FF fires nothing.
  • JS inclusion - IE fires onreadystatechange. FF fires onload. Both will execute the script before firing the event.

Conclusion

1. So there is, after all, a cross-browser way to tell when a JavaScript is actually included and that is to attach two event listeners - onload and onreadystatechange.
2. In IE you have two ways to tell when a CSS is included.

 

Deferring scripts for faster rendering

Monday, October 23rd, 2006

So it turns out one can defer scripts, meaning give the browser a hint that a script will not mess up with the page right now, so it won't affect the rendering and can be delayed for later. This could give you increased performance during the initial page load and rendering, since you're giving the browser a clearance to temporarily skip this script and continue with the other magic it's doing.

The syntax is simple, just add defer attribute to the script tag and (to be XHTML compliant, give it a) "defer" value. Like this:

<script type="text/javascript" defer="defer">
  // deferred ... 
</script>

<script
  type="text/javascript"
  src="defer.js"
  defer="defer">
</script>

Believe it or not, the defer attribute, which originated from MS, is now a part of the standard XHTML, as well as HTML4.01 strict and is not supported by FireFox.

I put up a little demo, if you want to see it in action. In my demo, if you check it with FF (all defers ignored) you'll get:
ff.png
With the defers, in IE, you get:
ie.png
Have in mind that there's not guarantee it will always happen like this. If, for example, an external non-deferred script is taking a while to download, a deferred one might get processed it it's ready. So you cannot rely on this, you can merely instruct the browser: "Hey, dude, this is a low priority, do the rendering first, the best you can..."

 

When is a JavaScript include ready?

Monday, October 23rd, 2006

This is a follow up to my article (the most popular on my blog based on the comments) about the JavaScript includes, the technique to include new .js files after the page is loaded, by using DOM to create a new script tag. The problem that is discussed in the comments is how to find out when/if the new script was actually downloaded. Here's a solution (IE only!).

Today I came accross an article on MSDN, written back in 1998 where they talk about readyState property of an inline JavaScript. So I decided to try it in conjunction with my JS includes. It worked! The solution is IE-only, but probably there is something similar for Firefox. Please post a comment if you know one.

The idea is that after a new DOM element (a script tag) is created, you can have access to the readyState property of the element. If it says "complete", then the new script is included and it's OK to call functions from it. If you want to "listen" when the script download will be completed, you can attach an listener to the onreadystatechange event, just like with XMLHttpRequests.

Here's an example:

var js;
function include_js(file) {
    var html_doc = document.getElementsByTagName('head').item(0);
    js = document.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', file);
    html_doc.appendChild(js);

    // alert state change
    js.onreadystatechange = function () {
        alert(js.readyState);

        if (js.readyState == 'complete') {
            // safe to call a function
            // found in the new script
            imready();
        }
    }
    return false;
}

This also works if you decide to include new CSS files on the fly. If you want to know in your javascript when the CSS is downloaded, you can do the same check.

Here's a demo that includes a CSS and alert()s onreadystatechange and also includes a JS, alerts the state change and when "complete", calls a function from the newly inlcluded script.

This solution to the problem "When is my include loaded?" is perfect, if you ask me, very clean and simple. The problem is it's IE-only, but the good news is that FF won't give you an error, it will just work as if the extra code was not there, simply because FF won't fire an onreadystatechange event.

2006-10-25 update: Thanks to the comments, the cross-browser way is here.

 

Yahoo API search with JavaScript alone

Thursday, October 12th, 2006

Previously I've mentioned how I do image searches with my bigger daughter, hunting for images of Cinderella, Ariel the little mermaid, and other equally beautiful princesses. So I thought why not build a custom little app for the little kid to do this, I mean, her, being the good (and beautiful) girl that she is, totally deserves it. Here are the very basics of such an app, my idea was to do a little demo how you can consume Y! services to do web and image searches, using nothing but JavaScript. Look ma, no server! ;)

» demo

JS alone

The cool think about Y! web services is that they provide you different options for the output format of the results they return. You can get XML, php serialized string or JSON. (No SOAP, thank you very much!). I think more companies should consider such an offer, I mean since you get your service to work, the output format is just a ... a format, nothing special.

How do you make the request? With JavaScript includes, meaning you use DOM methods to sreate a new <script> tag and add it to the head of your page. The src attribute of the new script tag points to the Y! service, a longish kind of URL with a bunch of parameters.

When making your request, you say that the output format should be JSON. Then you provide a callback function name. What Y! service does is it calls the function you gave, passing the JSON output as a parameter. Let's say you made a request to:

http://..../...&output=json&callback=myfunction

and we assume the result of your request is an array:
[1,2,3]
The Y! service will write out:
myfunction([1,2,3])
At the end, your function gets called, receiving the response from the call to the Y! service. It's now up to this function to decide what to do with the result.

Implementation

It all starts with a simple form and a placeholder for the result:

<body>
<form
    action=""
    method="get"
    onsubmit="ysearch.doSearch(
                this[0].value,
                'content',
                this[1].checked
              );
              return false;">
  <input />
  <input type="checkbox" />Image search
  <input type="submit" value="go!"/>
</form>
<div id="content">&nbsp; </div>
</body>

Submitting this form calls the doSearch() method of the ysearch class.

ysearch class has a few properties:

// url of the image search service
this.image_url = "http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?";
// url of the web search service
this.web_url = "http://api.search.yahoo.com/WebSearchService/V1/webSearch?";

// ID of element that will
// display the result
this.where = '';
// is this an image search?
this.image_search = false;

// parameters for the search
this.url = "output=json"+
           "&callback=ysearch.process" +
           "&appid=YahooDemo" +
           "&start=1" +
           "&results=5" +
           "&query=";

// reference to the last included script
this.last_include = false;

Then comes the doSearch method. It does the following:

  1. Checks input parameters to see if this is a request for a web search or an image search. The two types af searches use a different web service with a different URL
  2. Appends the search query to the URL
  3. Creates a new script tag with SRC that was just figured out
  4. Saves a reference to the new script tag, so that it can be cleaned up with the next search

Here's the actual code for the doSearch() method:

this.doSearch = function(q, where, web_image) {

  this.where = where;

  var script_filename = "";
  if (web_image) {
      script_filename = this.image_url;
      this.image_search = true;
  } else {
      script_filename = this.web_url;
      this.image_search = false;
  }

  script_filename += this.url + escape(q);
  var html_doc = document.getElementsByTagName('head').item(0);
  // remove the last included script, we don't need it anymore
  if (this.last_include) {
      html_doc.removeChild(this.last_include);
  }
  var js = document.createElement('script');
  js.setAttribute('type', 'text/javascript');
  js.setAttribute('src', script_filename);
  html_doc.appendChild(js);

  // set the pointer to the last include
  this.last_include = js;

};

Becuase we passed the callback function name ysearch.process to the service, the new Y! script will call it, passing the search results as an object. Let's see what process() does, really nothing but looping through the results object (description of the object here and here) and spitting out HTML using the politically incorrect innerHTML. Here's the code:

this.process = function(resp) {

  if (resp.ResultSet && resp.ResultSet.totalResultsReturned > 0) {
    // loop through the results
    var out = '';
    for (var i = 0,
     max = resp.ResultSet.Result.length;
     i < max;
     i++) {

    var item = resp.ResultSet.Result[i];
    if (this.image_search) {
      out += '<p><a href="' + item.Url + '">'
      out += '<img src="' + item.Thumbnail.Url + '" /><\/a>';
      out += '<br />' + item.Summary + '<\/p>';
    } else {
      out += '<p><a href="' + item.Url + '">' + item.Title + '<\/a>';
      out += '<br />' + item.Summary + '<\/p>';
    }

    }
    document.getElementById(this.where).innerHTML = out;
  } else {
    alert('no results');
  }
}
 

OPML to HTML via JavaScript

Friday, October 6th, 2006

Earlier today in a discussion with the lovely Amy Stephen, I thought of posting the RSS feeds I read. So I was thinking I would need to approach this in a good old web 1.0. way, finding a way to scrap content from the Google Reader. Luckily for me, I saw they have an Export feature. Turns out the export is in some new to me OPML format, basically an XML document. The question was to get the XML and turn it into HTML the festest way. The answer I came up with was to use JavaScript.

Demo

The demo is here, it's using my OPML doc, but you can paste yours and get the result. Further if you want to modify the HTML produced you can (using FirefoxF's JS console or Firebug, or typing javascript:... in IE's address bar) tweak the HTML "templates" I'm using. Just set the properties opml2html.html_template and opml2html.item_template.

Implementation

Implementation was a breeze. I did an opml2html class/object with one method - parse() and two attributes which are the templates for the html result. Having already experimented with getting an XML document object out of XML string, this part was a matter of copy/paste.

var doc;
// code for IE
if (window.ActiveXObject) {
    doc = new ActiveXObject("Microsoft.XMLDOM");
    doc.async = false;
    doc.loadXML(opml);
// code for Mozilla, Firefox, Opera, etc.
} else {
    var parser = new DOMParser();
    doc = parser.parseFromString(opml,"text/xml");
}

The rest is getting the attributes from the "outline" elements and replacing the values in my html templates. There are two nested outline elements and we're interested only in the inner ones, so the ones that return TRUE when calling hasChildNodes() on them are skipped.

var outlines = doc.getElementsByTagName('outline');

var html = '';
for (var i = 0, max = outlines.length; i < max; i++) {

  curr = outlines[i];
  if (!curr.hasChildNodes()) {
    title   = curr.getAttribute('title');
    htmlurl = curr.getAttribute('htmlUrl');
    xmlurl  = curr.getAttribute('xmlUrl');
    html += this.item_template.replace(/{TITLE}/, title)
            .replace(/{HTMLURL}/, htmlurl)
            .replace(/{XMLURL}/, xmlurl);
  }

}
var opml_title = doc.getElementsByTagName('title')[0]
                    .firstChild.nodeValue;
html = this.html_template.replace(/{ITEMS}/, html)
               .replace(/{OPMLTITLE}/, opml_title);
return html;

For the full source, check the demo, should work in IE and FF.

 

3 ways to define a JavaScript class

Friday, September 29th, 2006

Introduction

JavaScript is a very flexible object-oriented language when it comes to syntax. In this article you can find three ways of defining and instantiating an object. Even if you have already picked your favorite way of doing it, it helps to know some alternatives in order to read other people's code.

It's important to note that there are no classes in JavaScript. Functions can be used to somewhat simulate classes, but in general JavaScript is a class-less language. Everything is an object. And when it comes to inheritance, objects inherit from objects, not classes from classes as in the "class"-ical languages.

1. Using a function

This is probably one of the most common ways. You define a normal JavaScript function and then create an object by using the new keyword. To define properties and methods for an object created using function(), you use the this keyword, as seen in the following example.

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = getAppleInfo;
}

// anti-pattern! keep reading...
function getAppleInfo() {
    return this.color + ' ' + this.type + ' apple';
}

To instantiate an object using the Apple constructor function, set some properties and call methods you can do the following:

var apple = new Apple('macintosh');
apple.color = "reddish";
alert(apple.getInfo());

1.1. Methods defined internally

In the example above you see that the method getInfo() of the Apple "class" was defined in a separate function getAppleInfo(). While this works fine, it has one drawback – you may end up defining a lot of these functions and they are all in the "global namespece". This means you may have naming conflicts if you (or another library you are using) decide to create another function with the same name. The way to prevent pollution of the global namespace, you can define your methods within the constructor function, like this:

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function() {
        return this.color + ' ' + this.type + ' apple';
    };
}

Using this syntax changes nothing in the way you instantiate the object and use its properties and methods.

1.2. Methods added to the prototype

A drawback of 1.1. is that the method getInfo() is recreated every time you create a new object. Sometimes that may be what you want, but it's rare. A more inexpensive way is to add getInfo() to the prototype of the constructor function.

function Apple (type) {
    this.type = type;
    this.color = "red";
}

Apple.prototype.getInfo = function() {
    return this.color + ' ' + this.type + ' apple';
};

Again, you can use the new objects exactly the same way as in 1. and 1.1.

2. Using object literals

Literals are shorter way to define objects and arrays in JavaScript. To create an empty object using you can do:
var o = {};
instead of the "normal" way:
var o = new Object();
For arrays you can do:
var a = [];
instead of:
var a = new Array();
So you can skip the class-like stuff and create an instance (object) immediately. Here's the same functionality as described in the previous examples, but using object literal syntax this time:

var apple = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

In this case you don't need to (and cannot) create an instance of the class, it already exists. So you simply start using this instance.

apple.color = "reddish";
alert(apple.getInfo());

Such an object is also sometimes called singleton. It "classical" languages such as Java, singleton means that you can have only one single instance of this class at any time, you cannot create more objects of the same class. In JavaScript (no classes, remember?) this concept makes no sense anymore since all objects are singletons to begin with.

3. Singleton using a function

Again with the singleton, eh? :)

The third way presented in this article is a combination of the other two you already saw. You can use a function to define a singleton object. Here's the syntax:

var apple = new function() {
    this.type = "macintosh";
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
}

So you see that this is very similar to 1.1. discussed above, but the way to use the object is exactly like in 2.

apple.color = "reddish";
alert(apple.getInfo());

new function(){...} does two things at the same time: define a function (an anonymous constructor function) and invoke it with new. It might look a bit confusing if you're not used to it and it's not too common, but hey, it's an option, when you really want a constructor function that you'll use only once and there's no sense of giving it a name.

Summary

You saw three (plus one) ways of creating objects in JavaScript. Remember that (despite the article's title) there's no such thing as a class in JavaScript. Looking forward to start coding using the new knowledge? Happy JavaScript-ing!

 

JSON JavaScript cookies

Wednesday, September 27th, 2006

Here's a little thing I came up with, which hopefully can make reading/writing cookies with JavaScript easier.

The big idea

The idea is to keep a JavaScript object (a hash array) of all little variable things you want to store in a cookie. Then, once ready, you encode the object into a JSON string and save it to a cookie. To load the data from a previously saved cookie, you decode the JSON string back into an object.

The tiny implementation

Having the little JSON lib from json.org, it's very easy. The solution was to have an object called prefs (the idea initially came when I wanted to save user preferences), which has:

  • data attribute - stores the data you want to save,
  • save() method, and
  • load() method.

The code is as follows:

var prefs = {

    data: {},

    load: function () {
        var the_cookie = document.cookie.split(';');
        if (the_cookie[0]) {
            this.data = unescape(the_cookie[0]).parseJSON();
        }
        return this.data;
    },

    save: function (expires, path) {
        var d = expires || new Date(2020, 02, 02);
        var p = path || '/';
        document.cookie = escape(this.data.toJSONString())
                          + ';path=' + p
                          + ';expires=' + d.toUTCString();
    }

}

Using the prefs object

In order to use this you need to satisfy dependencies first, including json.js and prefs.js:

<script type="text/javascript" src="json.js"></script>
<script type="text/javascript" src="prefs.js"></script>

Then you're ready to do save()s and load()s. If you need to delete a cookie, you call save() with date in the past. Here are some examples:

// save
prefs.data.something = "one"; // save one
// ... do other stuff ...
prefs.data.another = "two";
// ready to store?
prefs.save();

// another syntax
var to_save = {
    one: 1,
    two: 2,
}
prefs.data = to_save;
prefs.save();

// delete
var date_in_the_past = new Date(2000,02,02);
prefs.save(date_in_the_past);

// read
var what = prefs.load();
// load populates prefs.data and also returns
alert(what.something);
// or ...
alert(prefs.data.something);

Thanks

Thank you for reading! As always, any comments are appreciated.

 

Exploring Web1.0 kitsch – YUI snowflakes

Sunday, September 24th, 2006

Kitsch

So what's "kitsch"? Well, something that may look like an art, but is usually a thing of a bad taste, over-ornamented, glittering, too colorful, well, something a bit ugly, but liked by a lot of people. It's sometimes questionable what is kitsch and what is a really valuable piece of art, but anything arty is always a bit questionable, isn't it? More about kitsch on Wikipedia.

Web 1.0 kitsch

Here I don't mean to say that anything that is not web 2.0 is ugly, in fact I cannot say that everything that is not web 2.0 is (bad) web 1.0, because there's never a clear difference. Only because you don't have a tag cloud on your site, that doesn't mean your site is old and second best. There are kitschy web2.0 sites, of course, but here I'm thinking about those Web1.0 "effects", things that are hopefully in the past, we've all seen it, it's the rotating 3D @-signs, the animated gifs, the status bar animations, the things that follow your cursor and, of course, the ever so cool, snowflakes flying around the page.

Motivation

My 3-year-old daughter loves to browse with me, asking me to bring up images of Cinderella, Snow White and other favorites. Having no idea where to find them, I do an image search and then visit the sites that come up, hoping for the best. (BTW, that's not always safe, try "barbie") Most of the sites I get are kind of old, web 1.0. style, and one of them had these hearts flying around the screen. My daughter loved them. So I questioned myself how hard it would be to do something similar with all those new cool JS libraries we have today. I tried the YUI and it turned out it is easy, I believe I was able to do the snowflakes within an hour, most of that time spent on figuring out the "mathematical model" of what I was trying to do.

Not ideal

I'm the first to admit the solution is not ideal, for example my flakes are not of random size in IE (my fault) and the whole animation has little breaks every second or so in Firefox. I guess for the seconds bug it's either that YUI is not the best tool for the job or I'm using it the wrong way. But hey, this is a one-hour project. So let me tell you what I did.

The math

What I decided to do is have my snowflakes appearing on random place from top, left or right, and then disappearing at a random place on the left, right or bottom. Also to have a max of 20 flakes at any time, it's bad enough to have 20 to load you processor, more is just crazy. Then I have 4 random points on the screen for each flake that serve as control points when the flake is doing its curve.

Implementation

Using DOM I create a div that contains an asterisk, this is my snowflake. Then I'm using the Motion along the curve available from YUI in order to animate this div. This motion takes a start point, an end point and some control points, in my case 4. First thing to figure out is how much space we have on the screen. YUI helps with this:

max_x: YAHOO.util.Dom.getViewportWidth() - 20,
max_y: YAHOO.util.Dom.getViewportHeight() - 20,

Then the 4 control points are easy, just 4 random points:

var control = [];
for (i = 0; i < 4; i++) {
    control[i] = [
    Math.floor(Math.random() * this.max_x),
    Math.floor(Math.random() * this.max_y)
    ];
}

The hardest part was the start and end points. As I said, I decided to have start points from either top, left or right, so that gives me an array of three random points:

var start_points = [
    [Math.floor(Math.random() * this.max_x), 0],
    [0, Math.floor(Math.random() * this.max_y)],
    [this.max_x, Math.floor(Math.random() * this.max_y)]
];

First is the top where on the Y-axis I have always 0 and a random value for X, from 0 to max_x. The left is defined as X = 0 and Y is random from 0 to max_y, the right is X = max_x and Y is again random from 0 to max_y.

For the end points it's similar, only there instead of top of the screen I have the bottom. For the bottom Y is max_y and X is random.

var end_points = [
    [0, Math.floor(Math.random() * this.max_y)],
    [this.max_x, Math.floor(Math.random() * this.max_y)],
    [Math.floor(Math.random() * this.max_x), this.max_y]
];

Then I pick a random value from each of the two arrays

var this_start_index = Math.floor(Math.random() * 3);
var this_end_index   = Math.floor(Math.random() * 3);
var this_start = start_points[this_start_index];
var this_end   = end_points[this_end_index];

Once I have start/end and control points, I'm ready to create the new flake div, where the size of the flake and the DIV id are random:

// size of the flake
var flake_size = 10 + Math.floor(Math.random() * 20);

// random ID of the flake
var flake_id = "flake" + 99 + Math.floor(Math.random() * 99999)

// create the markup for the flake (using html2dom.com)
var html2dom_root = document.body;
html2dom_root_1_div = document.createElement("div");
html2dom_root_1_div.setAttribute("id", flake_id);
html2dom_root_1_div.setAttribute("style", "width: 5px; font-size: " + flake_size + "px");
html2dom_root_1_div_1_text = document.createTextNode("*");
html2dom_root_1_div.appendChild(html2dom_root_1_div_1_text);
html2dom_root.appendChild(html2dom_root_1_div);

Now I'm ready to create and setup the YUI animation (motion) instance:

// animation attributes
var attributes = {
   points: {
      to: this_end,
      from: this_start,
      by: [10, 10],
      control: control
   }
};

// setup animation/motion object
var myAnim = new YAHOO.util.Motion(flake_id, attributes);
// no easing
myAnim.method = YAHOO.util.Easing.easeNone;
// random duration
myAnim.duration = 20 + Math.floor(Math.random() * 20);
// on completion remove the flake and make more
myAnim.onComplete.subscribe(this.removeElement);
myAnim.onComplete.subscribe(this.make_flakes);
// go!
myAnim.animate();

The two functions that are "subscribed" on animation completion are responsible for removing the current flake div and for creating another set of flakes. The set of flakes generated is using the logic - generate a random number of new flakes (min 1, max 5) unless you've hit the upper limit of 20.

Demo

Here's the demo where you can see the whole script, I kept it in the HEAD part of the demo page.

 

quirks or no quirks bookmarklet

Wednesday, September 20th, 2006

Here's a bookmarklet that will tell you whether or not the browser renders your page in Standards Complaiance Mode or Quirks Mode. The bookmarklet will figure this out for the page as well as for all the frames (and their frames) recursively. Enjoy!

Install

Drag this to your bookmarklets/favorites or right click and add to favorites:

quirks or !quirks

Sample output

Here's a sample output, produced when used in my WordPress backend when writing this post:
quirks.png

As you can see the page has two frames (probably iframes, doesn't matter), one of them is rendered in Standards Compliant Mode (CSS1Compat) the other one is in Quirks Mode (BackCompat). The overall document is CSS1Compat as well. For the frames, if they were named, you would see the name of the frame before the URL brackets.

And this is GMail, wow, lotsa frames, none compliant ;)
quircks-gmail.png

The code

The code is pretty simple, just accessing the compatMode of the the document object. Here it is in more human readable form (not one long line like bookmarket code).

var response = 'Document mode: ' + document.compatMode;
function checkFrames(w) {
  if(w.frames && w.frames.length>0){
    response+='\\n\\n';
    for(var i=0;i<w.frames.length;i++){
      var fr=w.frames[i];
      try {
        response+=fr.name +
              ' ('+fr.document.location+') - '+
              fr.document.compatMode+'\\n';
      } catch (e) {
        response+='Could not access this frame\\n';
      }
      checkFrames(fr);
    }
  }
}
checkFrames(window);
alert(response);
 

AJAX MVC (so to speak)

Tuesday, September 19th, 2006

This is sort of a framework thing to create AJAX applications, based on the MVC design pattern. Yep, I have a lot of buzzwords here, I admit, but this shouldn't be taken too seriously. I was doing a bunch of small projects lately and I found myself using something like this little framework, without even thinking about it. Then I thought about it and I found that the scripts and the organization of them may resamble MVC a bit. So how does MVC fit when you mix things like thin and fatter client, HTML, JavaScript, XMLHttpRequest, PHP and CSS?

Usual AJAX app flow

What usually happens in an AJAX application is:

  1. you have an HTML page, styled with CSS
  2. you click on something
  3. JS sends request to the server (to a PHP script)
  4. JS updates the original HTML page

Mapping to the MVC pattern

OK, so what part of this process can be associated with a View, or a Model or a Controller? The Model is easy, it's the business logic, writing to a database and so on. This is the PHP script. The View? Obviously this is the HTML page and the CSS. But I'd like to think also about the JS that updates the page as part of the View. I mean it makes sense, it's updating the presentation part. Sometimes you even use innerHTML in the JS, but even if you use DOM, it becomes part of the HTML anyway. How about the Controller? Well, we have two controllers here. One that is on the server side, a PHP script that receives requests and "asks" the Model for the response. The other controller is on the client side, this is the JavaScript that decides what happens on a click of a button and sends an appropriate AJAX request to the PHP controller. Therefore I would consider any behavioural JS as part of the Controller, including attaching events as well as sending HTTP requests.

Here's an illustration:
AJAX MVC

In action (example)

I went ahead and implemented a very simple application to prove the concept. It's just a blank styled HTML page with a button. The HTML page includes two JavaScripts responsible for behaviours (Controller) and page updates (View). The page also includes a few unrelated helper javascripts, in my case I'm using the YUI library. The JS Controller attaches an event to the button. Then when you click the button, the JS Controller sends a request to the PHP controller. The PHP controller (just a simple switch) figures out what was requested and calls the appropriate object of the business model. In my simplistic case, the abovementioned "model object" is just a simple function, but this can be easily built upon. The Model returns (JSON-encoded) response, in this case it's a list of installed PHP extensions. Now the response is received by the View JS and it updates the page. After that the View calls another function from the JS controller that attaches new events to the new content. (Yep, a little glitch here, maybe it would have been better if the Model's response is handled by the JS controller which in turn calls the JS view updater, but anyway this is easy to fix)

Directory layout

Here's the directory structure:
AJAX MVC dir

One might argue that it's better if you don't mix .js, .css and .php files in the same directory but the whole idea is open to interpretations anyway, it's just an illustration of the idea.

The code for the example

We get to the fun part, the actual implementation. So we start with a simple .html page, the initial part of the view.

This is index.html

<?xml version="1.1" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>Welcome</title>
  <link rel="stylesheet" href="../view/styles.css" type="text/css" media="all" title="Default styles" />
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/yahoo/yahoo-min.js"></script>
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/event/event-min.js"></script>
  <script language="javascript" type="text/javascript" src="../_extras/yui/build/connection/connection-min.js"></script>
  <script language="javascript" type="text/javascript" src="../view/updates.js"></script>
  <script language="javascript" type="text/javascript" src="../controller/behaviours.js"></script>
</head>
<body>

  Welcome to my app!
  <br />
  <form action="" method="post">
    <input type="button" name="b" id="thebutton" value="I'm a button, click me!" />
  </form>
  <div id="content">&nbsp;</div>

</body>
</html>

As you can see, nothing special, simply including the CSS styles, the YUI "extras" and two other javascripts - one part of the View and one that is part of the Controller.

The Controller JS is responsible for attaching an event listener to the button.

This is an excerpt from the behaviours.js

// the behaviour class
var behaviours = {

    phpcontroller: "../controller/switch.php?request=",

    // more behaviour.methods....
}

// initial page load, attach onload event(s)
YAHOO.util.Event.addListener(
    'thebutton', 'click', behaviours.theButtonClick);

Now when the user clicks the button, the method behaviours.theButtonClick() is executed. It fires a request to the PHP controller switch and says that the request type is "loadSomething":

theButtonClick: function(e) {
  alert('Ouch! \n\nOK, I\'ll make a request for ya, buddy!');
  YAHOO.util.Connect.asyncRequest (
      'GET',
      behaviours.phpcontroller + 'loadSomething',
      {success: updates.writeContent}
  );
},

The PHP controller (controller/switch.php) receives the request, does a simple switch to validate the request type and then calls the appropriate (in my case just a simple) function from the business model. Here's the full switch.php code:

<?php
// is this a request?
if (empty($_GET['request'])) {
  die();
}
// get the business logic
include_once '../model/business.php';

// figure out the request
// and call the business logic object
switch ($_GET['request'])
{
  case 'loadSomething':
    echo loadSomething();
    break;
  case 'loadSomeMore': // not used, example
    echo loadSomeMore();
    break;
}
?>

The function loadSomething() from the PHP model gets a list of installed PHP extensions, encodes them into JSON and sends them back. This is a full listing of the ../model/business.php

<?php
function loadSomething() {
  $extensions = get_loaded_extensions();
  return '["'. implode('","', $extensions) . '"]';
}
?>

If you go back and look at the AJAX request, you'll see that on success, I call the updates.writeContent() method. The ../view/updates.js script contains stuff that updates the HTML of the original page, so its place is in the View part of the app. writeContent simply creates an HTML table with the results (the list of PHP extensions). Then I wanted to attach event listeners to this table just to change color, but it can be more than that. Attaching events is a job for the JS Controller, therefore a method of its class is called. Here's a full listing of updates.js:

var updates = {

  writeContent: function (xmlhttp) {
    if (!xmlhttp.responseText) {
      alert("I got nothing from the server");
    }
    var data = eval(xmlhttp.responseText);
    var write_to = document.getElementById('content');
    write_to.innerHTML = ''; // yeah, I know

    var html2dom_root = write_to;
    var table = document.createElement("table");
    var table_1_tbody = document.createElement("tbody");
    for (var i in data) {
      table_1_tbody_2_tr = document.createElement("tr");
      table_1_tbody_2_tr_1_td = document.createElement("td");
      num = 1 + parseInt(i);
      table_1_tbody_2_tr_1_td_1_text = document.createTextNode(num);
      table_1_tbody_2_tr_1_td.appendChild(table_1_tbody_2_tr_1_td_1_text);
      table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_1_td);
      table_1_tbody_2_tr_2_td = document.createElement("td");
      table_1_tbody_2_tr_2_td_1_text = document.createTextNode(data[i]);
      table_1_tbody_2_tr_2_td.appendChild(table_1_tbody_2_tr_2_td_1_text);
      table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_2_td);
      table_1_tbody.appendChild(table_1_tbody_2_tr);
    }
    table.appendChild(table_1_tbody);
    html2dom_root.appendChild(table);

    behaviours.updateTableBehaviour();
  }
}

(BTW, for the DOM part I'm used the help from my little tool html2dom to make my life a bit easier)

And finally here's the rest of the JS controller (behaviours.js), the method behaviours.updateTableBehaviour() that adds an event listener to the new table and the trClick() that handles clicks on this table. On click, it justs changes the color of the underlying row.

  trClick: function (e) {
    var target = (e.srcElement) ?
      e.srcElement.parentNode : e.target.parentNode;
    if (target.tagName == 'TR') {
      if (target.className == 'tr-on') {
        target.className = '';
      } else {
        target.className = 'tr-on';
      }
    }
  },

  updateTableBehaviour: function () {
    var el = document.getElementById('content').firstChild;
    YAHOO.util.Event.addListener(
      el, 'click', behaviours.trClick);
  }

Demo and downloads

  • Demo - the live example
  • Zipped demo - all the source code for the example
  • Template - the source code for the example but with the example part commented, so you can use it as a template for your next AJAX project. The only thing you need to do is to drop the YUI in the _extras/yui folder.

Thank you for reading, any comments welcome!

 

HTML2DOM

Thursday, September 14th, 2006

Here's this HTML-2-DOM service - http://www.html2dom.com What it does is pretty simple - you paste some HTML code and the output is JS script code that uses DOM functions to produce the same result. Could be useful when you're working on an AJAX-style app that generates new content using JavaScript.

I build this simple script, inspired by this great book I was reading - "Build Your Own AJAX Web Applications". In the book, the author sometimes starts with writing up what is the HTML code for the result you want to achieve, and then goes ahead with giving the DOM code. Because, you know, DOM code can be quite verbose and sometimes a bit hard to follow. So I thought, why not write up a simple tool to automate this HTML to DOM transition.

The code is not complicated at all, it just takes the HTML, treats it as an XML document, then loops through all the elements of the XML doc and all the attributes for each element. The script is here, hopefully reusable and you can grab it for your own projects if you wish. You can check the source of html2dom.com's index page for an example how to use the html-2-dom class.

Some limitations of the script (that I know of) are result of the fact that I'm treating the HTML as XML document. So you might get some errors if the HTML you paste is not well-formed, with all closed tags and so on. Also you cannot use &nbsp; and other entities, because XML doesn't know about them. What XML knows is only the pre-defined 5. And last, out of the different node types, my script understands only three - element, attribute and comment. I think it's enough for the practical purposes I was aiming at, even the comment type is a bit of a stretch.

So enjoy and as always, any feedback is welcome!

 

Greasemonkey – execute custom javascripts on any page

Thursday, September 7th, 2006

If you want to try executing custom local scripts on any page you visit, try Greasemonkey. Here's a 10 seconds tut.

The task is to create a custom script and to make sure it's executed every time you leave a page on phpied.com

  1. Install Gresemonkey from here
  2. Create a file phpied.user.js (all your custom scripts must end in .user.js) with the following
     var start = new Date();
     window.addEventListener("unload", function(e) {
        var end = new Date();
        var diff = Math.floor((end - start) / 1000);
        alert("Man, I spent " + diff +
              " of my precious seconds on this guy's page!" +
              " Now that's called investment!"
    
        );
    }, false);
  3. Open phpied.user.js in the browser. You'll see a message from Greasemonkey inviting you to install.
  4. Click Install... and you're done. Now this script will execute on every single page you hit. To change it so that it executes only when you visit phpied.com do:
  5. Right-click the monkey icon in the bottom-right of the browser screen. Select Manage User Scripts.
  6. Click the * in the Included Pages list, then hit Edit. Type "http://www.phpied.com/*" This means "execute this script on every page on phpied.com". Click OK.
  7. Reload this page to see the script in action.

N.B. To modify a user script, do not modify the original file where you had it initially on your file system, won't work, I tried it ;) Instead, modify the copy that GM has stored. Right-click the monkey icon -> Manage -> select your script in the listing on the left and click Edit.

I've read before about the Greasemonkey Firefox extension, but never tried it before yesterday. Never tried probably because of a comment on the sitepoint.com's article about Greasemokey. The comment I found so funny, yet true, was "I just don't like the idea of having to spend time on another person's website when I barely get enough time to spend on my own." Well, sometimes one might like to try custom scripts on his/her own site, for example to test some stuff on the production server without the risk of breaking something.

More resources:
- Home page
- Tutorial @ sitepoint
- Free ebook
- User scripts

 

Parent’s styles in an iframe

Sunday, September 3rd, 2006

Here's a JavaScript that let's you style an iframe just like its top parent. The script is basically just a proof of concept I did after talking to a friend about similar problem he has had in the past, so feel free to modify and use if you like it.

So I have a page, called big.html and a stylesheet for this page, called big.css. On the page there is an iframe that loads small.html. small.html has its own style, called small.css. What my little Javascript function does is:

  1. Getting all top parent's <link> tags
  2. Looping through the tags and checking if the rel attribute has value stylesheet
  3. For all stylesheets found, makes a copy of the link tag and all its attributes and adds it to the head of the iframed page

Here's the code:

function styleMe() {
  if(window.top && window.top.location.href != document.location.href) {
  // I'm small but I'm not alone

    // all parent's <link>s
    var linkrels = window.top.document.getElementsByTagName('link');
    // my head
    var small_head = document.getElementsByTagName('head').item(0);
    // loop through parent's links
    for (var i = 0, max = linkrels.length; i < max; i++) {
      // are they stylesheets
      if (linkrels[i].rel && linkrels[i].rel == 'stylesheet') {
         // create new element and copy all attributes
        var thestyle = document.createElement('link');
        var attrib = linkrels[i].attributes;
        for (var j = 0, attribmax = attrib.length; j < attribmax; j++) {
          thestyle.setAttribute(attrib[j].nodeName, attrib[j].nodeValue);
        }

         // add the newly created element to the head
        small_head.appendChild(thestyle);

      }
    }

    // maybe, only maybe, here we should remove the kid's own styles...

  } else {
    alert('I hate to tell you that, but you are an orphant :(  ');
  }
}

To see the code in action, see big.html.

 

Form auto-fill bookmarklet

Tuesday, May 16th, 2006

Intro

So here's the thing, we all know we hate forms, the only thing we hate more than forms themselves is actually filling out forms. But the forms are the interface to our web apps, so we cannot do without them. What we would love to do without, but can't, is skip the application testing part where you fill out forms like there's no tomorrow, just to make sure your app is rock solid.

And filling out forms is a pain. So for some time I wanted to get my hands on a little something that can fill out a form, any form, with a click of a button. JavaScript is ideal for such a task and the best sort of a "little something" is probably a bookmarklet. That is how this bookmark was born.

What is it, what it does?

This is a bookmarklet. You go to page that has one or more forms and you click the bookmarklet. It completes the form for you with some random data. The whole thinking was to have a form ready to be submitted and generating as less validation errors as possible. Here are some details:

  • All defaults are kept as they are
  • All passwords fields are completed with the same password, in case there is a password/password confirmation combo. The default password is "secret"
  • If a text field has the string "email" in its name, the auto-generated value would be a random string @ example.org
  • If a text field has the string "name" in its name, a name-looking value will be generated.
  • All checkboxes will be checked (who knows which one of them might be "Accept terms" or anything else that is required)
  • Multi-selects will have a random number of random options selected

Install

Right-click and bookmark or drag to your personal bookmarks toolbar.

Form auto-fill

Demo

Here's the demo.

The code

The demo and the code below are "normal" code, with proper indentation and all. The actual bookmark though has to be on one line and as small as possible, so it's pretty much unreadable. Ah, and while the demo will work in IE, the bookmarklet won't, because it's too big for IE. IE allows up to about 500 characters in the URL (or a bookmarklet) while mine is about 2000 "compressed" or 3000 cleaner. So unless I do something heroic in compressing the script, it won't work on IE. No biggie I'd say, since you'll be testing your application and most likely you use Firefox anyway.

The big picture

Using JSON, the class/object is called auto and it has the following interface:

var auto ={

    // a list of names that will be used to generate 
    // normal looking names
    names: "Steve Buscemi Catherine Keener ...",

    // this is where all the random words will come from
    blurb: "phpBB is a free...",

    // default password to be used in all password fields
    password: "secret",

    // the main function that does all
    fillerup: function() {},

    // helper function, returns randomly selected words
    // coming from this.blurb
    getRandomString: function (how_many_words) {},

    // helper function, returns randomly selected names
    // coming from this.names
    getRandomName: function () {},

    // returns this.password
    getPassword: function () {},

    // returns a random int from 0 to count
    getRand: function (count) {}
}

The actual form fill-out is initiated by calling auto.fillerup()

As you can probably guess, the only interesting function is fillerup(), so let me show you what it does.

fillerup()

In case you're wondering, the name of the function comes from a Sting song:
Fill'er up, son, unleaded.
I need a full tank of gas where I'm headed ...

The function starts by identifying all the elements candidate to be completed:

var all_inputs    = document.getElementsByTagName('input');
var all_selects   = document.getElementsByTagName('select');
var all_textareas = document.getElementsByTagName('textarea');

OK, we have our work cut out for us, let's start by looping through the selects:

// selects
for (var i = 0, max = all_selects.length; i < max; i++) {
    var sel = all_selects[i]; // current element
    if (sel.selectedIndex != -1
        && sel.options[sel.selectedIndex].value) {
        continue; // has a default value, skip it
    }
    var howmany = 1; // how many options we'll select
    if (sel.type == 'select-multiple') { // multiple selects
        // random number of options will be selected
        var howmany = 1 + this.getRand(sel.options.length - 1);
    }
    for (var j = 0; j < howmany; j++) {
        var index = this.getRand(sel.options.length - 1);
        sel.options[index].selected = 'selected';
        // @todo - Check if the selected index actually 
        //         has a value otherwise try again
    }
}

Then - textareas, they cannot be simpler. We only check if there isn't already a value and if there's none, we get two "paragraphs" of 10 words each.

// textareas
for (var i = 0, max = all_textareas.length; i < max; i++) {
    var ta = all_textareas[i];
    if (!ta.value) {
        ta.value = this.getRandomString(10)
                   + '\\n\\n'
                   + this.getRandomString(10);
    }
}

Next (and last), come the inputs. They are a bit more complicated as there are too many of them. Here's the overall code with the skipped details for each input type.

// inputs
for (var i = 0, max = all_inputs.length; i < max; i++) {
    var inp = all_inputs[i];
    var type = inp.getAttribute('type');
    if (!type) {
        type = 'text'; // default is 'text''
    }
    if (type == 'checkbox') {...}
    if (type == 'radio') {...}
    if (type == 'password') {...}
    if (type == 'text') {...}
}

We're absolutely unforgiving when it comes to checkboxes - just check them all, no questions asked, take no prisoners.

if (type == 'checkbox') {
    // check'em all
    // who knows which ones are required
    inp.setAttribute('checked', 'checked');
    /* ... ooor random check-off
    if (!inp.getAttribute('checked')) {
        if (Math.round(Math.random())) { // 0 or 1
            inp.setAttribute('checked', 'checked');
        }
    }
    */
}

Next, do the radios. They are a bit more complicated, because once we have an element, before checking it, we need to verify that there are no other radios with the same name (and in the same form) are already selected and checked.

if (type == 'radio') {

    var to_update = true;
    // we assume this radio needs to be checked
    // but in any event we'll check first

    var name = inp.name;
    var input_array = inp.form.elements[inp.name];
    for (var j = 0; j < input_array.length; j++) {
        if (input_array[j].checked) {
            // match! already has a value
            to_update = false;
            continue;
        }
    }

    if (to_update) {
        // ok, ok, checking the radio
        // only ... randomly
        var index = this.getRand(input_array.length - 1);
        input_array[index].setAttribute('checked', 'checked');
    }
}

Passwords - trivial, just make sure you always set the same password.

if (type == 'password') {
    if (!inp.value) {
        inp.value = this.getPassword();
    }
}

And finally - the text inputs. We try to guess the nature of the text field by its name. Here there's plenty of room for improvement and more guesses.

if (type == 'text') {
    if (!inp.value) {
        // try to be smart about some stuff
        // like email and name
        if (inp.name.indexOf('name') != -1) { // name
            inp.value = this.getRandomName() + ' ' + this.getRandomName();
        } else if (inp.name.indexOf('email') != -1) { // email address
            inp.value = this.getRandomString(1) + '@example.org';
        } else {
            inp.value = this.getRandomString(1);
        }
    }
}

C'est tout

That's it, hope you liked it and start using it ;) Any comment or suggestions - let me know.

 

Opacity for the thumbs

Friday, May 5th, 2006

I've been just toying with the CSS opacity to make fancier image thumbnail rollovers, it's actually quite easy. The idea is when you have a thumbnail photo gallery to make the thumbs semi-transparent and, on mouse over, to remove the transparency and show the real image as is.

All it takes is this little piece of CSS:

a img {
    opacity: 0.55;
    filter:alpha(opacity=55);
}
a:hover img {
    opacity: 1;
    filter:alpha(opacity=100);
}

Here's a demo. CSS file is here.

The demo uses Y!API to get a few images (I never seem to have good images laying aroud when I need them). The API call gets JSON output from the Yahoo! search API to make it easy (and server-side free) to display the results. You may peek into the JS source if you're curious (more Y! JSON search API here), but doesn't have anything to do with the main purpose - the thumbs rollovers.