Larry Steinle

December 4, 2011

Create Scrollable Table with CSS and jQuery

Filed under: Web — Larry Steinle @ 10:50 am
Tags: , , ,

In the post, CSS Table Scrollbar with Fixed Headers, we explored a fairly simply way to create tables with scrollbars using CSS. Today we take it up a notch using jQuery to eliminate several limitations introduced with the pure CSS approach.

Ideally our scrollable table should behave just like any ordinary table. The size of the cells can have a fixed width or a variable width based on the content. The cells should be able to support full word breaks instead of wrapping in the middle of a word. Finally, the table should be capable of resizing with the window when percentage widths are used.

With the help of JavaScript and jQuery we will create a scrollable table that behaves more like a normal table.

To see the logic in action look at the jsFiddle Demo.

Challenges

Resizing Table Widths Based on Percentage

The DOM always returns the width of an element in pixels. On one hand this is good because we always know the exact width. However, there are times we need to know the original value used. Is the table a fixed width or is it a variable width based on a percentage? If we don’t have access to the original value how do we know when to resize the table with the window?

Complex Column Width Calculations

Column widths get very complicated quickly. Many factors are taken into account when determining the width of a column. What is the width assigned to the column in pixels or percentage? What content is contained in the column and how much does that content allow the column to shrink/grow? Is there a minimum width or a maximum width assigned to the cell? Is the content in the cell allowed to wrap?  Are the cells merged together? These are just a few of the concerns that any solution intending to mimic a normal table will have to contend with.

Relax! It’s Not THAT Hard!

While these different challenges may seem daunting they are truly fairly simple to address.

To address the resizing topic assign the width to the host div tag and not directly to the table. This way the table can always be set to use the entire 100% width of it’s container.

As for the column width calculations don’t bother with them! Allow the browser to do the heavy lifting for you. Before setting the size of the columns disable absolute positioning so the correct widths can be retrieved from the browser itself!

Creating Test HTML

Once again we’ll create a simple div tag with a vScroll class name to host our standards formatted HTML table. In today’s table the header will contain two rows with one row utilizing spanning (colspan and rowspan) to demonstrate that our solution can support complex column width calculations.

We will also include a standard table to demonstrate how close we are able to replicate the standard layout in our scrollbar table.

As a reminder notice that the width for the standard table is assigned to the table itself while for the scrollable table the width is assigned to the hosting div tag.

<h1>Scrollable Table</h1>
<div class="vScroll" style="height:286px;width:90%;">
<table>
<thead>
<tr>
<th>Heading 1</th>
<th colspan="2">Heading 2</th>
<th rowspan="2">Heading 4</th>
</tr>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Footer 1</th>
<th>Footer 2</th>
<th>Footer 3</th>
<th>Footer 4</th>
</tr>
</tfoot>
<tbody>
<tr><td width="200px">Data 1</td><td>Data 2 lorem epsim bo Data 2 lorem epsim bo Data 2 lorem epsim bo Data 2 lorem epsim bo satism</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
</tbody>
</table>
</div>

<h1>Standard Table</h2>
<table style="width:90%;">
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<th>Heading 4</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Footer 1</th>
<th>Footer 2</th>
<th>Footer 3</th>
<th>Footer 4</th>
</tr>
</tfoot>
<tbody>
<tr><td width="200px">Data 1</td><td>Data 2 lorem epsim bo Data 2 lorem epsim bo Data 2 lorem epsim bo Data 2 lorem epsim bo satism</td><td>Data 3</td><td>Data 4</td></tr>
<tr><td>Data 1</td><td>Data 2</td><td>Data 3</td><td>Data 4</td></tr>
</tbody>
</table>

In the header of our page we will add the CSS from the previous post. A few styles will be taken out of the CSS as these will be managed in the jQuery in this solution:

<style type="text/css">
/* Standardize Table Styling Across Browsers for Both Standard and Scrollable Tables */
table {border:none;border-collapse:collapse;line-height:18px;margin-right:1px;table-layout:fixed;}
th, td {margin:0px;padding:0px;}

