SVG Maker Project - Month Calendar

This project details how to create a month calendar with Scalable Vector Graphics (SVG) which will use the Javascript Date() object functions (methods) to update the calendar to show the current month.

The image to the left shows a typical calendar format where there are only 5 weeks needed. Of course, some months will require a 6th row when their first day starts later in the week. Let's take a look at the SVG code needed to display this image.

The code shows an SVG image using 100% of the width and height of the container the image is placed in and telling the browser to use (0,0) and (800,500) as the coordinates of the container. After some style definitions a grouping with class="calendar" contains the calendar shown.

The <rect> that follows creates the elliptical top that holds the text of the month and year, which is the next line of code - a <text> element. The <text> element has 2 class definitons, so styles from both defintions will be applied. Note that when more than one class is used, it is separated by a space, not a semi-colon like the style properties are.

The next grouping uses the translate transformation to offset all elements contained in the grouping relative to (15,60). The <rect> element that follows draws the large box to contain the calendar. It is 440 pixels high, so it uses the remainder of our viewBox (440+60=500). Next a horizontal line is drawn using the <path> element that is transformed 40 pixels down. It is the line under the text showing the days of the week. That leaves us with 400 pixels to draw the lines separating the weeks of the calendar.

There are 2 groups defined next, one for five weeks (id="FiveRows") and one for six weeks (id="SixRows"), but the six week grouping will be hidden since its class definition is hide and the hide style sets the display property to none - so the browser ignores it. Javascript will change the class attribute as needed as we will see when we add the code to the image.

The next set of <path> elements draw the vertical lines separating the days of the week. And lastly, a grouping of <text> elements display the days of the week.

Basic Calendar Outline

<svg width="100%" height="100%" viewBox="0 0 800 500"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<style type="text/css"><![CDATA[
.calendar {font-family:Arial;}
.border {fill:white;stroke:navy;stroke-width:2;}
.month {fill:navy;font-size:40px;font-weight:bold;}
.wd {fill:black;stroke:none;font-size:25px;font-weight:bold;}
.center {text-anchor:middle;}
.show {display:initial;}
.hide {display:none;}
]]>
</style>

<g class="calendar">
<rect x="15" y="5" width="770" height="55" rx="400" ry="28" class="border" />
<text id="month" x="400" y="45" class="month center">September, 2015</text>

<g transform="translate(15,60)" class="border">
<rect x="0" y="0" width="770" height="440" />
<path d="M0,0L770,0" transform="translate(0,40)" />

<g id="FiveRows" class="show">
<path d="M0,0L770,0" transform="translate(0,120)" />
<path d="M0,0L770,0" transform="translate(0,200)" />
<path d="M0,0L770,0" transform="translate(0,280)" />
<path d="M0,0L770,0" transform="translate(0,360)" />
</g>

<g id="SixRows" class="hide">
<path d="M0,0L770,0" transform="translate(0,106.6)" />
<path d="M0,0L770,0" transform="translate(0,173.2)" />
<path d="M0,0L770,0" transform="translate(0,239.8)" />
<path d="M0,0L770,0" transform="translate(0,306.4)" />
<path d="M0,0L770,0" transform="translate(0,373)" />
</g>

<path d="M0,0L0,440" transform="translate(110,0)" />
<path d="M0,0L0,440" transform="translate(220,0)" />
<path d="M0,0L0,440" transform="translate(330,0)" />
<path d="M0,0L0,440" transform="translate(440,0)" />
<path d="M0,0L0,440" transform="translate(550,0)" />
<path d="M0,0L0,440" transform="translate(660,0)" />
</g>

<g transform="translate(70,88)" class="wd center">
<text x="0" y="0" transform="translate(0,0)">Sun</text>
<text x="0" y="0" transform="translate(110,0)">Mon</text>
<text x="0" y="0" transform="translate(220,0)">Tue</text>
<text x="0" y="0" transform="translate(330,0)">Wed</text>
<text x="0" y="0" transform="translate(440,0)">Thu</text>
<text x="0" y="0" transform="translate(550,0)">Fri</text>
<text x="0" y="0" transform="translate(660,0)">Sat</text>
</g>

</g>

</svg>

Adding Dates

Adding dates to the image is somewhat painful because each box in the calendar needs a text element to hold the date. For a 5 row calendar that is 5x7=35 text elements and 6x7=42 text elements for a 6 row calendar. But, thanks to copy and paste it can be done much more quickly than if we had to type each one. Once it is set up, the tedious part is done.

The code below gets added to the FiveRows grouping. Similar code is added to the SixRows grouping, but is not shown at this time. It will be included in the final code listing at the end.

A couple of things worth noting. Each row is placed in a group which is transformed with just the y coordinate changing. Also each group has identical <text> elements (except for the text itself). In the final code listing the text elements will contain a space character since the javascript will be filling it in. The space character gives the text element a child node so that the javascript can access the firstChild property of the DOM when the javascript updates it.

Also, each <text> element has a unique id with a "-5" suffix. The SixRow grouping will contain similar code with a "-6" suffix. The significance of that will be obvious when the javascript gets added.

It might look like a lot of code, but with copy and paste, it is created very quickly.

<g class="wd">
<g transform="translate(5,65)">
<text id="B1-5" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B2-5" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B3-5" x="0" y="0" transform="translate(220,0)">1</text>
<text id="B4-5" x="0" y="0" transform="translate(330,0)">2</text>
<text id="B5-5" x="0" y="0" transform="translate(440,0)">3</text>
<text id="B6-5" x="0" y="0" transform="translate(550,0)">4</text>
<text id="B7-5" x="0" y="0" transform="translate(660,0)">5</text>
</g>
<g transform="translate(5,145)">
<text id="B8-5" x="0" y="0" transform="translate(0,0)">6</text>
<text id="B9-5" x="0" y="0" transform="translate(110,0)">7</text>
<text id="B10-5" x="0" y="0" transform="translate(220,0)">8</text>
<text id="B11-5" x="0" y="0" transform="translate(330,0)">9</text>
<text id="B12-5" x="0" y="0" transform="translate(440,0)">10</text>
<text id="B13-5" x="0" y="0" transform="translate(550,0)">11</text>
<text id="B14-5" x="0" y="0" transform="translate(660,0)">12</text>
</g>
<g transform="translate(5,225)">
<text id="B15-5" x="0" y="0" transform="translate(0,0)">13</text>
<text id="B16-5" x="0" y="0" transform="translate(110,0)">14</text>
<text id="B17-5" x="0" y="0" transform="translate(220,0)">15</text>
<text id="B18-5" x="0" y="0" transform="translate(330,0)">16</text>
<text id="B19-5" x="0" y="0" transform="translate(440,0)">17</text>
<text id="B20-5" x="0" y="0" transform="translate(550,0)">18</text>
<text id="B21-5" x="0" y="0" transform="translate(660,0)">19</text>
</g>
<g transform="translate(5,305)">
<text id="B22-5" x="0" y="0" transform="translate(0,0)">20</text>
<text id="B23-5" x="0" y="0" transform="translate(110,0)">21</text>
<text id="B24-5" x="0" y="0" transform="translate(220,0)">22</text>
<text id="B25-5" x="0" y="0" transform="translate(330,0)">23</text>
<text id="B26-5" x="0" y="0" transform="translate(440,0)">24</text>
<text id="B27-5" x="0" y="0" transform="translate(550,0)">25</text>
<text id="B28-5" x="0" y="0" transform="translate(660,0)">26</text>
</g>
<g transform="translate(5,385)">
<text id="B29-5" x="0" y="0" transform="translate(0,0)">27</text>
<text id="B30-5" x="0" y="0" transform="translate(110,0)">28</text>
<text id="B31-5" x="0" y="0" transform="translate(220,0)">29</text>
<text id="B32-5" x="0" y="0" transform="translate(330,0)">30</text>
<text id="B33-5" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B34-5" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B35-5" x="0" y="0" transform="translate(660,0)"> </text>
</g>
</g>

Adding Script

Sun Mon Tue Wed Thu Fri Sat

The script shown below sets a few variables, Months is an array of month names, Days is an array of the number of days in each month, and suffix defaults to "-5", the suffix of the text ids for a FiveRow calendar. Then the event listener will call the generateCalendar() function once the image loads.

The function generateCalendar() gets a new Date object, saves the current day in a variable, changes the day to the 1st of the month to get the day of the week of the first day of the month, then sets the day back to today.

After setting month and year variables with the month and 4 digit year, the code does a leap year check and sets the second element in the Days array to 29 if it is a leap year. Fun fact: centuries that are divisible by 400 are leap years, but centuries that are not, are not leap years. Note that the "%" operator is a modulus or remainder operator and "||" is the operator for a "logic OR" and "&&" is the operator for a "logic AND". So that line of code reads, "If the remainder of the year divided by 400 is equal to zero, OR if the remainder of the year divided by 100 is not equal to zero AND the remainder of the year divided by 4 is equal to zero, the set the Days array second entry equal to 29" - the array index is zero relative, so 1 is the second entry and 0 is the first.

Next the code gets the <text> element (node) with id="month", and sets its firstChild nodeValue property to the Months array for the month plus a space plus the year. This put the month and year on the top of the calendar.

The day of the week for the first day of the month is an offset into the calendar. Since the FiveRow calendar has 5x7=35 boxes, if the day of the week offset plus the number of days in the current month exceed 35, we will need to make a SixRow calendar. That is what the code is doing next, setting the class attributes of the FiveRow and SixRow groupings to show or hide to display the correct one. It also sets the suffix variable, which is use later.

Lastly, the code enters a for loop, looping for the number of days in the current month. Each loop it updates a <text> elements with the day of the month. It builds the id of the <text> element by starting with "B", then adding the for loop index plus 1 plus the day of the week offset, then adds the suffix for the number of rows needed for this months calendar. Within the for loop, it also checks the for loop index plus 1 to see if this element is today's date. If so, it sets the style attribute for the day to "fill:red", so today stands out on the calendar.

<script type="application/x-javascript"><![CDATA[

var Months = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var Days = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
var suffix = "-5";

window.addEventListener("load", generateCalendar);
function generateCalendar()
{
var i;
var dt = new Date();

// Get Day of Week for the first day of the month
var day = dt.getDate();
dt.setDate(1);
var dow = dt.getDay();
dt.setDate(day);

var month = dt.getMonth();
var year = dt.getFullYear();

// Leap Year Check
if(year%400==0 || (year%100!=0 && year%4==0)) Days[1]=29;

var node = document.getElementById("month");
node.firstChild.nodeValue = Months[month]+" "+year;

if((dow+Days[month])>35)
{
node = document.getElementById("FiveRows");
node.setAttribute("class","hide");
node = document.getElementById("SixRows");
node.setAttribute("class","show");
suffix = "-6";
}
else
{
node = document.getElementById("FiveRows");
node.setAttribute("class","show");
node = document.getElementById("SixRows");
node.setAttribute("class","hide");
suffix = "-5";
}

// Put the day in the correct calendar box
for(i=0;i<Days[month];i++)
{
node = document.getElementById("B"+(i+1+dow)+suffix);
node.firstChild.nodeValue = i+1;

//set attribute red for today's date
if((i+1)==dt.getDate())
{
node.setAttribute("style","fill:red");
}
}
}
]]>
</script>

Entire Code for the Calendar

Copy and paste the code below into your text editor, save the file with a .svg extension, and open it in a browser. Edit the file, then refresh the browser to see the effect of your change. Note the viewBox is changed slightly - from 500 to 505 to give a small margin at the bottom.

Try changing the line of code in the script var dt = new Date(); to var dt = new Date("01/01/2016");. Then try to add a clickable point to move the calendar forward and backward, i.e. draw a small arrow on each end of the calendar title and add an event listener element for a "click" event. Now you can check any date in time. What day of the week were you born on?

