I've employed a bit of progressive JS here on the blog to make it easier for readers to copy the blocks of code that I post. Go ahead, try it: click within the code block, and the entire thing is selected. Makes for pretty easy copying, rather than having to carefully select just the text inside the box...
// I'm code!! Copy me!!
$ hello world
// w00t...
The way it works
Here's how it works: I've defined a function which goes over every single <code> element on the page. For each code element, a textarea element is created and inserted into the DOM tree directly before the corresponding code element. Once a textarea is added to the DOM, it is hidden via CSS. The textarea is given, as child nodes, recursive clones of code's DOM child nodes. Then, the onclick event of the code element and the onblur event of the textarea are set to different functions. The code's onclick function hides the code element, un-hides the textarea element gives the textarea the focus, and selects the textarea's contents. The textarea's onblur function hides the textarea and un-hides the corresponding code element.
It's possible to have the script do this for all code elements on the page, but I've implemented it to only target those code elements to which I've given a class name of "copyable".
The CSS
The hiding-un-hiding tricks aren't possible without CSS. Well, they're possible, but such an implementation would make the JS much messier. Here I've used CSS to similarly style both code elements and elements with a class name of "codeenhance" (which is used to identify the inserted textareas).
.codeenhance, code {
width:430px;
color:#000;
display:block;
background:#eee;
padding:0 10px;
padding-bottom:1em;
margin:0;
border-width:3px 1px !important;
border:#99f solid;
overflow:auto;
line-height:1.4em;
font-family:monospace;
font-size:1.1em;
}
.codeenhance, .copyable {
height:250px;
}
code {
white-space:pre;
}
.codeenhance {
padding:0;
}
.codeenhance-off {
display:none;
}
The first declaration is the main styling for the code elements. The height specification for .codeenhance and .copyable fix the height of copyable code blocks, so your 159 lines of copyable JS don't take up too much space. I've left the height out of the main declaration, to allow the other code blocks to expand/retract to their contents' size. The white-space declaration for code elements maintains the whitespace as you typed it. The padding declaration for .codeenhance fixes an interesting jumping issue with the textareas. And the display declaration for .codeenhance-off is what hides the appropriate items.
The Javascript
Here's the JS. The explanations are in the code comments ;)
// Enable click-select on code elements
function enhanceCode() {
// Get all code tags
var codes = document.getElementsByTagName("code");
// loop over code tags
for (i=0; i<codes.length; i++) {
// working only on tags of class "copyable"
if (codes[i].className.match('copyable')) {
// for each tag, create a new textarea
var text = document.createElement("textarea");
// copy the code's children over
for (j=0; j<codes[i].childNodes.length; j++) {
// special code for Blogger, to take care of extra br elements
if (codes[i].childNodes[j].nodeName == "BR")
text.appendChild(document.createTextNode('\r\n'));
else
text.appendChild(codes[i].childNodes[j].cloneNode(true));
}
// set the initial classname
// (use .className rather than .setAttribute('clas', 'blah') because IE6
// doesn't like the latter
text.className = "codeenhance codeenhance-off";
// setup the onblur event
text.onblur = decodex;
// insert the textarea before the code
codes[i].parentNode.insertBefore(text, codes[i]);
// setup the onclick event
codes[i].onclick = codex;
}
}
}
// this = code element; this.previousSibling = textarea element
function codex() {
// show the textarea
this.previousSibling.className =
this.previousSibling.className.replace(/ ?codeenhance\-off/, "");
// hide the code
this.className = this.className+" codeenhance-off";
// focus and select the textarea
this.previousSibling.focus();
this.previousSibling.select();
}
// this = textarea; this.nextSibling = code
function decodex() {
// show the code
this.nextSibling.className =
this.nextSibling.className.replace(/ ?codeenhance\-off/, "");
// hide the textarea
this.className = this.className+" codeenhance-off";
}
window.onload = function() {
// run the enhancement /after/ the window has loaded
enhanceCode();
}
No comments:
Post a Comment