Navigation:

Archives:

October 2014
M T W T F S S
« Dec    
 12345
6789101112
13141516171819
20212223242526
2728293031  

Thoughts on Web Page Layout

The underlying philosophy of the World Wide Web has been that mark-up is about content and the it is the browsers job to determine how to layout the content within the constraints of the user’s device. This, of course, ran headlong into the very human desire to want to control how our stuff looks when it is displayed to our readers.

The first attempt to resolve this conflict was frames which were problematical in many ways which I won’t get into here.

Web authors naturally turned to the next most obvious solution – to hijack tables and use transparent spacer images as layout tools. Although much maligned by purists (and properly so), the use of tables has been the most widely used layout methodology because of their ease of use and despite their many problems.

There are several efforts afoot to come up with a standard for simplifying CSS based layout. These include the Flexible Box Layout Module and the CSS Template Layout Module. The Flexible Box Layout Module seems “more normal” from a syntax perspective and seems to have the greatest industry support while the CSS Template Layout Module, while weirder, has some, IMHO, significant benefits, much more sketchy support, and because it currently relies on a JavaScript simulation – at tleast the potential to work in Internet Explorer.

Below, I will present my attempts (code and resulting screen shots) using both methods as I try to implement a layout that looks much like the layout of this blog without beings such a total crufty hack. Then, I will propose and try to implement a layout methodology that combines the best of both solutions.

Flexible Box Layout Module

I started with the Flexible Box Layout Module by adapting the code from How-To: Full Page CSS 3 Layout (Desktop) to present something that looks like my business cards layout.

Here’s the CSS …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
html,body {
  margin:             0;
  padding:            0;
}

#app {
  height:             100%;
  width:              100%;
  display:            -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-box-align:  stretch;
  display:            -moz-box;
  -moz-box-orient:    vertical;
  -moz-box-align:     stretch;
  display:            box;
  box-orient:         vertical;
  box-align:          stretch;
}

.content {
  margin:             10px;
}

#header {
  min-height:         50px;
  width:              100%;
  display:            -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align:  stretch;
  -webkit-box-flex:   0;
  display:            -moz-box;
  -moz-box-orient:    horizontal;
  -moz-box-align:     stretch;
  -moz-box-flex:      0;
  display:            box;
  box-orient:         horizontal;
  box-align:          stretch;
  box-flex:           0;
}

.hs {
  position:           relative;
  margin-left:        30px;
  margin-right:       30px;
  height:             20px;
  background-image:   url(blue.jpg);
}

.icn {
  position:           absolute;
  left:               170px;
}    

#main {
  width:              100%;
  bottom:             30px;
  display:            -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align:  stretch;
  -webkit-box-flex:   1;
  display:            -moz-box;
  -moz-box-orient:    horizontal;
  -moz-box-align:     stretch;
  -moz-box-flex:      1;
  display:            box;
  box-orient:         horizontal;
  box-align:          stretch;
  box-flex:           1;
}

#footer {
  min-height:         30px;
  width:              100%;
  display:            -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align:  stretch;
  -webkit-box-flex:   0;
  display:            -moz-box;
  -moz-box-orient:    horizontal;
  -moz-box-align:     stretch;
  -moz-box-flex:      0;
  display:            box;
  box-orient:         horizontal;
  box-align:          stretch;
  box-flex:           0;
}

.left {
  width:              180px;
  -webkit-box-flex:   0;
  -moz-box-flex:      0;
  box-flex:           0;
}

.vs {
  width:              40px;
  display:            -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-box-align:  stretch;
  -webkit-box-flex:   0;
  display:            -moz-box;
  -moz-box-orient:    vertical;
  -moz-box-align:     stretch;
  -moz-box-flex:      0;
  display:            box;
  box-orient:         vertical;
  box-align:          stretch;
  box-flex:           0;
}

.full {
  width:              40px;
  height:             100%;
  background-image:   url(gold.jpg);
}

.partial {
  width:              40px;
  height:             30px;
  background-image:   url(gold.jpg);
}

.no {
  width:              40px;
  height:             30px;
}

.center {
  min-width:          300px;
  -webkit-box-flex:   1;
  -moz-box-flex:      1;
  box-flex:           1;
}
     
.right {
  width:              220px;
  -webkit-box-flex:   0;
  -moz-box-flex:      0;
  box-flex:           0;
}

#header > .left {
  background-color:   #bbbbdd;
}
#header > .center {
  background-color:   #dddddd;
}
#header > .right {
  background-color:   #ddbbbb;
}
#main > .left {
  background-color:   #eeeeff;
}
#main > .center {
  background-color:   #ffffff;
}
#main > .right {
  background-color:   #ffeeee;
}
#footer > .left {
  background-color:   #ccccee;
}
#footer > .center {
  background-color:   #eeeeee;
}
#footer > .right {
  background-color:   #eecccc;
}

… here’s the HTML …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<html>
  <head>
    <link href="FlexBox Example.css" media="screen"  rel="stylesheet" type="text/css">
  </head>
  <body>
    <div id="app">
     
      <div id="header">
       
        <div class="left">
          <div class="content">
            <p>left header</p>
          </div> <!-- content -->
        </div> <!-- left -->
       
        <div class="vs">
          <div class="partial vs"></div>
          <div class="no"></div>
          <div class="partial vs"></div>
        </div>
   
        <div class="center">
          <div class="content">
            <h1>Vern's <span class="alt-font">Business Card</span> Layout</h1>
          </div> <!-- content -->
        </div> <!-- center -->
       
        <div class="right">
          <div class="content">
            right header
          </div> <!-- content -->
        </div> <!-- right -->
       
      </div> <!-- header -->
     
      <div class="hs"><img class="icn" src="green_lightening.png" /></div>

      <div id="main">
       
        <div class="left">
          <div class="content">
            Left Sidebar blah, blah, blah, blah, blah, blah, blah, blah, blah, blah
          </div> <!-- content -->
        </div> <!-- left -->
       
        <div class="vs full"></div>
   
        <div class="center">
          <div class="content">
            <h3>Center Content</h3>
            <p>
              All these layouts are designed to mimic the layout of my business cards.
            </p><p>
              This example uses the Flexible Box Module and is very loosely based on based on ...
            </p><p>
              <a href="http://developer.appcelerator.com/blog/2010/06/how-to-full-page-css-3-layout-desktop.html">
                How-To: Full Page CSS 3 Layout (Desktop)
              </a>
            </p>
          </div> <!-- content -->
        </div> <!-- center -->
       
        <div class="right">
          <div class="content">
            <h3>See Also:</h3>
            <h5>Flexible Box Layout Module</h5>
            <p><a href="FlexBox Example.html">Best Result</a></p>
            <h5>Template Layout Module</h5>
            <p><a href="Template Example.html">Best Result</a></p>
            <h5>Client-Side Template Layout</h5>
            <p><a href="Client Example.html">Best Result</a></p>
            <p><a href="Client Example XC.html">Uncontained Content</a></p>
            <p><a href="Client Example XT.html">Missing Template</a></p>
          </div> <!-- content -->
        </div> <!-- right -->
       
      </div> <!-- main -->
     
      <div id="footer">
       
        <div class="left">
          <div class="content">
            left footer
          </div> <!-- content -->
        </div> <!-- left -->
       
        <div class="vs full"></div>
   
        <div class="center">
          <div class="content">
            <p>
              &copy; 2010 Vern McGeorge
            </p>
          </div> <!-- content -->
        </div> <!-- center -->
       
        <div class="right">
          <div class="content">
            right footer
          </div> <!-- content -->
        </div> <!-- right -->
       
      </div> <!-- footer -->
     
    </div> <!-- app -->
  </body>
</html>

… and here’s the result …

If you want to try it, click here (best viewed in Firefox). Please note that I have replaced the copyrighted images I used for the vertical and horizontal stripes so I could share the code.

Although I could continue to tweak this code and get a better result, my interest in this topic is largely driven by what tool set yields an “easy to get it right” experience. Neither did, so after devoting a couple hours to each method, I eventually copied my VernMcGeorge.com theme over to my (SparkofIdeation) website and hacked together new background images to get the equivalent result.

Anyway, what worked well and what didn’t?

What worked well:

  • I got a good, solid layout with well positioned layout with a footer fixed to the bottom of the browser window (which I’ve never been able to do before and which is wonderful for web applications that want to use the bottom edge of the screen for useful purposes (such as navigation or mode selection) just like installed applications do. Very cool!
  • I got full coverage of all areas of the layout using standard background-color and background-image CSS declarations – no funky faux-columns or negative margins here!

What didn’t work well:

  • The footer overlays the main content (in Google Chrome).
  • Still a ton of mark-up devoted to layout. Rather than non-tabular data pretending to be tables, there are a bunch of nested div tags. While this may be better from a semantically pure point of view, the effect on the readability of the markup is same. Fully 67 of 100 lines in the HTML in the body is about layout and only 33 lines are actual content. Although I can (and did) do better in a later version (see below), there’s no getting around a significant fraction of non-content code.
  • This can only work on moz and webkit engine browsers (which is to say all the good ones) and fails miserably on Internet Explorer.

The first two problems could be coding errors on my part (which I will resolve later in this article if possible) but the third issue in intrinsic to this solution.

Template Layout Module

Next, I tried the CSS Template Layout Module.

Here’s the CSS …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
html,body {
  margin:           0;
  padding:          0;
}

#app {
  height:           100%;
  width:            100%;
}