/* Little Bit of Custom Styling for Flare */
th {background-color:#E9E9E9;border:solid 1px silver;}
td {border:solid 1px silver;}

/* Enable Scroll Styling Effect */
.vScroll {display:run-in;overflow-x:none;overflow-y:scroll;}

/* Fix Positioning Issue in IE 8 (and Earlier) and Mozilla */
.vScroll {border-left:solid 1px silver;}
.vScroll td:first-child {border-left:none;}
.vScroll thead, .vScroll tfoot {margin-left:-1px;}

/* Standardize Scrollbar in Safari to Be Same Width as Chrome, IE and Mozilla. */
::-webkit-scrollbar {width:16px;}
::-webkit-scrollbar-track {border-radius:10px;-webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.3);}
::-webkit-scrollbar-thumb {background-color:silver;border-radius:10px;-webkit-box-shadow: inset 0 0 3px rgba{0,0,0,0.5);}
</style>

Now we are ready to begin!

jQuery to Calculate Column Widths

After we have used jQuery to find the scrollable tables we’ll create a few variables to keep track of the tags that will be used often:

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");
});
};
</script>

Before we can accurately set the width of the cells we need to know the width of the table. Unfortunately the table width won’t be completely accurate for our calculations so we’ll need to use one size for the cell widths and another size for the final solution. This is a result of the complexity added by our CSS to support IE 8 and Mozilla left bordering.

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);
//RESIZE CELLS HERE
$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);
});
};
</script>

Another factor that needs to be addressed is that any width assigned to a cell may cause the table width to change. So we also need to take the time to remove widths from each cell reseting them to the default width value of auto.

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Remove Cell Width Formatting
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);
//RESIZE CELLS HERE
$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);
});
};
</script>

Now we are ready to set the width of the cells! To allow the browser to make all the width calculations for us we will first disable absolute positioning on the header and footer. Then we will assign the browser calculated width values directly to each cell in the header and footer. To make the system more efficient we will assign the width values to the cells in the first row of the body only because we have no idea how many body rows there will be and it is unnecessary to assign it to every single body row.

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Remove Cell Width Formatting
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);

//Disable positioning so browser can do all the hard work.
 //This allows us to support min-width, max-width, nowrap, etc.
 $Head.css("position", "relative");
 $Foot.css("position", "relative");

 //Navigate thru each cell hard coding the width so when the association
 //is broken all of the columns will continue to align based on normal
 //table rules. Only traverse the first row cells in the body for efficiency.
 $body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
 $Head.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
 $Foot.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });

//Enable positioning for fixed header positioning.
 $Head.css("position", "absolute");
 $Foot.css("position", "absolute");

$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);
});
};
</script>
Now all that is left is to reposition the header and the footer.
<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Remove Cell Width Formatting
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);

//Disable positioning so browser can do all the hard work.
//This allows us to support min-width, max-width, nowrap, etc.
$Head.css("position", "relative");
$Foot.css("position", "relative");

//Navigate thru each cell hard coding the width so when the association
//is broken all of the columns will continue to align based on normal
//table rules. Only traverse the first row cells in the body for efficiency.
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });

//Enable positioning for fixed header positioning.
$Head.css("position", "absolute");
$Foot.css("position", "absolute");

$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);

//Position Heading Based on Height of Heading
$Scroll.css("margin-top", ($Head.height() + 1) + "px");
$Head.css("margin-top", (($Head.height() - 1) * -1) + "px");

//Position Footer Based on Height of Scroll Host
$Scroll.css("margin-bottom", $Foot.css("height"));
$Foot.css("margin-top", $Scroll.height() - 1 + "px");
});
};
</script>

There are two events that we need to attach our jQuery code: Document Ready and Window Resize. Document Ready event to initialize the table sizing and the Window Resize event to maintain the correct table sizing.

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Remove Cell Width Formatting
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);

//Disable positioning so browser can do all the hard work.
//This allows us to support min-width, max-width, nowrap, etc.
$Head.css("position", "relative");
$Foot.css("position", "relative");

//Navigate thru each cell hard coding the width so when the association
//is broken all of the columns will continue to align based on normal
//table rules. Only traverse the first row cells in the body for efficiency.
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", $(c).width()); });

//Enable positioning for fixed header positioning.
$Head.css("position", "absolute");
$Foot.css("position", "absolute");

$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);

//Position Heading Based on Height of Heading
$Scroll.css("margin-top", ($Head.height() + 1) + "px");
$Head.css("margin-top", (($Head.height() - 1) * -1) + "px");

