I've always felt that the HTML CODE element was a great element when it came to the semantic web. My biggest gripe with the CODE element is that it is difficult to style. For starters it's a single element with no child elements so there really is no effective way to style each 'row' of code with the CODE element. That means if you want to give your code, line numbers, style those line numbers or give your code zebra stripes it can be a bit difficult to say the least.
Since I began writing these articles, I've always placed my code blocks in the CODE element. I've noticed that most robust code block styling solutions prefer that your code sits in an OL tag which makes the most sense when trying to style it. My first task was to convert all my CODE elements to contain OLs and LIs for children to represent each row of the code blocks.
A Bit Of JavaScript
Okay, so I lied in my title of this article, sort of. If you already have your code blocks set up in this manner you can skip this JavaScript. I didn't have mine set up this way already so I needed a bit of JavaScript to port over my CODE only elements to the new friendly CODE OL LI format.
<code>
<ol>
<li></li>
<li></li>
</ol>
</code>
And the JavaScript...
I should point out that using window.onload = DOMReadyAll; isn't necessarily the best way to run JavaScript. In a production environment you'll want to use a JavaScript framework such as JQuery to ensure your JavaScript runs when the entire DOM has been loaded. (I use JQuery on my site to accomplish this).
//http://www.somacon.com/p355.php
String.prototype.trim = function()
{
return this.replace(/^s+|s+$/g,"");
}
//http://www.experts-exchange.com/Web/Web_Languages/JavaScript/Q_21478202.html
function codeToList()
{
//Convert code elements to ordered lists
var code = document.getElementsByTagName("code")
for (i = 0; i < code.length; i++)
{
code[i].innerHTML = "
<ol class='code'>" + code[i].innerHTML.replace(/
/g, "<li>").trim() + "</ol>";
} //end for
//Apply zebra stripes
if (document.getElementsByClassName)
{
var code = document.getElementsByClassName("code");
for (i = 0; i < code.length; i++)
{
var li = code[i].getElementsByTagName("li")
var cn = "odd";
for (x = 0; x < li.length; x++)
{
li[x].className = cn;
cn == "odd" ? cn = "even" : cn = "odd"; //condition ? true : false
} //end for
} //end for
} //end if
} //end function
window.onload = DOMReadyAll;
function DOMReadyAll()
{
codeToList();
}
On To The CSS
I apply the .CODE class to the OL element. Originally I wrote this accomplishing zebra stripes using two classes. (I've left that in the original JavaScript above in case somebody is able to solve the problem for me.) As you can see below I have a background-image that now mimics the zebra stripes. I also reset the OL element with 0 margins and padding. Finally I set it to overflow so that if the code runs wide, it will add scrollbars to it.
.code
{
background: url(code_bg.png) repeat left top;
border: 1px solid #cccccc;
list-style-type: none;
margin: 0px;
padding: 0px;
overflow: auto;
width: 550px;
}
Styling The LI Element In Code Blocks
Next up we need to style the LI element. I used one of Windows Vista's new code-friendly fonts called 'Consolas'. As a fallback I included the other standard code fonts as well. It's also important to include 'white-space: pre' as this will keep your code formatted correctly. I also found that I needed to place line-height: 0px to make it work correctly in Safari.
.code li
{
color: #669933;
font-family: "Consolas", "Courier New", Courier, mono;
line-height: 0px; /* to remove white border issue */
white-space: pre;
}
Setting Up Our CSS Counter
I never thought I'd find a practical use for CSS counters until now. Because it's almost impossible to style the numbers in LI tags without wrapping your LI text in another HTML element I opted to use a CSS counter and style that instead. The following code resets our counter to 0 in case there were more than one code block per page.
.code { counter-reset: li; }
Styling Our CSS Counter Number
I then insert the counter :before the LI tag using CSS generated content. I also need to increment the counter via 'counter-increment: li'. li is essentially a variable name I give the counter in CSS. It could be named 'i', 'cat', 'dog', etc - really whatever you want. Since I am using it on a series of LI elements I named it 'li'.
All the other CSS properties are style related. It should be noted that all of these properties will ONLY apply to the CSS counter generated content and not the entire LI element which is the reason I opted for this method in the first place. I should also point out that the line-height you use, corresponds to the the background-image height you use on the ol.code element.
.code li:before
{
counter-increment: li;
content: counter(li) ". ";
background: #ececec;
border-right: 1px solid #cccccc;
color: #555555;
display: inline-block;
font-family: Arial, Helvetica, sans-serif;
line-height: 30px; /* minumum line height = 24, smaller causes white border issue */
margin-right: 20px;
padding-right: 5px;
text-align: right;
width: 50px;
}
Hiding Those Leftover LIs
Finally I use a bit of CSS to hide any empty LI elements. For whatever reason, I found that the first or last line of the code element would contain an extra line and I use this to hide that.
.code li:empty { display: none; }
Zebra Striping With Only CSS
I've also included a commented-out section that if somebody had more time, could get working. It would basically look like this.
.code li:nth-child(odd) { background: #ffffff; }
.code li:nth-child(even) { background: #fafafa; }
.code .odd { background: #ffffff; }
.code .even { background: #fafafa; }
The JavaScript code will still generate the .even .odd classes but I was unable to get this working correct. The overflowing ol.code element was stopping the LI background-color from spanning the entire width of the OL tag. As a last resort I applied a background-image to the ol.code element.
Putting It All Together
<script type="text/javascript">
//http://www.somacon.com/p355.php
String.prototype.trim = function()
{
return this.replace(/^s+|s+$/g,"");
}
//http://www.experts-exchange.com/Web/Web_Languages/JavaScript/Q_21478202.html
function codeToList()
{
//Convert code elements to ordered lists
var code = document.getElementsByTagName("code")
for (i = 0; i < code.length; i++)
{
code[i].innerHTML = "
<ol class='code'>" + code[i].innerHTML.replace(/
/g, "<li>").trim() + "</ol>";
} //end for
//Apply zebra stripes
if (document.getElementsByClassName)
{
var code = document.getElementsByClassName("code");
for (i = 0; i < code.length; i++)
{
var li = code[i].getElementsByTagName("li")
var cn = "odd";
for (x = 0; x < li.length; x++)
{
li[x].className = cn;
cn == "odd" ? cn = "even" : cn = "odd"; //condition ? true : false
} //end for
} //end for
} //end if
} //end function
window.onload = DOMReadyAll;
function DOMReadyAll()
{
codeToList();
}
</script>
<style type="text/css">
.code
{
background: url(code_bg.png) repeat left top;
border: 1px solid #cccccc;
list-style-type: none;
margin: 0px;
padding: 0px;
overflow: auto;
width: 550px;
}
.code li
{
color: #669933;
font-family: "Consolas", "Courier New", Courier, mono;
line-height: 0px; /* to remove white border issue */
white-space: pre;
}
.code { counter-reset: li; }
.code li:before
{
counter-increment: li;
content: counter(li) ". ";
background: #ececec;
border-right: 1px solid #cccccc;
color: #555555;
display: inline-block;
font-family: Arial, Helvetica, sans-serif;
line-height: 30px; /* minumum line height = 24, smaller causes white border issue */
margin-right: 20px;
padding-right: 5px;
text-align: right;
width: 50px;
}
.code li:empty { display: none; }
/*
.code li:nth-child(odd) { background: #ffffff; }
.code li:nth-child(even) { background: #fafafa; }
.code .odd { background: #ffffff; }
.code .even { background: #fafafa; }
*/
</style>
<code>
<ol class="code">
<li>img</li>
<li>{</li>
<li>border: 1px solid #cccccc;</li>
<li>padding: 10px;</li>
<li>opacity: .9;</li>
<li>}</li>
<li></li>
<li>img:hover</li>
<li>{</li>
<li>border-color: #aaaaaa;</li>
<li>opacity: 1;</li>
<li>}</li>
<li></li>
</ol>
</code>