2005-08-20

Adding calendar navigation to your Blogger template

A later follow-up article is available on this topic, where I dig into making a pop-open calendar with keyboard bindings for navigating back and forth through your post archives, among other things.

Not caring much for Blogger's idea of navigation for my personal diary, today I set about adding a calendar widget similar to one I have been accustomed to having last time I was blogging, in a self-hosted do-it-all-yourself environment. Lazy-bastardness has changed that situation since, but I digress -- let's get down to business, shall we?

Someone probably made a decent calendar widget in the past few years, right? Right. Good; then I won't waste a lot of time boring my head off tweaking HTML+CSS, which I, quite honestly, detest.

Okay, download and unzip the 1.0 release, and dig in. First things first, we put it in a suitable location on some hosting of our own, something like http://www.example.com/jscalendar-1.0/ (you probably have it on some different location, yadda yadda) and toss in the necessary bits to add it to your template's <head> portion. I tucked mine in just before the end of the head section, since we will be tweaking other things in the vicinity very soon anyway. So here we go; add these lines:

<style type="text/css">@import url("/skins/aqua/theme.css");</style>
<script type="text/javascript" src="/calendar.js"></script>
<script type="text/javascript" src="/lang/calendar-en.js"></script>
<script type="text/javascript" src="/calendar-setup.js"></script>

(don't forget to prepend your URL prefix to the src/url arguments!) and we now have a beautiful bit of GNU LGPL calendar code loaded and ready, just aching to get to do something for us. So, pick a spot in your template where you want the finished calendar widget to go, and add an empty <div id="calendar-container"></div> tag there. If you're eager to try out a dummy right away, by all means go ahead: temporarily add this right after the div:

<script type="text/javascript"><!--
Calendar.setup( {flat:'calendar-container'} );
//--></script>

(Tada!)

Okay, not very interesting before it does anything; I agree. Before we continue, though, we need to tweak some settings (or you need to rewrite some of my code to fit your own preferences). First, track down Settings -> Archiving -> Archive Frequency and set it to "Daily". (You need this to get each entry in your blog listed in the page - and hence to show up in the calendar.) Then (and the order you perform these changes is important) set Settings -> Formatting -> Archive Index Date Format to 2005-08-20 (ISO YYYY-MM-DD format; very nice to parse). In case you're alienated by practical date formats, don't panic; the visitor will not see these dates anyway. Setting Date Header Format will however be visible, but my example code further down will assume you did, so if you are lazy or share my preferences you'll want to have ISO dates here, too. These are the dates shown near the head of a post in your average Blogger template, and they are used by the script to detect which date should be highlighted as "this post". But now I'm getting ahead of myself.

Now dive into your template and look up the archive portion - it's probably surrounded by a pair of <MainOrArchivePage></MainOrArchivePage> tags. Replace those with a <div id="archive"></div> tag (since you want your navigation widget on all pages, including the single-day's-posts page), and since we want the archive portion easily targetable for hiding.

That could be done with a CSS style rule #archive { display:none; }, but I opted to perform the hiding from javascript instead, knowing that there still are a few who navigate the web with javascript turned off. This way, you won't condemn them to live without navigation, even though they don't get the benefit of your spiffy DHTML generated-live calendar widget. A shame, but that's their choice, and it's very decent of you to degrade gracefully.

Make sure the contents of that div contains something like

<BloggerArchives>
<li><a href="<$BlogArchiveURL$>"><$BlogArchiveName$></a></li>
</BloggerArchives>

(the <li> and any other additional tags besides the <a> tag aren't needed, but don't hurt either). Just make sure the div swallows up every bit you want to hide for the crows who can see your nifty calendar. Now drop the proof-of-concept <script> tag you might have tossed in below the other div, and finally add this chunk at the end of your <head> tag:

<script type="text/javascript"><!--
function calendar()
{
var archive = document.getElementById( 'archive' );
if( archive )
{
archive.style.display = 'none';
var notes = {};
var links = archive.getElementsByTagName( 'a' );
if( !links.length ) return;
var i, j, node, date, y, m, d;
for( i=0; i<links.length; i++ )
{
node = links[i];
date = node.innerHTML.split('-'); // YYYY-MM-DD
y = parseInt( date[0], 10 ); if(!notes[y]) notes[y] = {};
m = parseInt( date[1], 10 ); if(!notes[y][m]) notes[y][m] = {};
d = parseInt( date[2], 10 ); notes[y][m][d] = node.href;
}
var dates = document.getElementsByTagName( 'h2' ), thisDate;
for( i=0; i<dates.length; i++ )
if( dates[i].className == 'date-header' )
{
var ymd = dates[i].innerHTML.split('-'); // YYYY-MM-DD
thisDate = new Date( parseInt( ymd[0], 10 ),
parseInt( ymd[1], 10 )-1,
parseInt( ymd[2], 10 ) );
break;
}
top.notes = notes;
Calendar.setup(
{
step : 1, // show every year in the year menus
date : thisDate, // selected by default
flat : 'calendar-container', // div element
range : [ parseInt(links[0].innerHTML), y ],
showOthers : true, // show whole first/last week of month
flatCallback : dateChanged, // what to do on date selection
dateStatusFunc: disableDateP // which dates to show/hide how
});
}
}

// Returns true for all dates lacking a note, false or a css style for those having one.
// Exception: today does not return true, even if it lacks a note. (improves navigation)
function disableDateP( date, y, m, d )
{
var now = new Date;
if( (y == now.getFullYear()) &&
(m == now.getMonth()) &&
(d == now.getDate()) )
return false;
return noteFromDate( date ) ? false : true;
}

function noteFromDate( date )
{
var note = top.notes[date.getFullYear()] || {};
note = note[date.getMonth()+1] || {};
return note[date.getDate()];
}

function dateChanged( calendar )
{
if( calendar.dateClicked )
{
var note = noteFromDate( calendar.date );
if( note )
window.location = note;
}
}//--></script>

and edit the <body> tag following it to read <body onload="calendar()"> -- and you're all set to go! Dates with entries on them are clickable, and you can navigate around among the years and months as you well please, without any time-consuming web server roundtrips too.

Of course, feel free to experiment, perhaps most easily with the parameters in the Calendar.setup() call, or indeed with any other aspect; having the date-header parsing code match your date listing preferences or whatnot. Another good exercise is probably trying out a layout where you add the widget as a popup rather than the flat embedded one - that way you'll get extremely nifty keyboard navigation via the arrow (and control) keys too in the widget.

Share and enjoy! Spread the love! :-) And feel free to link your creative results.

Note: you always have to click "Republish Entire Blog" when adding a new date that previously had no entry, if you want your visitors to be able to click back in time and then find their way back to the future again using the calendar. Remember, already published pages won't know of your latest entry otherwise, and will thus not make it clickable.

43 comments:

  1. the world is a better place just because people like you exist! Amazing!

    ReplyDelete
  2. What's "nan"? It says "undefined nan"

    ReplyDelete
  3. "Not a Number", most likely. Sounds very much like a bug. Were you to say where that appeared (in the calendar featured in this blog? Your own, when trying to install it?) and which browser make that you used to peek at it, I might be able to further track down (and hopefully fix!) the problem.

    ReplyDelete
  4. Fixed! Sorry...

    In my blog, I was installing the calendar and it said "nan" where it was supposed to be the numbers of the days and instead of "Month 2005" it said "undefined nan". In your tutorial says "set Settings -> Formatting -> Archive Index Date Format to 2005-08-20" So I went and set "Settings -> Formatting -> Date Header Format to 2005-08-20" so now both "Archive Index Date Format" and "Date Header Format" is like that and... it works fine now. Maybe I miss something in the instructions. Thanks!

    ReplyDelete
  5. Ah! Excellent, a bug already fixed. Or, well, already bypassable, anyway -- the real fix, would be to introduce the more advanced date parsing code I wrote for my previous and next date links tutorial, which could allow for using most of the other available date formats as well. I'll leave that on my "might do" list, for now.

    ReplyDelete
  6. Hey buddy, sorry to bother you again but its not showing up in IE, thats the one I installed, the one in your blog is good in IE. So... what im doing wrong!

    ReplyDelete
  7. I don't know. Does it have a URL where I can peek and see what happens?

    ReplyDelete
  8. Oh sorry, of course:
    daleclick-foro.blogspot.com

    This is like a test-blog I have for that purpose.

    ReplyDelete
  9. Fixed again!

    It was the language, I was using spanish, change it back to english (calendar-en.js) and fixed.

    ReplyDelete
  10. How annoying. For some reason, the calendar project I picked to work off has really bad, old and outdated language files for non-English languages; the Swedish language file was just as bad before I fixed mine.

    If you take your trouble to edit it to work for you (it's probably just a few words missing, I'd venture guessing), I'd appreciate a copy or link to it so I can update mine, so others can benefit from it as well.

    It's not very hard and you don't have to understand much about programming to fix it; start off the English file, or try looking at the Spanish one and compare it to the English, to see which words are missing and should be added.

    ReplyDelete
  11. Sooner or later I will have to do that so I let you know then. Thanks for that script is very good.

    ReplyDelete
  12. Hi its me again...

    In your calendar, when I hover over a day number it says at the bottom of the calendar the title of that day post. In my calendar it just shows the abreviated date. How I change it so it displays the title instead of the date... or is there something wrong or missed.

    Thanks for helping.

    ReplyDelete
  13. That functionality is not yet covered by any article of mine, so it's not that you are doing anything wrong.

    It is unfortunately a very complicated thing to do with Blogger, though, as they again do not provide the tools to do it. I would suggest read ing this post and joining me in trying to convince Blogger to provide the tools for doing this in a sane fashion.

    What I do is also hinted at in my comment to that post -- I bookmark all of my posts at Del.icio.us with date readings and wrote a rather complex script to load all those bookmarks back from Del.icio.us when a visitor accesses the blog, to then puzzle it all together so hovering an entry links that date's archive page.

    For a number of reasons, I have not written an article on how to address these, the way my blog does it. This is not intended as a verbal attack, if it comes out that way; I'm just formulating why I think there won't ever be one either, unless Blogger provides the functionality above.

    • * Very much time labour goes into pulling off this.

    • * It's a rather small (though, I admit, very nice) improvement, compared to the work involved to you yourself as a blog maintainer.

    • * It would be even more work for me, to try to help those who would try and fail to do the above, for any number of reasons (and there are so very many steps involved, all of which must be done right for it to work), and I want my articles to be useful even to novice users.

    • * I'm not motivated to, in part because of all the work that would go into it, in part because I don't see much appreciation for my work, (or any at all, if measured by grateful donations of a dollar or thereabout).

    ReplyDelete
  14. THANKS SO MUCH!!!

    i've been tryin to get a calendae archive for months....

    u saved me!!!! hahaha...

    ure an angel..

    ReplyDelete
  15. sorry .. just curious.
    is there any way to import another skin. probably the blue one. coz my webpage bckground is black.

    thanks~~~

    ReplyDelete
  16. Sure. Instead of the line that picks the skin skins/aqua/theme.css, pick another theme option. In the base install, these are on offer, and if you know your way around CSS, you can easily configure it any other way you like, too:

    skins/tiger/theme.css
    calendar-blue.css
    calendar-blue2.css
    calendar-brown.css
    calendar-green.css
    calendar-win2k-1.css
    calendar-win2k-2.css
    calendar-win2k-cold-1.css
    calendar-win2k-cold-2.css
    calendar-system.css

    ReplyDelete
  17. Thanks a lot, now I have a cool canlendar too.
    非常感谢,参考你的文章和代码,我自己也拥有了一个日历。
    助かりました。とてもすばらしいカレンダーです。本当にありがとうございました。

    ReplyDelete
  18. Thanks, u r a superstar...this is just what i've been looking for...i'm no deep techie but i figured it out from your instructions pretty quick.

    ReplyDelete
  19. You ROCK.
    There's absolutely no reason for me to leave Blogger now...
    Thank you.

    ReplyDelete
  20. i have tried and tried to get the calander to work on my blogger and it just doesn't show up. how can i tell if it is the server i uploaded the package too or if i put something else wrong in my template?

    ReplyDelete
  21. Hi a.o., Im not ecmanaut but it should be the server or hosting where you at, try another hosting im using 100webspace and it works just great...

    ReplyDelete
  22. Hi,

    I dun think I need to tell what a great favour you have done for many who implemented the calendar but I would like to thank you for the effort put in.

    Have a question: I am using for my Header Date Format something like April 15, 2006 and would like to keep using this instead of the one in your instructions 2006-04-15. Is there any way I can do it currently or it is something you are thinking of doing in the pipeline?

    Thanks again for this. It is GREAT!

    ReplyDelete
  23. It's doable, but you would have to rewrite some bits of the code. A good place to start is here, but you need to be javascript literate to pull it off. Good luck, and be sure to blog about it if you succeed!

    ReplyDelete
  24. I'll try the calender in Portuguese language? Have someone reported any bug in this language? Do you think it can work well?

    ReplyDelete
  25. God!!
    That's a big work!
    Sounds good, but i want to keep my archives monthly, i think 3 years and a lot of posts may impact page load performance.

    Anyway, great job!

    Saludos
    Mario

    ReplyDelete
  26. hi Johan,
    i did as you instructed but it seems i missed something because my calendar looks like this: http://tempbura.blogspot.com/
    please tell me my mistake. thank you!

    ReplyDelete
  27. Is there any chance that this can be made to work with weekly archiving?

    ReplyDelete
  28. In theory: yes, as long as you get the code to link appropriate dates to the first anchor on the week archive page for that week, for instance by bookmarking it on del.icio.us and using something similar to the code my blog uses.

    It's a bit of work though and would probably require at least moderate javascript programming skills, so if you don't figure it out on your own, you are probably out of luck.

    ReplyDelete
  29. Hmmm, I followed the tutorial on Daleclick then went to you. Everything looks to work fine, except that my calendar still does not point to the dates in which there are posts.

    Check it out at: www.ciudadelectronik.com/blog

    and BTW, I feel your pain about money, I have had the same problems before helping other people with technology (I still do). So if you have a paypal account, can send me a request via paypal, and a small amount of money is enough, hey, go for it, just let me know in my blog... after all, I feel that you have done a great job not only me but for a whole lot of other people (I coulnd't tolerate probalby helping THAT much)

    ReplyDelete
  30. It seems your template's div named "archive" does not list what dates the archive links are from (and in fact does not link to post archive pages but post permalinks), so the calendar doesn't know what dates to connect them to. You should seek out the tag for the archive list that generates links on the form 2006_08_11_archive.html and similar, with contents like 2006-08-11, name that div id="archive" and proceed, and then hopefully get some action.

    It's not really that I need economical compensation, but a (1) PayPal dollar by would be a neat motivator, even if sort of insignificant. :-) Thoughts do count. I'm not sure I know how to handle requests, but feel free to try the PayPal thingamajig on my blog top right, and it'll route you through to the standard PayPal pages.

    ReplyDelete
  31. yesssssss. I almost thought I wouldn't make it... The visa was rejected, but apparently the cheking account worked. Enough details said, I hope you can enjoy @ least a six-pack of your favorite beverage in my name!!!

    BTW, no, it was fairly easy because I have already the paypal setup!!! I wouldn't have taken more than 3 minutes if it wouldn't have been for the visa getting rejected.

    Everything was done from here in three steps. Enter amount. Login. Select Payment Method. Nice.....
    I might use paypal for something in the future (i still don't know what to do with my html/css/php/mysql/photoshop yet) but im sure i wanna do something real.

    Anyways, thanks for the fast comment (dawm that was fast) I will try and let you know...

    ReplyDelete
  32. By lucky coincidence I took a peek in the mail before going to bed. :-) Cool -- then at least the important bits with my PayPal console seem to work.

    (I ought to pack it up as a Blogger Widget, now that those are more or less easy sharing, these days. :-) Thanks, and good luck with the calendar!

    ReplyDelete
  33. F#*@!!!!

    Didn't You know Exactly what were you lookin for!!!! Yep, just like other comments, im sorry, i don't know if you this for praise (and i don't care) but man, Amazing!!!

    Thanks a whole lot. Now I'm going to imagine you eating your favorite to go combo with your favorite drink!!! And i feel so good that we helped each other mutually!! See, that's what im fu#*@& talking about!!!

    I'll be visiting you not only to ask questions...but more to say hello to a new friend... Thanks man. I don't know with what can I pay you back (I mean technology related of course)

    ReplyDelete
  34. Heyyyyy!!! (I got hooked and didn't sleep but I hope the contrary for you).

    I was thinking...
    Have you thought using mysql to store possible combinations of dates? Let's says you store in one column 12 months, another column 31 days...no
    forget about it. (just thought of leap year and daylight saving time issues).

    Well, nevermind. Retrieve the date from blogger-- you said they don't provide the tools right?. Is the date one of them i.e.: Throwing the date into a php or javascript varible?

    That could be helpful? Hmmmmm. You have more experience, is there a way to get that date into a variable automatically? that could be fun to work with (even though yes, you don't get anything else really out of it, considering all the crap to think and make- just for a stupid date format)

    ReplyDelete
  35. hey, um, can i just ask...im not a techie and iv only started tweaking my layout. well i followed the instructions and found no problem saving it, only that the calendar won't show? does this have something to do with my background? or is it i just am doing it wrong?

    ReplyDelete
  36. hey!
    I tried to include the calendar into my blog - and so far I just have added the example-js-calendar without any changes. but the problem I have now is that, well, the css-specifications of my blog seem to somehow "crash" or override the "aqua-skin" of the calendar. do you know any possibility how to "tell" my blog to not use the normal css-specifications of my blog for the part where I want to show the calendar?

    ReplyDelete
  37. It might be that it requires some additional styling to look right. Try #calendar-container { width:20em; } for instance. position:relative might be another good thing to toss in. I'm no expert on these matters either, I'm afraid, but good luck!

    ReplyDelete
  38. Hi
    sorry to bother you.your calendar is so cool.But how can i do this in new blogger.thanks.

    ReplyDelete
  39. Seems others already have; try the (gray) backlinks for this post, or asking there, if not enough to copy their recipes. There is at least one in Spanish, and another less talkative in English, so it's at least possible. The first one, as translated by google.

    ReplyDelete
  40. These comments have been invaluable to me as is this whole site. I thank you for your comment.

    ReplyDelete
  41. thanks. amazing code

    ReplyDelete

Limited HTML (such as <b>, <i>, <a>) is supported. (All comments are moderated by me amd rel=nofollow gets added to links -- to deter and weed out monetized spam.)

I would prefer not to have to do this as much as you do. Comments straying too far off the post topic often lost due to attention dilution.

Note: Only a member of this blog may post a comment.