.content {
  margin:           10px;
}

.icn {
  position: absolute;
  left:     170px;
}  

#template
{
  height:           100%;
  width:            100%;
  display:
    "abef"          /20px
    "acef"          /20px
    "adef"          /20px
    "gggg"          /20px
    "hijk"
    "limn"          /30px
    180px 40px * 220px
  ;
}
 
#left_header        { position: a; }
#thvs               { position: b; }
#vsgap              { position: c; }
#bhvs               { position: d; }
#header             { position: e; }
#right_header       { position: f; }
#hs                 { position: g; }
#left_main          { position: h; }
#mvs                { position: i; }
#main               { position: j; }
#right_main         { position: k; }
#left_footer        { position: l; }
#footer             { position: m; }
#right_footer       { position: n; }

#template::slot(a)  { background: #bbbbdd; }
#template::slot(b)  { background: #f44; }
#template::slot(c)  { background: #bbd; }
#template::slot(d)  { background: #f88; }
#template::slot(e)  { background: #dddddd; }
#template::slot(f)  { background: #ddbbbb; }
#template::slot(g)  { background: #88f; }
#template::slot(h)  { background: #eeeeff; }
#template::slot(i)  { background: #f88; }
#template::slot(j)  { background: #ffffff; }
#template::slot(k)  { background: #ffeeee; }
#template::slot(l)  { background: #ccccee; }
#template::slot(m)  { background: #eeeeee; }
#template::slot(n)  { background: #eecccc; }

#thvs {
  background-image: url(gold.jpg);
  height:           20px;
  width:            40px;
}
#bhvs {
  background-image: url(gold.jpg);
  height:           20px;
  width:            40px;
}
#hs {
  background-image: url(blue.jpg);
  height:           20px;
}
#mvs {
  background-image: url(gold.jpg);
  width:            40px;
}

… here’s the HTML …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<html>
  <head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://css-template-layout.googlecode.com/files/jquery.tpl_layout1.1.1.js"></script>
    <script type="text/javascript">
      $(document).ready(function()
      {
        $.setTemplateLayout()
        var x = 1;
      });
    </script>
    <link href="Template Example.css" media="screen"  rel="stylesheet" type="text/css">
  </head>
  <body>
    <div id="template">
     
      <div id="thvs"> </div>
      <div id="bhvs"> </div>
      <div id="hs"> <img class="icn" src="green_lightening.png" /> </div>
      <div id="mvs"> </div>
     
      <div id="left_header">
          <div class="content">
            <p>left header</p>
          </div> <!-- content -->
        </div> <!-- left_header -->
     
      <div id="header">
        <div class="content">
          <h1>Vern's <span class="alt-font">Business Card</span> Layout</h1>
        </div> <!-- content -->
      </div> <!-- header -->
     
      <div id="right_header">
        <div class="content">
          right header
        </div> <!-- content -->
      </div> <!-- right_header -->

      <div id="left_main">
        <div class="content">
          Left Sidebar blah, blah, blah, blah, blah, blah, blah, blah, blah, blah
        </div> <!-- content -->
      </div> <!-- left_main -->
     
      <div id="main">
        <div class="content">
          <h3>Center Content</h3>
          <p>
            All these layouts are designed to mimic the layout of my business cards.
          </p><p>
            This example uses the Template Layout Module and is very loosely based on ...
          </p><p>
            <a href="http://www.nealgrosskopf.com/tech/thread.php?pid=46">
              CSS Template Layouts: A Simpler CSS Layout System, Now Possible With JQuery
            </a>
          </p><p>

          </p>
        </div> <!-- content -->
      </div> <!-- main -->
     
      <div id="right_main">
        <div class="content">
          <h3>See Also:</h3>
          <h5>Flexible Box Layout Module</h5>
          <p><a href="FlexBox Example.html">Best Result</a></p>
          <h5>Template Layout Module</h5>
          <p><a href="Template Example.html">Best Result</a></p>
          <h5>Client-Side Template Layout</h5>
          <p><a href="Client Example.html">Best Result</a></p>
          <p><a href="Client Example XC.html">Uncontained Content</a></p>
          <p><a href="Client Example XT.html">Missing Template</a></p>
        </div> <!-- content -->
      </div> <!-- right_main -->

      <div id="left_footer">
        <div class="content">
          left footer
        </div> <!-- content -->
      </div> <!-- left_footer -->
     
      <div id="footer">
        <div class="content">
          <p>
            &copy; 2010 Vern McGeorge
          </p>
        </div> <!-- content -->
      </div> <!-- footer -->
     
      <div id="right_footer">
        <div class="content">
          right footer
        </div> <!-- content -->
      </div> <!-- right_footer -->
       
    </div>  
  </body>
</html>

…and here’s the result …

You can look at a live example here, although because of problems which I have not yet identified, the solution has degraded from the screen shot. I have copied this example into two new HTML and CSS files and reduced to down to the simplicity of “Hello, World!” (hoping to delete the problem at some point and thereby identify it) but have not been successful. It’s clearly some stupid little thing that I have grown blind to, so I leave the solution to you, the reader.

When I tried this a year ago on a different project, I got a better result. Obvously, I made some mistakes this time around or the solution that allows me to experiment with this method has degraded with time. I’m betting on my mistakes as the cause.

Regardless of any mistakes, there’s enough here (in the screen shot) to look at some of the pros and cons of this solution.

Let’s start with the cons:

  • This solution relies on some fairly heavy duty JavaScript to interpret and apply the template after the page is loaded.
  • Because of this, users that disable JavaScript get an ugly result.
  • Everything has floated to the top and overlaps other content – yuck!
  • The post-processing to apply the layout positions everything absolutely and renders the resulting page virtually unreadable using developers tools and consoles such as Firebug.
  • While this solution looks like it might apply background-colors well enough, it didn’t do anything for background images until later – when it stopped doing layout. Not a good trade in my book.

Now, let’s consider the pro’s:

  • Although the nested “content” divs remain in this markup, all of the rest of the layout has been relegated to the CSS file where, IMHO, it belongs. Each section of content can then be wrapped in a single div with an id attribute that identifies what it is and can be used to position the content within the layout.
  • The sections of content are order independent from the layout and can be presented from most important to least in the HTML markup. In contrast, layout that depends on tables or nested divs must be presented in a left-to-right, then top-to-bottom format (at least in English).
  • Although the current proposal for the Template Layout Module is restricted to rectangular slots (into which content is positioned), the spec foresees the possibility of non-rectangular and dis-contiguous slots in some future version. Wouldn’t that be sweet?

Client-Side Template Layout

Next, I asked my self, “How can I get the best of both these solutions? Just how much of the markup devoted to layout could I remove if I was willing to pull out all the stops?”

The solution I can up with uses JavaScript to support the loading and application of a client-side template based on the Flexible Box Layout Module. This both removed most of the markup from the web page itself and allowed for order independence between the content as it is loaded and the content as it is finally presented to the user.

JavaScript is used to demonstrate this process although with two small extensions to the HTML specification, JavaScript would no longer be needed and the end result will be improved.

I started with the code from the Flexible Box Layout example above split the markup into a template (which includes default content) and the actual page content. I then added JavaScript to load and apply the template.

Here’s the JavaScript Template Loader…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Template Loader in three parts - Content Loader, Utility Functions, Functions to Load and Apply Template

// Part 1 - Content Loader

// Credit where credit is due. This is pretty much straight out of "AJAX in Action" by Dave Crane and Eric Pasarello with Darren James.

/*
url-loading object and a request queue built on top of it
*/


/* namespacing object */
var net=new Object();

net.READY_STATE_UNINITIALIZED=0;
net.READY_STATE_LOADING=1;
net.READY_STATE_LOADED=2;
net.READY_STATE_INTERACTIVE=3;
net.READY_STATE_COMPLETE=4;


/*--- content loader object for cross-browser requests ---*/
net.ContentLoader=function(url,onload,onerror,method,params,contentType){
  this.req=null;
  this.onload=onload;
  this.onerror=(onerror) ? onerror : this.defaultError;
  this.loadXMLDoc(url,method,params,contentType);
}

net.ContentLoader.prototype.loadXMLDoc=function(url,method,params,contentType){
  if (!method){
    method="GET";
  }
  if (!contentType && method=="POST"){
    contentType='application/x-www-form-urlencoded';
  }
  if (window.XMLHttpRequest){
    this.req=new XMLHttpRequest();
  } else if (window.ActiveXObject){
    this.req=new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (this.req){
    try{
      var loader=this;
      this.req.onreadystatechange=function(){
        net.ContentLoader.onReadyState.call(loader);
      }
      this.req.open(method,url,true);
      if (contentType){
        this.req.setRequestHeader('Content-Type', contentType);
      }
      this.req.send(params);
    }catch (err){
      this.onerror.call(this);
    }
  }
}


net.ContentLoader.onReadyState=function(){
  var req=this.req;
  var ready=req.readyState;
  var httpStatus=req.status;
  if (ready==net.READY_STATE_COMPLETE){
    if (httpStatus==200 || httpStatus==0){
      this.onload.call(this);
      // allows the passing of this context to the callback function
      // so the callback function has access to req.
    }else{
      this.onerror.call(this);
    }
  }
}

net.ContentLoader.prototype.defaultError=function(){
  alert("error fetching data!"
    +"\n\nreadyState:"+this.req.readyState
    +"\nstatus: "+this.req.status
    +"\nheaders: "+this.req.getAllResponseHeaders());
}

// Part 2 - Utility Functions

// Credit where credit is due. The findNodes() function is straight out of Foothill College's COIN 71 (Web Development) by Elaine Haight.

// Looks through an XML tree to find nodes with "tagname" and
// a particular "attribute" / "value" pair. Returns an array
// of nodes if found and returns an empty array if no matching
// nodes is found. Modified by VEM to use a custom comparator.

function findNodes (tree, tagName, attribute, value, comparator) {
  comparator = (comparator) ? comparator : defaultComparator;
  var targetNodeArray = new Array();
  var nodeArray = tree.getElementsByTagName(tagName);
  // Build an array of all nodes in nodeArray where the attribute matches the value.
  for (var i=0; i<nodeArray.length; ++i) {
    var attributeValue = nodeArray[i].getAttribute(attribute);
    if (null != attributeValue) {
      var found = comparator(attributeValue, value);
      if (found) {
        targetNodeArray.push(nodeArray[i]);
      }
    }
  }
  return (targetNodeArray);
}

// Looks through an XML tree to find a node with "tagname" and
// a particular "attribute" / "value" pair. Returns the first node
// found and returns null if no such node is found.

function findNode (tree, tagName, attribute, value, comparator) {
  var nodeArray = findNodes(tree, tagName, attribute, value, comparator);
  var targetNode = (nodeArray.length > 0) ? nodeArray[0] : null;
  return (targetNode);
}

// Returns true if value1 == value2 and false otherwise.
function defaultComparator (value1, value2) {
  return (value1 == value2);
}

// Part 3 - Functions to Load and Apply Template

// Returns true if attributeValue matches the pattern ("... value xxx ...")
// that we are looking for and false otherwise.
function containsComparator (attributeValue, value) {
  var contains = false;
  var classArray = attributeValue.split(' ');
  // Look for the contains class followed by one other class name
  for (var i=0; i<classArray.length-1 && contains==false; ++i) {
    if (classArray[i] == value) {
      contains = true;
    }
  }
  return (contains);
}

// Returns "xxx" from an element's class="... value xxx ..." attribute.
function containsId (container) {
  var contains = null;
  var classList = container.getAttribute('class');
  var classArray = classList.split(' ');
  // Look for the contains class followed by one other class name
  for (var i=0; i<classArray.length-1 && contains==null; ++i) {
    if (classArray[i] == 'contains') {
      contains = classArray[i+1];
    }
  }
  return (contains);
}

// If successfully loaded, apply the template.
function applyTemplate () {
  var body = document.getElementsByTagName('body')[0];
  // Add the template node as the last (and if everything works, the only) child of the body.
  // If everything works out, all preceeding children will be moved into the template, leaving it as the only child.
  var bodyHTML = body.innerHTML;
  var templateHTML = this.req.responseText;
  body.innerHTML = bodyHTML + templateHTML;
  // Walk through template finding nodes that contain attribute class="... contains id ..."
  var containers = findNodes(body, '*', 'class', 'contains', containsComparator);
  // For each node found
  for (var i=0; i<containers.length; ++i) {
    var container = containers[i];
    // Find node in the body with matching tag name and attribute id="id"
    var contentId = containsId(container);
    var content = document.getElementById(contentId);
    if (null != content) {
      // Remove any existing default content from the container.
      container.innerHTML = "";
      // Insert the content element into the container element and remove the content
      // element from its original place in the document.
      container.appendChild(content);
    }
  }
}

// If not successfully loaded, complain about it.
function templateCouldNotBeLoaded () {
  alert("Template Could Not Be Loaded!"
    +"\n\nreadyState:"+this.req.readyState
    +"\nstatus: "+this.req.status
    +"\nheaders: "+this.req.getAllResponseHeaders());
}

// After the page is loaded, load and apply the template to it.
function loadAndApplyTemplate (templateFile) {
  new net.ContentLoader(templateFile, applyTemplate, templateCouldNotBeLoaded);
}

… here’s the CSS file …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
html,body {
  margin:   0;
  padding:  0;
}

#template {
  height:   100%;
  width:    100%;
  display:  -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-box-align:  stretch;
  display:  -moz-box;
  -moz-box-orient:  vertical;
  -moz-box-align:   stretch;
  display:  box;
  box-orient:     vertical;
  box-align:      stretch;
}

