In a previous post, I mentioned using ordered lists to display block-level code snippets. The solution worked, but had disadvantages, most notably the inefficiency during initially getting the code on the site. So I decided to revisit displaying block-level code snippets, with a solution that accomplished the following goals:

  • Efficiency during initial writing / copy & paste
  • Extensibility
  • Cross-browser compatibility
  • Minimal footprint
  • Usability (line numbers, ability to copy & paste, etc.)

The new solution acts on any code in the following format:

<pre><code>
<?php echo 'Hello World'; ?>
</code></pre>

… and turns it into this:


<php echo 'Hello World'; ?>

Here’s the code:


// Handles dynamic beautifying of code blocks
var codeHandler = {
	codeBlocks: '',
	codeBlock: '',
	lineCount: '',
	init: function() {
		// Store code blocks
		this.codeBlocks = $("pre code");
		// Loop through each code block
		(this.codeBlocks).each(function() {
			// Store code block reference
			codeHandler.codeBlock = $(this);
			// Make any adjustments to actual code text
			codeHandler.codeHTML(codeHandler.codeBlock);
			// Get line count
			codeHandler.lineCount = 1; codeHandler.getLineCount(codeHandler.codeBlock);
			// Display the line numbers
			codeHandler.printLineNumbers();
		});
	},
	// Edit text in given elm
	codeHTML: function() {
		// Remove initial extra line breaks
		this.codeBlock.firstChild.nodeValue = this.codeBlock.firstChild.nodeValue.replace(/^[\s]*[\n\r]/g,'');

		// Remove trailing extra line breaks
		this.codeBlock.lastChild.nodeValue = this.codeBlock.lastChild.nodeValue.replace(/[\n\r]+[\s]*$/g,'');
	},
	// Count the line breaks in a given element
	getLineCount : function(elm) {
		// Loop through code block elements recursively, counting line breaks
		for (var i in elm.childNodes) {
			var kid = elm.childNodes[i];
			if (kid.nodeName) {
				if (kid.hasChildNodes()) codeHandler.getLineCount(kid);
				if (kid.nodeType == 3) {
					if (kid.nodeValue.match(/[\n\r]/g)) {
						codeHandler.lineCount += kid.nodeValue.match(/[\n\r]/g).length;
					}
				}
			}
		}
	},
	// Print line number list in code block
	printLineNumbers : function() {
		// Create a list for the line numbers
		var lineWrapper = document.createElement('ul');
		lineWrapper.className = 'lineNumbers';
		(this.codeBlock).appendChild(lineWrapper);
		// Add a list item for every line
		for (var i = 1; i < (this.lineCount+1); i++) {
			var line = document.createElement('li');
			line.appendChild(document.createTextNode(i));
			lineWrapper.appendChild(line);
		}
	}
};
// Call the initialization function as soon as the DOM is loaded
DOMAssistant.DOMReady(codeHandler.init);

I wanted to start simple, so for the time being the only thing the script really does is remove beginning and ending carriage returns, and adds line numbers. The line numbers are specified and positioned in a different block, so the user should be able to highlight and copy the code without touching them.

The CSS does the majority of the beautifying:


pre code {
	display: block;
	padding: 10px 10px 10px 30px;
	line-height: 22px;
	position: relative;
	border: 1px solid #ccc;
	overflow: auto;
	width: 91%;
	white-space: pre;
}
ul.lineNumbers {
	position: absolute;
	top: 8px;
	left: 0;
	margin: 0;
	padding: 0;
}
ul.lineNumbers li {
	border-right: 1px solid;
	list-style-type: none;
	line-height: 18px;
	margin: 4px 0;
	padding: 0 4px;
}

The CSS does several things:

  • Handles the overflow so lines don’t wrap or extend outside of the code block. (Handling overflow in IE is extremely annoying… Read the expanding box problem, rendering scrollbars inside vs. outside the box, other google results.)
  • Positions the line numbers absolutely at the top left of the code block and pads the left of the code so there is no overlap.
  • Ensures equivalent line heights for the line numbers and the code so they line up.
  • Background, borders, etc.

I’ll probably revisit block-level code snippets again in the future, but for the time being this solution makes it extremely easy to add code to the site, and should be useful enough for the user.

About

Not Just a Hat Rack (NJHR) focuses on best practice solutions for problems you’ll encounter during a typical site build. There’s an emphasis on new technology when possible (HTML5, CSS3, etc.), but all suggested solutions will work cross-browser, quickly and efficiently. more »

I'm Andrew Church, an aspiring web developer currently living and working in Washington, DC. I’ve been employed as a professional developer since 2004, when I graduated with a degree in Information Sciences & Technology from Penn State University. I'm particularly interested in front-end web development technologies (HTML, CSS, JavaScript), but I do have experience with the entire site build process. « less

Tweets

Delicious