Tuesday, January 27, 2009

Step by Step Separation

It would be worth reading Chris Heilmann's article "The seven rules of Unobtrusive JavaScript". We basically get the idea that even if javascript is not available the web page should work well. To do that, javascript must not interfere with HTML directly but by means of hooks or triggers. Let's see a simple example in which the structure, presentation and behaviour is mixed in the body of the web page. We'll try to separate each layer into three different blocks in the same page.
<html>
<script>
function onClick(sUrl) {
  window.open(sUrl)
}
</script>
<body>
  
    NBC Official site
  
</body>
</html>
When the user clicks the green link "NBC Official site" a new page will open. But this code is extremely obtrusive. The href attribute has a value which require javascript enabled. Besides, there is an onclick attribute that will make possible the opening of a new window. Again, javascript is necessary. First of all, we'll clean any behavioral attributes and values from the HTML. The result is this:
<html>
<script>
function onClick(sUrl) {
  window.open(sUrl)
}
</script>
<body>
  
    NBC Official site
  
</body>
</html>
Now, the behaviour of this page is not the one we wanted. We must find a way to open a new page when clicking on the link. Here is where the idea of javascript triggers comes in handy. Let's add a class attribute to the link tag:
<html>
<script>
function onClick(sUrl) {
  window.open(sUrl)
}
</script>
<body>
  
    NBC Official site
  
</body>
</html>
And finaly let's write the necessary javascript code that will open a new page when the user clicks a link of class "externalLink". I've used prototype as the base library to simplify the javascript code.
<script type="text/javascript" src="prototype.js"></script>
<script>
document.observe("dom:loaded",function() {
  $$('.externalLink').each(function(element) {
      element.observe("click",function(event) {
          window.open(element.href,'','top=10,left=10,width=800,resizable');
          event.stop();
      })
  });
});
</script>
There is only on final detail to take out presentation tags from the HTML code. We need to add a style block in the head of the document. This is the new proposal:
<html>
<head>
  <style>
    .externalLink { color:green; }
  </style>
  <script type="text/javascript" src="prototype.js"></script>
  <script>
    document.observe("dom:loaded",function() {
        $$('.externalLink').each(function(element) {
            element.observe("click",function(event) {
                window.open(element.href,'','top=10,left=10,width=800,resizable');
                event.stop();
            })
        });
    });
  </script>
</head>
<body>
  NBC Official site
</body>
</html>
There's is a tricky thing about the way I used to separate presentation and behaviour from the structure of the page, the HTML. Not everybody will accept using the same class value "externalLink" to trigger javascript and CSS at the same time. It could be a bit confusing if we changed the name of the class thinking that the only consequence is that we must change only the name of the CSS rule. Doing so, we lose the behaviour of the page. It's easy to avoid the problem if CSS and javascript is in the same page, but be careful if those blocks are in separate files. We would have a coupling problem between the CSS file and the javascript file. I prefer to adopt a solution that consists in using a naming convention for those values that are aimed to trigger javascript. We could use "jsExternalLink". The initial js states clearly that this value is a hook for javascript to play its role. However, values to trigger CSS rules will stay the same. Up till now it's been much more common to use class and id values to trigger CSS than javascript. That is why I only use this naming convention for javascript triggers.

No comments: