Close and Go BackBack to Viget

Simple jQuery Solution To A Simple Problem

Doug Avery
Doug Avery, ON THE TOPIC OF Javascript
Jul03 11

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.

Aaron Kuzemchak said on 07/03 at 11:38 AM

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.

Doug Avery said on 07/03 at 11:43 AM

re: Dimensions, thanks for the tip, I hadn’t seen that plugin or the news about the integration with 1.2.6. Cool!

Thomas Thomassen said on 07/03 at 12:06 PM

If you don’t need the animation, wouldn’t it be enough to set overflow to normal, then on hover set it to visible?

Doug Avery said on 07/03 at 02:14 PM

@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.

Tomas said on 07/05 at 06:14 PM

really interesting and useful post!
Thanks you a lot!

Anup said on 08/09 at 10:23 AM

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

Abras said on 10/17 at 07:12 PM

Hi, how can i make this for a specific div?

Doug Avery said on 10/20 at 07:26 PM

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.

Scott Thompson said on 11/26 at 07:51 AM

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

Doug Avery said on 12/01 at 10:49 AM

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.

Michael said on 12/02 at 10:18 AM

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…

Name:

Email:

URL:

Not a robot? Prove it by entering the word below.


Some HTML (strong, a, em) is allowed.

Notify me of follow-up comments?

We're The Designers

at Viget Labs. We write about design news, trends, techniques, buildout, inspiration, CSS, and our projects.

Know Someone?

Viget's hiring a Senior Ruby on Rails (RoR) Developer. Find out more »

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....

Subscribe to Comments RSS RSS

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.