//Position Footer Based on Height of Scroll Host
$Scroll.css("margin-bottom", $Foot.css("height"));
$Foot.css("margin-top", $Scroll.height() - 1 + "px");
});
};

$(document).ready(function() { ReSizeScrollTables(); });
$(window).resize(function() { ReSizeScrollTables(); });
</script>

There is one last issue that needs to be addressed. Because we reset the width on all our cells to auto we are unable to set a defined width for the cell. This can be addressed as long as the width attribute is assigned to the tag. If the width is assigned with css style then we are unable to access the original value and therefore unable to manage the width of the cell.

<script src="jquery-1.7.min.js" type="text/javascript"></script>
<script type="text/javascript">
function ReSizeScrollTables () {
$(".vScroll").each(function (i,c) {
var ScrollBarWidth = 16; //IE, Chrome, Mozilla & Opera use Size 16 by default
var $Scroll = $(c);
var $Table = $Scroll.find("table");
var $Head = $Scroll.find("thead");
var $Foot = $Scroll.find("tfoot");
var $Body = $Scroll.find("tbody");

//Remove Cell Width Formatting
$Body.first("tr").find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Head.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });
$Foot.find("th, td").each(function (i, c) { $(c).css("width", "auto"); });

//Set Width of Table, Header, Footer and Body Elements
$Table.css("width", $Scroll.width() - ScrollBarWidth + 2);

//Disable positioning so browser can do all the hard work.
//This allows us to support min-width, max-width, nowrap, etc.
$Head.css("position", "relative");
$Foot.css("position", "relative");

//Navigate thru each cell hard coding the width so when the association
//is broken all of the columns will continue to align based on normal
//table rules. Only traverse the first row cells in the body for efficiency.
        $Body.first(&quotetr&quote).find(&quoteth, td&quote).each(function (i, c) {
            var $c = $(c);
            $c.css(&quotestyle&quote, GetStyle(c));
            $c.css("width", $c.width());
        });
        $Head.find(&quoteth, td&quote).each(function (i, c) {
            var $c = $(c);
            $c.css(&quotestyle&quote, GetStyle(c));
            $c.css("width", $c.width());
        });
        $Foot.find(&quoteth, td&quote).each(function (i, c) {
            var $c = $(c);
            $c.css(&quotestyle&quote, GetStyle(c));
            $c.css("width", $c.width());
        });

//Enable positioning for fixed header positioning.
$Head.css("position", "absolute");
$Foot.css("position", "absolute");

$Table.css("width", $Scroll.width() - ScrollBarWidth - 3);

//Position Heading Based on Height of Heading
$Scroll.css("margin-top", ($Head.height() + 1) + "px");
$Head.css("margin-top", (($Head.height() - 1) * -1) + "px");

//Position Footer Based on Height of Scroll Host
$Scroll.css("margin-bottom", $Foot.css("height"));
$Foot.css("margin-top", $Scroll.height() - 1 + "px");
});
};

function GetStyle(c) {
    var $c = $(c);
    var cellStyle = $c.attr("style");
    return ((cellStyle != null) && (cellStyle.length > 0)) ? cellStyle : $c.css("style");
};

$(document).ready(function() { ReSizeScrollTables(); });
$(window).resize(function() { ReSizeScrollTables(); });
</script>

Conclusion

Now we have a fluid, scrollable table that looks professional and behaves like a standard table when the page is resized.

Advertisement

3 Comments »

  1. Was originally excited by your first demo. Unfortunately I didn’t realize that the jquery version is flexible but doesn’t provide Fixed headers. I need both.

    Comment by Laura Aspen — December 8, 2011 @ 4:29 am | Reply

    • Actually the jQuery version can support fixed headers with slight modification. But the width has to be assigned directly to the tag using the width attribute. That’s because we don’t know from the CSS when the width is fixed verses calculated.

      Comment by Larry Steinle — December 8, 2011 @ 10:12 am | Reply

  2. Um, unfortunately the JQuery code doesn’t work as posted. If I replace the $body, $header, and $footer variables with $Body, $Head, $Foot, the next to last code block will work (although the columns do not line up exactly in FF 10.0.3, but do in Chromium). The GetWidth() version doesn’t work in FF, even with the variable corrections.

    Comment by Joel White — April 30, 2012 @ 3:23 pm | Reply


RSS feed for comments on this post. TrackBack URI

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 )

Facebook photo

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

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: