Posts Tagged ‘dom’

My Ajax Experience presentation - YUI for control freaks

Saturday, October 4th, 2008

I just spent several splendid days at the Ajax Experience in Boston, MA and was asked to deliver a talk about the YUI. Here’s what I went for: I wanted to make very clear that while YUI is a library much like the others, the real power of it lies in the control it gives you over the whole frontend development process from start to end.

  • The CSS components make sure that creating CSS based layouts and typography are as easy cross-browser as libraries make JavaScript development
  • The YUI DOM control allows you to monitor the size of the browser window, the position of the document in the window and the dimensions of any element. You can use this power to control things like fixed positioning and element overlap and even monitor font resizing.
  • I explained the concept of Custom Events and how the -debug versions of YUI will notify you as a developer at any moment of execution about the internal happenings.
    * I showed the development tools - the YUI logger, profiler and test suite and how they make your development process much less random.

The slides are available on slideshare and here are the code examples mentioned in the slides for browsing and to download as a zip.

How to get all IDs and classes used in a document?

Monday, August 4th, 2008

This was a question from one of the attendees of my JavaScript course, and here is one solution:


 function getIDsAndClasses(elm,parent){
   var elm = elm || '*';
   if(typeof parent !== 'undefined'){
     if(typeof parent === 'string'){
       var parent = document.getElementById(parent) || document;
     }
   } else {
     var parent = document;
   }
   var elms = parent.getElementsByTagName(elm);
   var ids = [];
   var classesFilter = {};
   var i = elms.length;while(i–){
     if(elms[i].id !== ”){
       ids.push(elms[i].id);
     }
     if(elms[i].className !== ”){
       var singles = elms[i].className.split(’ ‘);
       var j = singles.length;while(j–){
         classesFilter[singles[j]] = singles[j];
       }
     }
   }
   var classes = [];
   for(var i in classesFilter){
    classes[classes.length]=classesFilter[i];
   }
   return {ids:ids,classes:classes}
 }

You can call this method either with no parameters or filter it down by providing an element name and a parent element. The parent element could be a DOM reference or a string, both work.

In any case, the output is an object with a property of ids containing an array of ID names and a property called classes with an array of class names.

The script filters out duplicate classes and gets all applied classes - provided they are space separated.

You can see it in action (in the FireBug console) here: Test getIDsAndClasses

I tried using regex to get the IDs and classes, but that turned out to be a mess in JavaScript.

Any faster way?

The missing native DOM methods - according to my course attendees

Thursday, July 31st, 2008

During the course I am currently giving in Sunnyvale on basics of the DOM and progressive enhancement I asked the attendees which methods seem to be missing in the native DOM implementation and this is what we came up with:

  • createLink(url,text) - a shortcut method to create a link with a text node inside - something you constantly have to do when creating interfaces
  • insertAfter(newNode,oldNode) - there is an insertBefore, but no insertAfter
  • removeNode(node) - the native removeChild is convoluted
  • textElement(elementName,text) - it seems not necessary to create an Element, then create a text node and apply it, this could be one step
  • addScript(url) - to lazy-load JavaScripts
  • normalizeNode(node) - to remove those pesky line-breaks that interfere with nextSibling or previousSibling
  • getText(node) - retrieving the text content of a node that is either a text or an element node
  • setText(node,text) - setting the text regardless of node type

I asked the attendees to come up with a method each and to present them, here’s what we got:


jukuhelpers = function(){
  function createLink(url,text,cssClass){
    var link =  document.createElement('a');
    if (typeof url === 'string'){
      link.setAttribute('href', url);
    }
    if (typeof text === 'string'){
       link.appendChild(document.createTextNode(text));
    }
    if (typeof cssClass === 'string'){
      link.className = cssClass;
    }
    return link;
  }
  function insertAfter(newNode,oldNode){
    oldNode.nextSibling
      ? oldNode.parentNode.insertBefore(newNode, oldNode.nextSibling)
      : oldNode.parentNode.appendChild(newNode);
  } 
  function removeNode(node){
    if (node) {
      node.parentNode.removeChild(node);
    }
  }
  function textElement(elementName,text){
    if (typeof text === 'string'){
      var txtElement = document.createElement(elementName);
      var txtNode = document.createTextNode(text);
      txtElement.appendChild(txtNode);
    }
  return txtElement;
  }
  function addScript(url){
    var s = document.createElement('script');
    s.setAttribute('type', 'text/javascript');
    s.setAttribute('src', url);
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(s);
  }
  function getText(node){
    var txt;       
    if (node && node.nodeType === 1) {
      if (node.hasChildNodes()) {
        txt = node.firstChild.nodeValue;
      }
    }
    if (node && node.nodeType === 3) {
      txt = node.nodeValue;
    }
    return txt;
  }
  function setText(node,text){
    if (node && node.nodeType === 1) {
      if (node.hasChildNodes()) {
        node.firstChild.nodeValue = text;
      }
      else {
        node.appendChild(document.createTextNode(text));
      }
    }
    if (node && node.nodeType === 3) {
      node.nodeValue = text;
    }
  }
  function normalizeNode(node){
    if(node.hasChildNodes){
      var spaceTest = /^\s+$/;
      var children = node.childNodes;
      for(var i=0;children[i];i++){
        if(children[i].nodeType === 3){
          if(spaceTest.test(children[i].nodeValue)){
            children[i].parentNode.removeChild(children[i]);
          }
        }
      }
    }
  }
  return{
    createLink:createLink,
    insertAfter:insertAfter,
    removeNode:removeNode,
    textElement:textElement,
    addScript:addScript,
    getText:getText,
    setText:setText,
    normalizeNode:normalizeNode
  }
}();

You can get the jukuhelpers.js file if you want to use it yourself.

Anything else that is missing or any bugs in this?

Training new developers in the valley - Day 3

Sunday, July 27th, 2008

On the third day we went deeper into the oddities of the DOM and how to access and create content in the current document. One thing I realized very fast is that teaching DOM before the days of FireBug was much easier - you can lead the group from property to property and method to method. With FireBug they are much faster in finding out what can be done and also get a lot of goodies that FireFox provides but aren’t the standard.

We went through the basics - setAttribute and the differences when using it in comparison to the shorter property notation (MSIE sees expando properties as attributes and in order to remove them you’d have to null both the attribute and the object property).

We then moved on quickly to createElement and createTextNode and detected the need to apply them to the document somehow to make them appear.
This lead to insertBefore and appendChild and we discovered that there is no insertAfter, which is a logical fault in the DOM.
As a remedy I asked the group to write their own insertAfter, which is a good exercise to re-iterate looping through child nodes as well as using the creation methods. There are of course several methods of writing an insertAfter, but I was pretty much stunned to see one of the attendees to come up with one I hadn’t thought of:


function insertAfter(newElm,elm){
  var clone = elm.cloneNode(true);
  elm.parentNode.insertBefore(clone,elm);
  elm.parentNode.replaceChild(newElm,elm);
}

I am not too sure about its performance, but I really like the logic of it: this way you can be sure the new node will be after the old one regardless of where the old node is (last node, first or somewhere in the middle). This also means you don’t need to fork and use insertBefore or appendChild respectively.

Other examples we went through were removing nodes with a certain class (to show the problem of the changing length when iterating over a nodeList and removing elements) and writing a simple form validation script that changes the labels of mandatory fields when they are empty.

I wrapped up the day using the JSON output of the del.icio.us API to write out a list of bookmarks and tags:

<script type="text/javascript">
function mydelicious(data){
  var ul = document.createElement('ul');
  for(var i=0,j=data.length;i<j;i++){
    var current = data[i];
    var item = document.createElement(’li’);
    var link = document.createElement(’a');
    link.appendChild(document.createTextNode(current.d));
    link.setAttribute(’href’,current.u);
    item.appendChild(link);
    var nested = document.createElement(’ul’);
    var tags = current.t.join(’,');
    var nesteditem = document.createElement(’li’);
    nesteditem.appendChild(document.createTextNode(tags));
    nested.appendChild(nesteditem);
    item.appendChild(nested);
    ul.appendChild(item);
  }
  document.body.appendChild(ul);
}
</script>
<script type="text/javascript" src="http://feeds.delicious.com/feeds/json/codepo8/?callback=mydelicious"></script>

In an extra step I then asked the team that instead of calling the API in an own script tag to progressively enhance a link and create the script tag dynamically:

<p><a href="http://del.icio.us/codepo8" id="bookmarks">My Delicious Links</a></p>
<script type="text/javascript">
function makemydelicious(){
  var source = document.getElementById('bookmarks');
  var url = source.getAttribute('href');
  var url = url.split('/');
  var username = url[url.length-1];
  var apiurl = ‘http://feeds.delicious.com/feeds/json/’ +   
                username + ‘/?callback=mydelicious’;
  var s = document.createElement(’script’);
  s.setAttribute(’type’,'text/javascript’);
  s.setAttribute(’src’,apiurl);              
  var head = document.getElementsByTagName(’head’)[0];
  head.appendChild(s);
}
makemydelicious();
</script>

We then ranted a bit about the non-logic of DOM methods and their parameter order (”why is document.insertBefore(oldNode,newNode) not possible but instead we need oldNode.parentNode.insertBefore(newNode,oldNode)??”) and came up with a wishlist of DOM methods that should be native:

  • createLink(url,text)
  • insertAfter(newNode,oldNode) - consistent with the native DOM inconsistency
  • removeNode(node)
  • textElement(elementName,text)
  • addScript(url)
  • normalizeNode(node) - removing whitespace
  • getText(node)
  • setText(node,text)

This list is also the courses homework, and we’ll take a look at the results on Monday.

Training new developers in the valley - Day 1

Thursday, July 24th, 2008

I am currently in Sunnyvale, California to teach a bunch of bright young people the ways of the DOM and YUI. I am one of the trainers in the Juku project of Yahoo! (alongside Ross Harmes and Douglas Crockford) and give a 12 day intensive course. Naturally, this keeps me busy and I don’t get to blog as much - or so I thought. Actually I don’t see much harm in doing a day-by-day report on what we covered here, as a reminder for myself and maybe an inspiration for your own training courses.

Day one is traditionally for me the day to test the waters and see how my style of training suits the group. I hate sitting in lecture-style training with a massive binder and interspersed with coding exercises that are more hello world than anything useful. Instead I do more of a hands-on style where I try to get the attendees to form and run most of the course with me aiding by steering and helping out. There is an overall master plan for the course (you have to cover x amount of content in y amount of time, after all) but the individual days might differ a lot according to the subject matter. I normally tend not to use the computer as much as possible (as it leads people to surf around and get distracted with work mail) but in this case this’d be tough to do.

I got to know the attendees and asked them who they are, what they do, why they are here and what they want to get out of the course. I was very happy to hear that whilst the subject knowledge level of the group differs greatly from member to member, they all wanted to “learn how to apply things in the real world” and “get in-depth knowledge of how browsers deal with the DOM and DOM scripting”.

I started by explaining that DOM scripting is more than just manipulating the DOM but that we coined the term (in the now defunct WaSP working group) as a quality mark of DHTML development. I re-iterated the need for separation of development layers and the ideas behind progressive enhancement.

  • We set up a valid HTML document, explaining what is needed for any document to become one - doctype, a title, encoding, language, reading direction and all the necessary elements.
  • We talked about where to put styles and scripts and the impact of their location on performance
  • We then went to learn about the DOM, setting up and using Firebug to play with it and took a look at getElementById() and getElementsByTagName().
  • We talked about optimizing for loops and iterating over resulting HTMLCollections with as few code as possible whilst not sacrificing maintainability or performance.
  • We went into reading HTML attributes and discovered the pains of reserved words like class and for
  • Last but not least we created our own getElementsByClassName function.

The last aspect was especially interesting, as I deliberately kept the specifications of the function loose and asked the group to plan it on a whiteboard before plunging into it. The discussion around the planning showed that there are millions of ways to approach this problem and that if you mix developers that come from a UI-centric background with hard-core C++ developers you get interesting approaches to the same problem

You can see the results of the different teams in this document. The different examples are commented out with the quick commenting trick so to try them out, just add another slash in front of the /* preceeding the functions.

Day two is about to start…

Wait till I come! is the blog of , a developer evangelist living and working in London, England. Download vcard.

Feed me, Seymour: Entries (RSS) and Comments (RSS).