Simple jQuery Solution To A Simple Problem
When building out the Viget Extend blog, I really wanted to give some special attention to the real meat of the content: the code blocks.
After looking at some other dev blogs, it seemed like there were two types: Big Ol' Blocks and Little Snippets. The big ones deserved the full treatment: Scrollbars, full colorization, line-numbering, all of it. We accomplished this with SyntaxHighlighter, a cool JS file that handles all this work. The developers just need to slap a name="code" on their pre tags, and SyntaxHighlighter formats and colors everything properly. (View an example of the treatment)
The shorter code snips were a little trickier. We didn't want them to linebreak automatically (like code here at Inspire does) for accuracy reasons, but we didn't want to apply scrollbars to something as simple as a single line of code. The solution was a quick jQuery idea: Expanding the code block on hover, so they grow to the full width of the page.
$("pre").hover(function() {
$(this).animate({ width: "765px"}, 250);
}, function() {
$(this).animate({ width: "437px" }, 250);
});
This was all right for a while, but soon we began running into posts like this one, where one or more of the blocks just didn't need the expanding treatment. In these cases, the jQuery effect was just annoying.
For a while, I just let it be, but this morning the answer hit me: We could probably do something to get the width of the code block's contents, and trigger the effect based on whether not the content width exceeded the block's. The first part was just setting up a variable to get the widths of both the block and the block's contents:
var contentwidth = $(this).contents().width(); var blockwidth = $(this).width();
But this isn’t quite enough, because the ”contents” traversal method needs a definite element to grab, and the pre tags only contained plain text. We needed to add an inline element to wrap the text:
$("pre").wrapInner("<span></span>");
This wraps the inside of pre tags with spans, but adds a problem: Now we’ve got errant spans showing up in our bigger, SyntaxHighlighted blocks. So we need a quick selector fix:
$("pre:not([name='code'])").wrapInner("<span></span>");
All better. The last step is to put it all together, and add the if statement that’s going to test whether the block expands or not:
$("pre:not([name='code'])").wrapInner("<span></span>");
$("pre").hover(function() {
var contentwidth = $(this).contents().width();
var blockwidth = $(this).width();
if(contentwidth > blockwidth) {
$(this).animate({ width: "765px"}, 250);
}
}, function() {
$(this).animate({ width: "437px" }, 250);
});
For an example of the finished effect, and the width discrimination, see Testing For HTML Tags in Rails Plugins.
Interesting take on the code blocks. I’m currently working on a site that will have a lot of code, and I’m planning on using a lightbox-style implementation, but I like the expanding width idea as well.
You could also have used the jQuery Dimensions plugin (which is included in jQuery 1.2.6) and used some of its methods (innerWidth, outerWidth, scrollLeft) to possibly eliminate the need for the additional spans.
re: Dimensions, thanks for the tip, I hadn’t seen that plugin or the news about the integration with 1.2.6. Cool!
If you don’t need the animation, wouldn’t it be enough to set overflow to normal, then on hover set it to visible?
@Thomas yes, I think I’d seen a solution somewhere that did that with CSS. It was a little more fun to animate it, though.
really interesting and useful post!
Thanks you a lot!
That’s really neat.
This comment is a digression from your feature but hope it helps: you could go one step further and wrap it into a plugin using something like this:
$.fn.expandableSyntaxHighlighter = function() {
return $(this).each( function() { /* your code here */ } );
}
Then anyone can use it e.g. $("pre").expandableSyntaxHighlighter();
(In your plugin version, where you reference $("pre"), you’d use $(this), etc.)
Of course, it st even possible for people then to use other elements and you can pass in options too if you want to be fancy.
I really like jQuery and am slowly finding that any solution I make like yours I wrap it into a plugin. If it helps, this is a useful pattern for writing plugins to make them take in options etc:
http://www.learningjquery.com/2007/10/a-plugin-development-pattern
Hi, how can i make this for a specific div?
Abras, you should be able to make it work for any block-level element you select using javascript. For example, you could just replace “pre” with “div#yourid”, to select a div with a specific ID.
Hello,
When i implemented syntax highlighting with the custom bbcode system i wrote for coderprofile.com i used two approaches. Geshi for the syntax highlighting (complete with a cache) since i found the JS version of syntax highlighting very annoying, slow, and if JS was disabled, no highlighting occured. So geshi was added.
Then, line numbering was the next problem. If i used CSS when selecting the code and copying it the line numbers would go with it! So i did two line numbering systems.
1. CSS version that when clicked, turned to a textarea of the same height which allowed people to copy and paste the code with all tabs etc. Line breaks are ok with this solution making it good for snippets.
2. Table version that highlights the line your mouse is over, and when you select text to copy and paste it the line numbers are not included in the pasted code (the disadvantage is there can be no line breaks and the tabs are converted to spaces). Full screen code works well with this solution.
The next problem is what to do when you have a huge code that is 2000px wide and 2000px in height? Well… the source code loads as normal, then using Prototype i get the height of the code and the width and if it’s bigger than X/Y then the css converts to overflow:scroll; and the width and height is adjusted. A link then appears to allow the user to view the code in full height if needed. For short snippets, the css oveflow property is simply left alone so no scroll bars appear.
That was my solution that i have been thinking of for the past year and a half!
Example 1 (table line numbers): http://www.coderprofile.com/networks/source-codes/476/weighted-picking-of-urls-affiliates
Example 2 (auto adjusting scroll bars): http://www.coderprofile.com/networks/discussion-forum/1638/bbcode-function-help-recursion-regex
Kind regards,
Scott
Scott, that’s a great solution! I’ll need to play around a little with Geshi, it definitely looks like a better solution than using JS for syntax highlighting.
Browsing various posts on the Extend blog, I notice that some blocks expand far more than needed, while some don’t quite get wide enough to view the entire length of the line(s).
Have you considered, instead of expanding to a blanket-preset 765px across the board, using the dimension of the contents? You calculate the ‘contentwidth’ on the fly; it seems like you could easily add a small padding and then use that value in the animation, which would ensure that each block expands enough (but only as much as necessary) to view the entire bit of code…
Next entry: Switching Mindsets: From WordPress to ExpressionEngine
Previous entry: Google Now Indexing Flash Content

Recent Comments
Hi Doug!
I just want to print this article :) But the print version is yet to be fully polished. I hope you guys spend sometime :) Viget inspire is a really nice resource for me....
- Lance on 'What To Expect When You're Expecting CSS/HTML Handoff'.
- Erik Wallace on 'What To Expect When You're Expecting CSS/HTML Handoff'.
- Jonathan on 'Switching Mindsets: From WordPress to ExpressionEngine'.
Subscribe to Comments RSS