designing a javascript widget

4 05 2008

I was playing with the idea on how to create javascript widgets. As an experiment I created a simple picture album viewer.

I have been having some problems with declarative widgets. I guess declarative widgets make it easier to add them to a page but it is bad for customizations and more dynamic behavior.

As an exercise I developed a prototype for a Photo Album Viewer. No magic here, you need to know HTML, JavaScript and CSS.

If you are looking for production-ready javascript widgets to use you wont find it here.

Javascript toolkits are great to solve cross-browser issues and usually give a boost in the productivity. But I will use plain Javascript. Tested on Firefox 2 only.

Photo Album Viewer

I want to design a basic Photo Album Viewer. Just one picture is show as the main content and an series of thumbnails from the album appears on the bottom. The user selects photos from the thumbnails clicking on it. A image from the thumbnail is displayed on the main content.

The whole point of developing a widget is to create something that will provide a richer user experience than a plain old HTML. I will focus on usability but will ignore colors, round borders…

Dealing with Images

Before creating the widget itself lets take a look in some issues of this application.

1) Thumbnails selection

The idea is that all photos will be available on its real size and on reduced size(thumbnail). The thumbnail bar was supposed to be "carousel" where you can go over all pictures in the album and load the thumbnails only when needed. To keep it simple I will load all of them in a scroll bar.

2) loading images dynamically.

Loading all images at once is not an option. Images will be loaded only on request. (though I plan to add some kind of pre-loading later). To load images dynamically, just create a "img" element and set it’s "src" attribute. You can register an event for "onload" to be notified when it is completed loaded.

var imgEle = document.createElement("img");
imgEle.onload = function(){...};
imgEle.setAttribute("class","nodisplay");
imgEle.setAttribute("src",photoURL);
containerNode.appendChild(imgEle);

3) Adjusting picture size

The whole picture must fit inside the display area, and of course the width/height ratio must be preserved. To do this some basic geometric calculation were done.

The real picture dimensions are Picture.width and Picture.height. The Picture.maxWidth/maxHeight are the maximum bounding box for the picture. The picture is adjusted to its biggest size without overflowing the bounding box and the margin is adjusted so the image appears in the center.

Picture.prototype.adjust_size = function(){
    var ratioX = this.width / this.maxWidth;
    var ratioY = this.height / this.maxHeight;
    var used,space;
    if(ratioX > ratioY){
      used = this.height / ratioX;
      space = this.maxHeight - used;
      this.node.style.width = this.maxWidth;
      this.node.style.height = used;
      this.node.style.marginTop = space / 2;
    }
    else{
      used = this.width / ratioY;
      space = this.maxWidth - used;
      this.node.style.width = used;
      this.node.style.height = this.maxHeight;
      this.node.style.marginLeft = space / 2;
    }
};

4) Usability

Images take time to load. So I added a "loading ajax" gif so the users know what is going on. I keep track of the image status (not-loaded/loading/loaded). The images are displayed only when completely loaded. Loaded images are cached.

The widget

Ok. Now we have enough material to think on the widget. The widget is nothing more than putting all parts together.

After creating the widget the DOM is supposed to have this structure.

<div>                             <!-- div containing the whole album -->
  <div class="picview">           <!-- display are for the picture -->
     <img src="ajax.gif" ...>     <!-- loading ajax gif  -->
     <span class="lightbox" ...>  <!-- another indication picture is loading -->
                                  <!-- an image NOT being displayed -->
     <img src="/images/pic1.jpg" class="nodisplay" ...>
                                  <!-- the image being displayed -->
     <img src="/images/pic4.jpg" ...>
                                <!-- more images....>
  </div>

  <div class="thumbbar">            <!-- thumbnails -->
     <a ...><img src="/images/thumb1.jpg"></a>
     <a ...><img src="/images/thumb2.jpg"></a>
                                    <!-- more thumbnails -->
  </div>
</div>

Where "…" indicates that some attributes need to be created dynamically (on client side).

So the million dollar question is: Where should be DOM structure be created? Server, client, both?

The problem is that you can not do everything on the server. You need to attach events to the DOM nodes, keep some runtime info of the widget. Most of the time I see widgets are mix a server and client code, this makes things harder to understand and work. If I could choose server only or client only I would go for server. But between sever & client or just client I go for client only.

So on the HTML page I just add a "onload" function to create the widget:

<script type="text/javascript">
  var ALBUM;
  function start(){
     var picview = document.getElementById("picview");
     ALBUM = new Album(document.getElementById("album"));
  }
</script>

Ok. So now I just need to pass some data. What is the best way to pass data to javascript? No not HTML. JSON of course:

<div id="album" class="data">
    [{thumb:'/media/images/thumb1.jpg',photo:'/media/images/pic1.jpg'},
    {thumb:'/media/images/thumb2.jpg',photo:'/media/images/pic2.jpg'},
    {thumb:'/media/images/thumb3.jpg',photo:'/media/images/pic3.jpg'},
    {thumb:'/media/images/thumb4.jpg',photo:'/media/images/pic4.jpg'},
    {thumb:'/media/images/thumb5.jpg',photo:'/media/images/pic5.jpg'},
    {thumb:'/media/images/thumb6.jpg',photo:'/media/images/pic6.jpg'},
    {thumb:'/media/images/thumb7.jpg',photo:'/media/images/pic7.jpg'}]
</div>

Of course this content would be created dynamically.

So instead of writing some HTML on server than have javascript to parse, modify, and attach events I just pass some data to the javascript widget. The widget is responsible for creating the HTML (easier than parsing and modifying). Another advantage is that you don’t need to add more code to make the widget work programmatically.

Take a look in the full example.

Advertisements

Actions

Information

5 responses

5 05 2008
eyal

first of all, nice implementation – simple and easy
But I wanted to direct your attention to a small change, that will make this script run a bit nicer.
when you use var img = document.createElement(“img”);
before you attach it’s src, you want to attach it’s onload event. this will be things run so much faster. when the new image object gets a new src attribute and will stop running the rest on the script, until the full image is loaded. Since is due to the fact that javascript is not asyncrolic when loading images.
the best practice is to attach the onload event first, and then the src

6 05 2008
schettino72

thanks for the tip regarding the onload event.
But I guess you are wrong when you say images are not loaded asynchronously.
Just try this:

<html><head>
<script type="text/javascript">
function addImg(){
var imgEle = document.createElement("img");
imgEle.onload = function(){console.log("img loaded")};
console.log("before set src");
imgEle.setAttribute("src","/media/images/thumb1.jpg");
console.log("after set src");
document.body.appendChild(imgEle);
console.log("after append element");
}
</script>
</head>
<body onload="addImg()"></body>
</html>

I got this:

before set src
after set src
after append element
img loaded

21 08 2008
prajwala

what eyal said is correct. you need to set onload event before setting src. otherwise onload event will not fire on IE.

18 01 2009
Javascript on the client as an alternative to Perl/PHP/Python on theserver - Page 3 | keyongtech

[…] Hi Dan, I also started playing with this idea some months back. i wrote a post on this subject: https://schettino72.wordpress.com/200…script-widget/ I am successfully using this approach to design js widgets. > If a webpage is created on the […]

22 08 2009
Namith

Someone might find this widget interesting

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: