Home, Inicio João Gomes Mota contact Web Design
JavaScript
RESEARCH PHOTOGRAPHY WEB DESIGN DISCOVERIES
Portugues Português

How to make an Advent Calendar with JavaScript

  1. Introduction
  2. Architecture
  3. Preparation
  4. HTML page creation
  5. Cookies
  6. Initialisation
  7. Open and close windows
  8. Is the calendar complete ?

1. Introduction

These pages show, step by step, how to make an Advent Calendar with JavaScript. The main purpose is to illustrate programming and web design techniques to build a functional application; thus the Calendar might look unappealing to many readers.

What is an Advent Calendar ?

An Advent Calendar is a traditional object associated with Christmas, with its roots traced back to the countries of Central and Northern Europe.

For Christians, Advent is the four Sunday period before Christmas Day, which occurs each year on December, 25th.

With an Advent Calendar, families prepare for Christmas, representing each day from December, 1st to December, 24th with a numbered window (or door, drawer, etc.). Every day, the corresponding window is open, revealing a surprise. Depending on the culture and religious beliefs of the families and the age of the recipients - often children -, the surprise might be a candy, a little gift, a drawing, a story about Advent, etc..

Assuming that material Calendars bring material surprises, virtual Calendars should offer virtual surprises, such as images, animated images, sounds, texts, films or applications. In this example, an elementary solution is presented: one single image is cut into 24 tiles, as in a mosaic. Each tile corresponds to one day, and as the days go by, the mosaic is completed:

On day 12th
fig.1 - The mosaic with days 1 to 12 "open"

In case one tries to open a "window" before the due date, a snowman appears to suggest one to be patient:


fig.2 - On the 12th, one must wait two days to be allowed to open window 14

Finally, when one reaches the 24th and all the windows are open, the mosaic is complete and the background grid vanishes.

 

2. Architecture

Once the purpose and operation of the application are known, the first step is to establish a functional specification, defining an architecture encompassing all the internal modules, defining interfaces between modules and the user interfaces in order to meet the requirements. Since this is an elementary application, four rules suffice to specify it:

One page for the Calendar, one page for the final scene.
Because the Calendar is so simples, this solutions suffices; moreover, the final page is very similar to the Calendar page, which suits the tutorial approach.
The Calendar takes the centre of the page, pushing the links to the support pages to the bottom of the page.
All links respond always
This is basic Web rule oft neglected. It shaped the application interface: users must always get some sort of feedback on clicked links, regardless of the application state.
It is obvious to expect that a closed window opens when clicked, revealing a tile of the mosaic. However, it is less obvious what to do if the user clicks on an open window. To solve this ambiguity, the window closes, restoring the image with the day's number.
In addition to the two cases above, if one user tries to open a window too early, a snowman appears and one second later, the closed window with the day's number is restored.
Hence, the user always get a visual feedback on each clicked link.
The state of the Calendar is saved between visits
The purpose of the Calendar is to open one window per day. Thus, it is interesting to restore the last condition at each new visit. Otherwise, one would be forced to restart the calendar from day 1 at each visit.
Cookies were used to fulfil this requirement.
Images are reused
In order to minimise the download time, to smoothen the transition between pages and to simplify the explanations contained in this tutorial, the images on the first page tiles are reused on the second.
Thus, once the Calendar is complete, the users moves smoothly to the second page, because most of the contents are already on cache.
See the full size Calendar in a new window.

3. Preparation

Before proceeding to the HTML page creation, it is necessary to prepare the visual elements:

  1. Choose a photograph.
    The size of the photograph - or any type of image - depends on the size of the monitor. In this case, the photograph is somewhat smaller than 800x600 pixels, since this is the minimum resolution of most screens.
  2. "Cut" 24 windows on the photograph.
    The numbers on the windows were assigned at random to keep the mosaic aspect. The images on the windows could have been cut with various sizes; however a design with 24 equal squares was preferred for the sake of simplicity.
    Around the images, the margins of the photographs are replaced with a background-filled grid. This reinforces the metaphor of the windows. The grid will vanish when the mosaic is complete.


fig.3 - Model of the photograph with 24 windows cut

  1. Save the photo margins as individual images to use on the final page, to remove the grid effect. In this example there are five images with the photograph width (to separate rows) and 28 images with the window height (to separate columns).
  2. Draw 24 numbered windows to hide the mosaic photographs. A graphics program is recommended to maintain graphic coherence.
    The windows should match the dimensions of the photographic tiles.
  3. Draw a window for those days that can not be open yet. In this case, the waving snowman was used to suggest patience (see fig. 2).

The code on the Calendar page will be explained in the following sections, while the corresponding code is shown on the left column.

 

4. HTML page creation

Once the images are prepared, one can proceed to the coding of the Calendar on a HTML page.

<tr>
  <td colspan="13"><img src="pix2.gif" width="750" height="12"></td>
</tr>
<tr>
  <td><img src="pix2.gif" width="15" height="110"></td>
  <td><A HREF="javascript:dia(12);"><img NAME="i12" src="12.gif" width="110" height="110" border="0" alt="12"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(19);"><img NAME="i19" src="19.gif" width="110" height="110" border="0" alt="19"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(3);"><img NAME="i3" src="3.gif" width="110" height="110" border="0" alt="3"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(8);"><img NAME="i8" src="8.gif" width="110" height="110" border="0" alt="8"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(21);"><img NAME="i21" src="21.gif" width="110" height="110" border="0" alt="21"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(2);"><img NAME="i2" src="2.gif" width="110" height="110" border="0" alt="2"></A></td>
  <td><img src="pix2.gif" width="15" height="110"></td>
</tr>

The centre of the Calendar is the 4-row by 6 column grid.

The grid includes the visible images (the numbered windows and the tiled photographs) and the transparent spacers <img src="pix2.gif"> that change dimensions according the margins size.

The code on the left corresponds to the first row of the calendar, encompassing days 12th - 19th - 3rd - 8th - 21st - 2nd.

Each grid cell <TD>...</TD> contains one image, and the grid images have an associated name "NAME=i..." to allow JavaScript functions to manipulate its content. The size of the grid images is the same for all cells: squares with a 110 pixels edge. The "alt=.." tag guides users once the windows are open.

The links on the opening windows are associated to the JavaScript command HREF="javascript:dia(...);" that will swap the images in case the user clicks on them.

There is a vertical spacer of 12 pixels width between each two neighbouring windows in the same row. At the edge of the rows, the spacers are 15 pixels wide. Between each two rows there is a spacer 750 pixels wide and 12 pixels high (see the first line of code).

Below the Calendar the page concludes with links

  1. to the explanatory page in Portuguese,
  2. to this page
  3. to restart the calendar, closing all the tiles.

 

 

5. Cookies

The whole calendar is supported on the cookie concept. Thus, although possible, it is awkward to use the Calendar without cookies.

A cookie is used to store the status of the Calendar on the user's computer between visits. With cookies, the application restores the status of the Calendar from the previous visit. In case the user switches to a new computer or a new browser, the Calendar will be presented in its initial status, with all the tiles hidden..

document.cookie= "advent=000000000000000000000000;";

Each cookie has two parts: the name and a string with the contents; a third part with the expiration date is recomended. In the Calender case, the name is advent= and the string is a sequence of 24 characters, one per each tiled window:

  • "0" for a closed window and
  • "1" for an open windows

If the user's computer does not accept cookies the previous state is not stored and the user must restart the process at each new visit.

hoje = new Date(),
janelas = new Array(25),
isCookie;

function init() {
var i,c=0;

6. Initialisation

The application uses three global variables:

  • Today's date, hoje,
  • an array with the status of each tiled window and
  • a flag that highlights whether cookies are accepted.

if(document.cookie.length>0) isCookie=true;
else {
 document.cookie="advent=000000000000000000000000;";
 if(document.cookie.length>0) isCookie=true;
 else isCookie=false;
}

The first step is to verify if the cookie is already active, in which case isCookie=true, otherwise the cookie is initialised. If the initialisation fails, cookies are not accepted.

ckOffset=document.cookie.indexOf("advent=")+6;
The next step is to compute the string index where the cookie content starts. This value depends on the browser.

if(isCookie) {
  for(i=1;i<25;i++) {
    janelas[i]=document.cookie.charAt(ckOffset+i);
    if(janelas[i]=='1') {
      len=document.images['i'+i].src.length;
      document.images['i'+i].src = document.images['i'+i].src.substr(0,len-3)+"jpg";
    }
  }
 
teste();
}
return;
}

The following code only applies in case cookies are active.

The cookie string is copied, one character at a time, into array janelas[]. Note that the array begins at 0 (where character is stores '='), while the Calendar windows begin at 1.

If the array entries are set to '1', the numbered window image is replaced by the tiled photograph.

Important: for this swap, it is necessary that the numbered windows and the tiled photographs have exactly the same filename, except the extension. This example uses JPG for photographs and GIF for numbered windows.

3.gif 3.jpg

fig.4 - The "closed" window and the corresponding "open" window.

Once the windows are restored to the previous visit status, a test is performed to determine if the Calendar is complete.

function reiniciar() {
if (document.cookie.length>0) document.cookie = "advent=000000000000000000000000;";
document.location = "1.html";
return;
}

The user can restart the Calendar using function reiniciar(). It resets the cookie content to 0000 and reloads the Calendar page on the browser, using the command document.location="1.html".

 

7. Open and close windows

The open/close operation is the core of the application: at each image swap, the cookie is updated.

Function dia(d), which takes the day of the clicked window as a parameter, performs the operations.

function dia(d) {
var str="advent=",
    len, i, ano, expira;

if(d <= hoje.getDate()) {
  len=document.images['i'+d].src.length;
  if(janelas[d]=='1') {
    document.images['i'+d].src = document.images['i'+d].src.substr(0,len-3)+"gif";
    janelas[d]='0';
  }
  else {
    document.images['i'+d].src = document.images['i'+d].src.substr(0,len-3)+"jpg";
    janelas[d]='1';
  }

Variable str="advent=" will hold the new cookie value and is initialised in the beginning of the function. The variable expira will hold the cookie expiration date.

The first test verifies if the selected window date is earlier or equal to the current month date, in which case, the window image is swapped:

  • if janelas[d]=='1', the photographs (image .JPG) is hidden and e the window is "closed": janelas[d]=='0' and the window holds the day number (image .GIF).
  • if janelas[d]=='0', the image with the day number (.GIF) is hidden and the window is "open": janelas[d]=='1', and the window shows the tiled photograph (.JPG).
  for(i=1;i<25;i++) str += janelas[i];

  ano=hoje.getYear();
  if(ano<1000) ano+=1900; // patch NN4
  expira=new Date(ano+1, hoje.getMonth(), hoje.getDate(), hoje.getHours(), hoje.getMinutes(), hoje.getSeconds());

  str += ";expires="+expira.toGMTString();
  document.cookie = str;

  setTimeout("teste()",5000);
}

Next, the cookie is updated, filling variable str with each position of array janelas[].

Following recommended practices, an expiration date is chosen: exactly, one year later. It is necessary to compute this date.

Some versions of Netscape Navigator 4 had a bug in function .getYear() and returned a two digit year. There is a simple patch to overcome this issue, although this might be negligible today.

Next, the expiration date is appended to variable str and the cookie is written.

Finally, after a five seconds delay, the test to verify if the calendar is complete is called.

else {
  document.images['i'+d].src='janela_fechada.gif';
  setTimeout("espera("+d+")",1000);
}
return;
}


function espera(d) {
document.images['i'+d].src=d+'.gif';
return;
}

If the chosen day is later than current date, the image with the window number is swapped by image 'janela_fechada.gif' and, after a one second delay, function espera(d), which takes the day of the clicked window as a parameter, is called. .

Function espera(d) restores the window with the number on the clicked image.

 

8. Is the calendar complete ?

Each time a window changes status, the Calendar completeness is computed and the user is notified in case the 24 windows are open.

function teste() { // check 24 conditions
var i, completo=true;

if(hoje.getDate()>=24) {
  for(i=1;i<25;i++) if(janelas[i]!='1') {
    completo=false;
    
break;
  }
  if(completo) document.location="25.html";
}
return;
}

Function teste() checks if the current date is equal or later to 24 and if the 24 windows are open (the 24 tiled photographs are shown). Note that sequence of conditions corresponds to the logical conjunction.

This implementation does not allow the user to complete the application after January, 1st, or the first of each month, for that matter, since the first condition is false. To keep the Calendar open all month long, it is necessary to remove the first condition. Otherwise, the Calendar can be completed only from the 24th to the end of each month.

There are many ways to check if all 24 windows are open. The most basic is to test the 24 conditions in sequence.

function teste() { // add the open window indexes
var i,c=0;
if(hoje.getDate()>=24) {
  for(i=1;i<25;i++) if(janelas[i]=='1') c+=i;
  if((c==300)) document.location="25.html";
}
return;
}
However, while I was writing this application, I remembered a story about Gauss, the mathematician, and arithmetic progressions and I chose a different solutions: sum the index of all open windows and check the result.
About this Nativity Scene

The Nativity figures in these photos are made from hand-painted clay figures bought in small town fairs around Portugal.

This is the most common representation of the Nativity in portuguese rural regions: a carpet of soft moss picked in the woods over which the typical figures of the Nativity are placed: Jesus, Mary, Joseph, the donkey and the cow, the wise men and the shepherds.
Popular traditions add many more figures from the Portuguese landscape and memories: castles, churches, bridges, white houses, etc..

In this basic Calendar the gift for taking the patience to complete the calendar is to view the image without the grid.

To this purpose, a second page was created, with the name 25.html. Its layout is an exact copy of the main page; only the spacers are replaced by the images that fill the blanks in and the links on the windows are removed:


fig.4 - The mosaic is complete

  1. Introduction
  2. Architecture
  3. Preparation
  4. HTML page creation
  5. Cookies
  6. Initialisation
  7. Open and close windows
  8. Is the calendar complete ?
Your feedback is welcome, should you have any doubt or comments about this application.
©2004-05 João Gomes Mota
Home, Inicio  → WEB DESIGN → APPLICATIONS
Published in February 2005. Last update: March 2005. inicio da página