<svg width="100%" height="100%" viewBox="0 0 800 505"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<script type="application/x-javascript"><![CDATA[

var Months = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var Days = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
var suffix = "-5";

window.addEventListener("load", generateCalendar);
function generateCalendar()
{
var i;
var dt = new Date();

// Get Day of Week for the first day of the month
var day = dt.getDate();
dt.setDate(1);
var dow = dt.getDay();
dt.setDate(day);

var month = dt.getMonth();
var year = dt.getFullYear();

// Leap Year Check
if(year%400==0 || (year%100!=0 && year%4==0)) Days[1]=29;

var node = document.getElementById("month");
node.firstChild.nodeValue = Months[month]+" "+year;

if((dow+Days[month])>35)
{
node = document.getElementById("FiveRows");
node.setAttribute("class","hide");
node = document.getElementById("SixRows");
node.setAttribute("class","show");
suffix = "-6";
}
else
{
node = document.getElementById("FiveRows");
node.setAttribute("class","show");
node = document.getElementById("SixRows");
node.setAttribute("class","hide");
suffix = "-5";
}

// Put the day in the correct calendar box
for(i=0;i<Days[month];i++)
{
node = document.getElementById("B"+(i+1+dow)+suffix);
node.firstChild.nodeValue = i+1;

//set attribute red for today's date
if((i+1)==dt.getDate())
{
node.setAttribute("style","fill:red");
}
}
}
]]>
</script>

<style type="text/css"><![CDATA[
.calendar {font-family:Arial;}
.border {fill:white;stroke:navy;stroke-width:2;}
.month {fill:navy;font-size:40px;font-weight:bold;}
.wd {fill:black;stroke:none;font-size:25px;font-weight:bold;}
.center {text-anchor:middle;}
.show {display:initial;}
.hide {display:none;}
]]>
</style>

<g class="calendar">
<rect x="15" y="5" width="770" height="55" rx="400" ry="28" class="border" />
<text id="month" x="400" y="45" class="month center">September, 2015</text>

<g transform="translate(15,60)" class="border">
<rect x="0" y="0" width="770" height="440" />
<path d="M0,0L770,0" transform="translate(0,40)" />

<g id="FiveRows" class="show">
<path d="M0,0L770,0" transform="translate(0,120)" />
<path d="M0,0L770,0" transform="translate(0,200)" />
<path d="M0,0L770,0" transform="translate(0,280)" />
<path d="M0,0L770,0" transform="translate(0,360)" />

<g class="wd">
<g transform="translate(5,65)">
<text id="B1-5" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B2-5" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B3-5" x="0" y="0" transform="translate(220,0)">1</text>
<text id="B4-5" x="0" y="0" transform="translate(330,0)">2</text>
<text id="B5-5" x="0" y="0" transform="translate(440,0)">3</text>
<text id="B6-5" x="0" y="0" transform="translate(550,0)">4</text>
<text id="B7-5" x="0" y="0" transform="translate(660,0)">5</text>
</g>
<g transform="translate(5,145)">
<text id="B8-5" x="0" y="0" transform="translate(0,0)">6</text>
<text id="B9-5" x="0" y="0" transform="translate(110,0)">7</text>
<text id="B10-5" x="0" y="0" transform="translate(220,0)">8</text>
<text id="B11-5" x="0" y="0" transform="translate(330,0)">9</text>
<text id="B12-5" x="0" y="0" transform="translate(440,0)">10</text>
<text id="B13-5" x="0" y="0" transform="translate(550,0)">11</text>
<text id="B14-5" x="0" y="0" transform="translate(660,0)">12</text>
</g>
<g transform="translate(5,225)">
<text id="B15-5" x="0" y="0" transform="translate(0,0)">13</text>
<text id="B16-5" x="0" y="0" transform="translate(110,0)">14</text>
<text id="B17-5" x="0" y="0" transform="translate(220,0)">15</text>
<text id="B18-5" x="0" y="0" transform="translate(330,0)">16</text>
<text id="B19-5" x="0" y="0" transform="translate(440,0)">17</text>
<text id="B20-5" x="0" y="0" transform="translate(550,0)">18</text>
<text id="B21-5" x="0" y="0" transform="translate(660,0)">19</text>
</g>
<g transform="translate(5,305)">
<text id="B22-5" x="0" y="0" transform="translate(0,0)">20</text>
<text id="B23-5" x="0" y="0" transform="translate(110,0)">21</text>
<text id="B24-5" x="0" y="0" transform="translate(220,0)">22</text>
<text id="B25-5" x="0" y="0" transform="translate(330,0)">23</text>
<text id="B26-5" x="0" y="0" transform="translate(440,0)">24</text>
<text id="B27-5" x="0" y="0" transform="translate(550,0)">25</text>
<text id="B28-5" x="0" y="0" transform="translate(660,0)">26</text>
</g>
<g transform="translate(5,385)">
<text id="B29-5" x="0" y="0" transform="translate(0,0)">27</text>
<text id="B30-5" x="0" y="0" transform="translate(110,0)">28</text>
<text id="B31-5" x="0" y="0" transform="translate(220,0)">29</text>
<text id="B32-5" x="0" y="0" transform="translate(330,0)">30</text>
<text id="B33-5" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B34-5" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B35-5" x="0" y="0" transform="translate(660,0)"> </text>
</g>
</g>
</g>

<g id="SixRows" class="hide">
<path d="M0,0L770,0" transform="translate(0,106.6)" />
<path d="M0,0L770,0" transform="translate(0,173.2)" />
<path d="M0,0L770,0" transform="translate(0,239.8)" />
<path d="M0,0L770,0" transform="translate(0,306.4)" />
<path d="M0,0L770,0" transform="translate(0,373)" />

<g class="wd">
<g transform="translate(5,65)">
<text id="B1-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B2-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B3-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B4-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B5-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B6-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B7-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
<g transform="translate(5,131.6)">
<text id="B8-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B9-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B10-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B11-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B12-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B13-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B14-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
<g transform="translate(5,198.2)">
<text id="B15-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B16-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B17-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B18-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B19-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B20-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B21-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
<g transform="translate(5,264.8)">
<text id="B22-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B23-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B24-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B25-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B26-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B27-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B28-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
<g transform="translate(5,331.4)">
<text id="B29-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B30-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B31-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B32-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B33-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B34-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B35-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
<g transform="translate(5,398)">
<text id="B36-6" x="0" y="0" transform="translate(0,0)"> </text>
<text id="B37-6" x="0" y="0" transform="translate(110,0)"> </text>
<text id="B38-6" x="0" y="0" transform="translate(220,0)"> </text>
<text id="B39-6" x="0" y="0" transform="translate(330,0)"> </text>
<text id="B40-6" x="0" y="0" transform="translate(440,0)"> </text>
<text id="B41-6" x="0" y="0" transform="translate(550,0)"> </text>
<text id="B42-6" x="0" y="0" transform="translate(660,0)"> </text>
</g>
</g>
</g>

<path d="M0,0L0,440" transform="translate(110,0)" />
<path d="M0,0L0,440" transform="translate(220,0)" />
<path d="M0,0L0,440" transform="translate(330,0)" />
<path d="M0,0L0,440" transform="translate(440,0)" />
<path d="M0,0L0,440" transform="translate(550,0)" />
<path d="M0,0L0,440" transform="translate(660,0)" />
</g>

<g transform="translate(70,88)" class="wd center">
<text x="0" y="0" transform="translate(0,0)">Sun</text>
<text x="0" y="0" transform="translate(110,0)">Mon</text>
<text x="0" y="0" transform="translate(220,0)">Tue</text>
<text x="0" y="0" transform="translate(330,0)">Wed</text>
<text x="0" y="0" transform="translate(440,0)">Thu</text>
<text x="0" y="0" transform="translate(550,0)">Fri</text>
<text x="0" y="0" transform="translate(660,0)">Sat</text>
</g>

</g>
</svg>

SVG Maker Projects - coding SVG images - at STEAMcoded.org

STEAMcoded.org STEAMcoded.org