#header {
  min-height:60px;
  width: 100%;
  display: -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align: stretch;
  -webkit-box-flex:0;
  display: -moz-box;
  -moz-box-orient: horizontal;
  -moz-box-align: stretch;
  -moz-box-flex:0;
  display: box;
  box-orient: horizontal;
  box-align: stretch;
  box-flex:0;
}

.hs {
  position:   relative;
  margin-left:      30px;
  margin-right:   30px;
  height:   20px;
  background-image: url(blue.jpg);
}

.icn {
  position: absolute;
  left:     170px;
}  

#content {
  width: 100%;
  bottom: 30px;
  display: -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align: stretch;
  -webkit-box-flex:1;
  display: -moz-box;
  -moz-box-orient: horizontal;
  -moz-box-align: stretch;
  -moz-box-flex:1;
  display: box;
  box-orient: horizontal;
  box-align: stretch;
  box-flex:1;
}

#footer {
  min-height:30px;
  width: 100%;
  display: -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-align: stretch;
  -webkit-box-flex:0;
  display: -moz-box;
  -moz-box-orient: horizontal;
  -moz-box-align: stretch;
  -moz-box-flex:0;
  display: box;
  box-orient: horizontal;
  box-align: stretch;
  box-flex:0;
}

.left {
  width:180px;
  -webkit-box-flex:0;
  -moz-box-flex:0;
  box-flex:0;
}

.vs {
  width: 40px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-box-align: stretch;
  -webkit-box-flex:0;
  display: -moz-box;
  -moz-box-orient: vertical;
  -moz-box-align: stretch;
  -moz-box-flex:0;
  display: box;
  box-orient: vertical;
  box-align: stretch;
  box-flex:0;
}

.full {
  width: 40px;
  height:   100%;
  background-image: url(gold.jpg);
}

.partial {
  width: 40px;
  height:   30px;
  background-image: url(gold.jpg);
}

.no {
  width: 40px;
  height:   30px;
}

.center {
  min-width:  300px;
  -webkit-box-flex:1;
  -moz-box-flex:1;
  box-flex:1;
}
     
.right {
  width:220px;
  -webkit-box-flex:0;
  -moz-box-flex:0;
  box-flex:0;
}

#header > .left {
  background-color: #bbbbdd;
}
#header > .center {
  background-color: #dddddd;
}
#header > .right {
  background-color: #ddbbbb;
}
#content > .left {
  background-color: #eeeeff;
}
#content > .center {
  background-color: #ffffff;

}
#content > .right {
  background-color: #ffeeee;
}
#footer > .left {
  background-color: #ccccee;
}
#footer > .center {
  background-color: #eeeeee;
}
#footer > .right {
  background-color: #eecccc;
}

.container {
  margin: 10px;
}

… here’s the HTML of the client side template …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<div id="template">
 
  <div id="header">
   
    <div class="left">
      <div class="container contains left_header">
        <p>default left header</p>
      </div> <!-- left_header -->
    </div> <!-- left -->
   
    <div class="vs">
      <div class="partial vs"></div>
      <div class="no"></div>
      <div class="partial vs"></div>
    </div> <!-- vs -->
   
    <div class="center">
      <div class="container contains center_header">
        <h1>default Vern's <span class="alt-font">Business Card</span> Layout</h1>
      </div> <!-- center_header -->  
    </div> <!-- center -->
   
    <div class="right">
      <div class="container contains right_header">
        default right header
      </div> <!-- right_header -->
    </div> <!-- right -->
   
  </div> <!-- header -->
 
  <div class="hs"><img class="icn" src="green_lightening.png" /></div>
 
  <div id="content">
   
    <div class="left">
      <div class="container contains left_sidebar">
        default Left Sidebar blah, blah, blah, blah, blah, blah, blah, blah, blah, blah
      </div> <!-- left_sidebar -->
    </div> <!-- left -->
   
    <div class="vs full"></div>
   
    <div class="center">
      <div class="container contains center_content">
        <h3>Center Content</h3>
        <p>
          default This layout is designed to mimic the layout of my business cards and is based on
        </p><p>
          <a href="http://developer.appcelerator.com/blog/2010/06/how-to-full-page-css-3-layout-desktop.html">
            How-To: Full Page CSS 3 Layout (Desktop)
        </a>
        </p><p>
          Copyright 2010 Awesome Productions Ltd. as modified by Vern McGeorge
        </p>
      </div> <!-- center_content -->
    </div> <!-- center -->
   
    <div class="right">
      <div class="container contains right_sidebar">
        default Right Sidebar
      </div> <!-- right_sidebar -->
    </div> <!-- right -->
   
  </div> <!-- content -->
 
  <div id="footer">
   
    <div class="left">
      <div class="container contains left_footer">
        default left footer
      </div> <!-- left_footer -->
    </div> <!-- left -->
   
    <div class="vs full"></div>
   
    <div class="center">
      <div class="container contains center_footer">
        <p>
          default Copyright 2010 Vern McGeorge
        </p>
      </div> <!-- center_footer -->
    </div> <!-- center -->
   
    <div class="right">
      <div class="container contains right_footer">
        default right footer
      </div> <!-- right_footer -->
    </div> <!-- right -->
   
  </div> <!-- footer -->
 
</div> <!-- template -->

… here’s the HTML of the web page…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<html>
  <head>
   
    <script type="text/javascript" src="TemplateLoader.js"></script>
    <script type="text/javascript">
      window.onload = function() {
        loadAndApplyTemplate('Client Example Template.html');
      };
    </script>
    <link href="Client Example.css" media="screen"  rel="stylesheet" type="text/css">
  </head>
  <body>
   
      <div id="center_header">
        <h1>Vern's <span class="alt-font">Business Card</span> Layout</h1>
      </div> <!-- center_header-->
     
      <div id="center_content">
        <h3>Center Content</h3>
        <p>
          All these layouts are designed to mimic the layout of my business cards.
        </p><p>
          The intention of this example (and the examples related to it) is to reduce the HTML markup in the page to the bare minimum
          and apply a template on the client-side to position the page content into the page template.
        </p><p>
          These examples use a combination of a JavaScript Content Loader and the Flexible Box Module and are very loosely based on based on ...
        </p><p>
          <a href="http://developer.appcelerator.com/blog/2010/06/how-to-full-page-css-3-layout-desktop.html">
            How-To: Full Page CSS 3 Layout (Desktop)
          </a>
        </p><p>
          The JavaScript Content Loader loads an HTML template into the body of the web page and then processes the template, looking for
          elements that include the "contains" class.
        </p><p>
          Specifically, containers in the template have the attribute <code>class="container, contains, xxx"</code> where ...
          <ul>
            <li>
              "container" is used in the CSS to apply any specific styling desired to all containers.
            </li><li>
              "contains" is used to tell the <code>applyTemplate();</code> function to look for content and move it into the template.
            </li><li>
              "xxx" is used to specify the id attribute's value for the content to be moved.
            </li>
          </ul>
          Generally, this works like a champ using the Firefox browser.
        </p><p>
          Note that in this example, everything works .
        </p><p>
          Chrome has trouble loading the template.
        </p><p>
          Other webkit browsers have not been tested.
        </p><p>
          Internet Explorer will not work for this example because of non-support for the Flexible Box Model.
        </p>
      </div> <!-- center_content-->

      <div id="left_sidebar">  
        <p>Left Sidebar 1, 2, 3, 4, 5, 6, 7, 8, 9, 10</p>
      </div> <!-- left_sidebar-->
   
      <div id="right_sidebar">
        <h3>See Also:</h3>
        <h5>Flexible Box Layout Module</h5>
        <p><a href="FlexBox Example.html">Best Result</a></p>
        <h5>Template Layout Module</h5>
        <p><a href="Template Example.html">Best Result</a></p>
        <h5>Client-Side Template Layout</h5>
        <p><a href="Client Example.html">Best Result</a></p>
        <p><a href="Client Example XC.html">Uncontained Content</a></p>
        <p><a href="Client Example XT.html">Missing Template</a></p>
      </div> <!-- right_sidebar-->

      <div id="center_footer">
        <p>
          &copy; 2010 Vern McGeorge
        </p>
      </div> <!-- center_footer-->
   
      <div id="left_header">
        <p>left header</p>
      </div> <!-- left_header-->

      <div id="right_header">
        right header
      </div> <!-- right_header-->
             
      <div id="left_footer">
        left footer
      </div> <!-- left_footer-->
     
      <div id="right_footer">
        right footer
      </div> <!-- right_footer-->
     
  </body>
</html>

… and here’s the result …

Business Card Layout - Client-Side Layout - Results

If you want to try it, click here (best viewed in Firefox).

The key to this solution is found in the class attribute values for certain divs in the HTML template, for example …

44
      <div class="container contains center_content">

… where the value of the class attribute is interpreted as follows:

  • “container” is used in the CSS to apply any specific styling desired to all containers. This takes the place of the nested “content” divs in the original.
  • “contains” is used to tell the applyTemplate(); function to look for content and move it into the template.
  • “center_content” is used to specify the id attribute’s value for the content to be moved.

… which means that the following will be moved from its starting position in the page and placed in the container, thus replacing any default content specified in the template …

18
19
20
      <div id="center_content">
...
      </div> <!-- center_content-->

Anyway, what worked well and what didn’t?

What worked well:

  • Everything good about the Flexible Box Layout.
  • The order independence and the minimal markup of Template Layout.

What didn’t work well:

  • The footer overlays the main content (in Google Chrome).
  • Significant flicker on load.
  • This can only work on moz and webkit engine browsers (which is to say all the good ones) and fails miserably on Internet Explorer.

I’ll address the flicker problem when I talk about how this could be implemented without the JavaScript hack.

First, let’s look at two obvious problems – content that doesn’t match a container and problems loading the template.

If content does match a container in the template, as shown below, …

56
57
58
59
60
61
62
      </div> <!-- center_content-->

      <div id="Xleft_sidebar"> 
        <p>Left Sidebar 1, 2, 3, 4, 5, 6, 7, 8, 9, 10</p>
      </div> <!-- Xleft_sidebar-->
   
      <div id="right_sidebar">

… then the content will be rendered at the top of the page and the template and its content will be pushed down.

If you want to try it, click here (best viewed in Firefox).

If the template cannot be loaded …

4
5
6
7
8
9
    <script type="text/javascript" src="TemplateLoader.js"></script>
    <script type="text/javascript">
      window.onload = function() {
        loadAndApplyTemplate('XClient Example Template.html');
      };
    </script>

… then an exception will be raised, the page will render “as is”, and the template will not be applied.

If you want to try it, click here (best viewed in Firefox).

More subtle errors caused by malformed HTML after the page, template, and page contents have been merged are the not considered here, nor is the impact on tools and validation.

Now could this technique be implemented without JavaScript? It would require two small changes to the HTML specification as shown below.

First, the link tag would have to be extended to allow the browser to load the template inside the body of the page without JavaScript. This could be done at the start of the page …

5
6
7
8
  <body>
    <link href="Client Example Template.html" media="screen"  rel="template" type="text/html">
   
      <div id="center_header">

… or it could be done a the end of the page (which is, in effect, what the JavaScript hack is doing …

87
88
89
90
      </div> <!-- right_footer-->
 
    <link href="Client Example Template.html" media="screen"  rel="template" type="text/html">    
  </body>

If loaded that the start, the browser should build a “hit list” of containers to be filled. If loaded at the end, the browser should build a “hit list” of content to be inserted. In either event, the code is not complex.

Note that the link tag is defined for other things besides spreadsheets (but not rel=“template”) however, browser support is only broadly available for loading stylesheets.

The second change is to define an explicit contains attribute for containers so that the class attribute does not have to be overloaded with this function …

17
18
19
20
21
    <div class="center">
      <div contains="center_header" class="container">
        <h1>default Vern's <span class="alt-font">Business Card</span> Layout</h1>
      </div> <!-- center_header -->  
    </div> <!-- center -->

If you want to mess around with this, I have included all the code and images in this zip archive – I ask only that you link back to or otherwise attribute my work to me.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>