Introduction
Having been posting to Google+, I found there was no easy way to search for my posts containing specific tags. My idea was for a listing of Google+ posts titles and tags.
Figure 1a: The design of my idea for a listing of Google+ posts titles and tags
With this interface, a user can see all the titles of my posts, but also hashtags, so it is relatively easy to follow a topic through the list.
The project consists of a simple HTML UI built and populated with JavaScript; JSON data to hold the titles and tags; in future it could be developed to use the Google+ API to obtain some of the data dynamically. [But a quick review of the Google+ API led me to believe there was no way to generate the data I required, in a simple format. So for this project, I planned first to put the data in by hand, but in format that could be reproduced by automation, from a suitable source later, hence JSON is used.]
The core task is to create a line builder that can generate the HTML for a table or a grid. Hence the main class is called TableGrid
. However, there are a couple of helper functions included.
The current project uses this class to build the UI, and styles with CCS.
The git repository is on GitHub, the following tag corresponds to this article:
Background
[You can skip this section if you are only interested in code.]
CodeProject has a lot of great articles, and it seemed that getting involved would be a good way to consolidate my knowledge. However, when doing my own articles, it seemed difficult to develop them, directly, to the point that they conform to Codeproject requirements. I needed a progression.
Occasionally, I could find a Stackoverflow question about a topic I had gathered some info on, then I could post my notes as an answer. [Although Q&A streams are often like stepping into a windtunnel.]
Then I began posting on Googleplus g+, where you can make any post trivial or complex, and re-edit as often as you like. So rather than dropping these ideas as not adequate for an article, they began to fill up g+.
I have now come full circle, because I am going to describe a little project that arose from my attempts to remain in control of my g+ posts.
Article Content
The project consists of the following files:
- HtmlTools.js - Contains a class
TableGrid
for building tables and grids and populating them from data - googlePostsList.html - This is a simple webpage to demonstrate the UI in this project
- googlePostsList.css - Accompanying CSS
- codeBehind.js - Uses the
TableGrid
class for this demo - Data.js - Contains the JSON data for this example
Although this is not a difficult project, there are several topics involved. To consider the topics separately, in this article, the front end UI and styling will be described first. Then the data requirement is discussed, followed by the class TableGrid
and its development.
UI and Styling
The final render of the HTML and CSS was shown in Figure 1a. The HTML structure is alternating div
so they can form either two columns or a stack of alternating hashtags and titles.
Figure 2a: Alternate layout, posts titles and tags in a stack, compare to Figure 1a
Listing 1a: The html for one post
<div class="list_container">
<div class="post_tags">
<span class="hashtag_button">hashtag1</span>
<span class="hashtag_button">hashtag2</span>
</div>
<div class="post_title">
<a href="https://...">
Title</a>
</div>
</div>
In the CSS to get the required layout behaviour of the div
, their display
attribute is set to inline-block
, and the location of the white space under each post in both arrangements is achieved by increasing the line-height
in the second div
.
.post_tags {
display: inline-block;
vertical-align: top;
}
.post_title {
display: inline-block;
line-height: 22px;
...
}
The look of the hashtags and titles, which are set in the CSS class hashtag_button
and post_title>a
are set to give the list items a similar look to Google+.
.hashtag_button {
background-color: #EEE;
border-color: #DDD;
...
}
.post_title>a {
color: #404040;
font-style: italic;
...
}
Notice that hashtag_button
is solely dedicated to this look, hence is separated from the container class post_tags
.
The hashtags
form a horizontal stack inside the div
class post_tags
the value of text-align
controls the stacking direction. Then in a section for responsive layout changes---where various width
settings are also defined---the behaviour of text-align
is set differently for each layout.
For full size, like Figure 1a, which are the default values.
.post_tags {
text-align: right;
width: 30%
}
For small size, like Figure 2a, which are controlled by a media rule.
@media screen and (min-width:0em) and (max-width:40em){
...
.post_tags {
text-align: left;
width: 100%;
}
...
}
Data
The example of the UI in Figure 1a shows some of the data required for each post:
In addition, the UI uses the post title as a hyperlink with the url of the post as a link.
On Google+ to follow a hashtag, it is added to the base url:
Like so: plus.google.com/explore/hashtag [NB this link is not implemented in the current example.] This does not require extra data for each post, so the JSON data for a single post has the following format.
Listing 2a: JSON data for a single
{ tagList: ["tag1", "tag2"],
title: "Title of post",
link:"https:\\ ... "
}
Data for each post is thus stored as an element in an array.
TableGrid
As outlined in the introduction, the requirement of the code behind is to build a list of HTML from rows of data. This is a common task, there are many solutions, this solution does it in the browser with JavaScript using a class dedicated to this task TableGrid
.
I have shown the specific data I want to use here, but I really want to maximize the reusability of the code I am developing, and hence data I can handle. To identify the best architecture, we'll compare a hierarchy to a simple 2D table.
Starting with the very simple, for example a list of animals that can be rendered as a list in an HTML table, each row would generate an item like this:
<tr> <td> dog </td> </tr>
Some functionality in this example can be reused in a general case.
- Iteration over rows of data
Also the table row always starts and ends with the same sequence <tr> ... </tr>
. This wrapping of the row can also be separated in more complicated structures.
- Separate the wrapping of a row, from the data formatting.
However the handling of data in this case is trivial, simply wrapping data with <td>data</td>
, but this would not generally be enough. We also want to be able to handle various types of data, from single strings, and arrays, to more complicated hierarchies, and add them to a table or other structure.
Next consider a more complicated example. Such as the previously suggested use of JSON data allowing hierarchical data sets. Then the problem is to find a pattern that satisfies both the simple example and the complex example. In addition, we need to be able to build more complicated structures than the simple table. I chose to leave the developer to define a function that would receive the data row as a variable, then any structure could be built.
- Customizable function to convert data row to formatted output.
The pattern derived from the above requirements is shown in Figure 3a.
Figure 3a: TableGrid class and usage pattern
To demonstrate this, we will continue with the example. From Listing 1a we can identify each row is prefixed by <div class="list_container">
and suffixed by </div>
. Then the inner data-containing elements can be built from the JSON data in Listing 2a in a custom line builder function, which is called while iterating through the rows.
function myLineBuilder(data){
s = '\n\t<div class="post_tags">\n\t\t'
s += HtmlBuilder_wrapArray(data.tagList,
' <span class="hashtag_button">', '</span>')
s += '\n\t</div>'
s += '\n\t<div class="post_title">'
s += '\n\t\t<a href="'+data.link+'/">'
s += '\n\t\t'+data.title+'</a>'
s += '\n\t</div>'
return s
}
Note that HtmlBuilder_wrapArray
function is a helper function contained in HtmlTools.js and is used here to generate span
tags for the hashtags
array data.tagList
.
Now we can plug these into TableGrid
:
tg = new TableGrid()
tg.setLineBuilder( myLineBuilder,
'\n<div class="list_container">', '\n</div>')
Then do the build and ask for the result:
tg.buildRows(myPostList)
htmlString = tg.getBlock()
Where myPostList
is the array of rows of data for each post.
The code implementing TableGrid
is:
function TableGrid(){
this.linebuilder
this.linePrefix
this.lineSuffix
this.block=""
this.setLineBuilder=function(lb,pre,suf){
this.linebuilder=lb
this.linePrefix=pre
this.lineSuffix=suf
this.block = ""
}
this.buildRows=function(table){
for(var i=0; i<table.length; i++){
tg.buildLine(table[i])
}
}
this.buildLine=function(data){
s = this.linePrefix
s += this.linebuilder(data)
s += this.lineSuffix
this.block += s
}
this.getBlock=function(){ return this.block; }
}
Review of Usage
- Build a data structure
- Design a HTML view of the data
- Deconstruct the HTML structure, into line wrapper, line content, and table wrapper
- Write the line builder
- initialize
TableGrid
- Once this is done, the data can be sent to
TableGrid.buildRows
The instance of the TableGrid
itself can be reused by sending a new data set.
Obviously, for building simple tables, this has lead to a relatively complicated method. To maintain simplicity, a creator function is available for simple tables. This has the additional benefit that these predefined instances of the class can also be used for testing. This is not covered further in this article but is in the uploaded project.
Summary
This project covered small amounts of HTML, JavaScript, JSON, CCS.
Thanks for reading, please leave a comment.
History