A List Apart

Menu
Issue № 183

Dynamic Text Replacement

by Published in CSS, HTML, JavaScript, The Server Side, Graphic Design, Typography & Web Fonts, Accessibility · 280 Comments

Text styling is the dull headache of web design. There are only a handful of fonts that are universally available, and sophisticated graphical effects are next to impossible using only standard CSS and HTML. Sticking with the traditional typefaces is smart for body text, but when it comes to our headings — short, attention-grabbing blocks of text — it would be nice to have some choice in the matter. We’ve become accustomed to this problem and we cope with it either by making the most of the few fonts we have, or by entirely replacing our heading-text with images.

Most sites that replace text with images do so using hand-made images, which isn’t so terrible when there are a set number of headings, but it quickly becomes unmanageable on a site that is updated several times per day. However the replacement is performed, each image needs to be bound to the text it is replacing. That binding usually manifests itself as an <img> tag, an embedded style sheet, or a custom id attribute. And over time, through layout changes and redesigns, that binding needs to be managed by someone.

We can forget all that nonsense. No more <img> or <span> tags, no more id attributes or wasted time in Photoshop, and no more messy CSS hacks. Using JavaScript and PHP, we can generate accessible image-headings using any font we like. And we don’t have to change the structure of our HTML or CSS at all.

View the demo to see Dynamic Text Replacement in action. Then read on to find out how you can add the same functionality to your site.

PHP

This small PHP script (available here) will deliver a dynamic PNG image to our browser whenever it’s asked to. Before we set it to work, though, it needs to be customized for your specific purpose. The first seven lines of code in the script serve this purpose:

  $font_file = 'font.ttf' ;
  $font_size = 56 ; 
  $text_color = '#ffffff' ;
  $background_color = '#000000' ;
  $transparent = true ;
  $cache_images = true ;
  $cache_folder = 'cache' ;
  • The $font_file variable must be set to the local path (not the URL) of a True Type (TTF) or Open Type (OTF) font on your web server. This is the font that your images will be created with; you’ll need to upload it to the web server from your own computer.
  • $font_size, unsurprisingly, refers to the size of the font in points.
  • $text_color and $background_color are hexadecimal color codes that indicate the color of the text, and color of the image’s background, respectively.
  • When $transparent is set to true, the edges of the image’s text will be blended with the $background_color to prevent anti-aliasing, and the actual background color will be entirely invisible.
  • By setting $cache_images to true, and $cache_folder to the local path of a writable directory on your web server, this script will save each image that it creates, caching them for later use. This can significantly speed up delivery of images to your visitors, and is particularly important on shared, or high-traffic servers.

To install this script, upload it to a web server that is configured with PHP support. Specifically, you will need PHP version 4.3 or higher, compiled with support for the GD graphics library, 1.6 or higher. If none of that means anything to you, email those requirements to your web host and they’ll tell you if your server is compatible.

Although we used PHP to construct the images in this implementation, your website does not need to be actively using PHP to take advantage of this technique. Regardless of how you generate your HTML pages, whether they’re edited by hand or through a CMS, you can use this technique as long as you can insert a [removed] tag into the <head> of your documents. I’ll explain that detail further, below.

Please note that what can be done with PHP can often be done with other tools as well. Perl, ASP, Java servlets, and other server-side programming languages would also be good candidates for generating custom images. PHP is an excellent choice because of its wide availability, platform independence, and easy learning curve. Consider the alternatives if you require something that PHP doesn’t provide or if you choose to create your own image-generation code from scratch. It might be simpler, however, to just adapt the PHP code presented here.

One thing that our customization of the script did not include was the text that it should use to generate our custom images. That’s because the text that we’re using to produce these images will be passed to the script via its URL. For example, loading the URL heading.php?text=URLs Are Fun will produce a graphic that reads “URLs Are Fun.” And they are. But we’ll never need to write them ourselves, because JavaScript will do it for us.

JavaScript

Download the JavaScript source file here.

This technique borrows heavily from Peter-Paul Koch’s JavaScript Image Replacement (JIR) method. The premise of JIR is very simple: Many CSS coders exploit browser bugs to hide CSS styles from those browsers. These hacks are akin to limited conditional statements in their code, turning CSS into a crude programming language. Rather than using this language-of-bugs to compensate for browser differences, Koch and others have put forth the idea that JavaScript — an actual scripting language — could more intelligently and accessibly perform the same task. This is wonderful for our purposes because JavaScript also gives us more flexibility. Specifically, we’ll be using it to replace text with images that don’t even exist yet.

When the page first loads, the script will attempt to load a small (1x1 pixel) test image. If this test is successful, we can conclude that the visitor’s browser supports the display of images, otherwise it would not have wasted bandwidth downloading it. This is the crux of JIR: By testing for image support, we can immediately determine whether or not our visitors have any use for stylized headings. If they do not, the script will stop right there.

Assuming the visitor’s browser supports images, the script then waits until the page is entirely finished loading, because it can’t replace text that hasn’t been downloaded yet. Once the HTML is finished loading, our script will search it for specified elements (<h2>, <span>, etc.) and replace the text inside of them with an <img> tag. This dynamic <img> tag has its alt attribute set to the original text, and its src attribute set to the URL of the PHP script that we just installed. The PHP script then sends back a custom PNG image, and voila: custom headings.

Weighing in at a hefty eight kilobytes, there’s a lot of stuff going on in this corner of the ring, but there are only two lines that need to be customized before the script will work.

  replaceSelector("h2","heading.php",true);
  var testURL = "test.png";

The replaceSelector function accepts three parameters: The first is the CSS-style selector that indicates which elements should be replaced. This selector may be almost any valid CSS selector, including id, class, element and attribute selectors.

The second parameter is the URL of our custom PHP script.

The third parameter is a true/false value that indicates whether word-wrap should be turned on for this replacement. When this flag is set to true, headings are broken into multiple images, one for each word. When it is false, only a single, non-breaking image is generated for each heading.

replaceSelector should be called once for each group of elements you want replaced by a custom image. The URLs in these lines can be absolute (http://…) or relative to our HTML file (path/filename).

The testURL variable needs to be set to the URL of a small (1x1 pixel) test image.

Once these lines are set correctly, you can upload the JavaScript file to your web server, and apply it to your web pages by adding the following line to their <head> tags.

  [removed]
  [removed]

Make sure the src attribute in that line points to the location that you uploaded the JavaScript file to.

That’s all that’s required to get dynamic text replacement working; we can stop right there if we want to. But there are a few optional improvements we may want to make before calling it quits.

Print Versions

As previously seen here in ALA, many sites are now employing specialized printer style sheets to give their visitors better hard copies of their content. In many cases this involves reversing the process of image replacement so that the printed copy of a page uses actual fonts rather than graphics, which often look poor on high-resolution printers. Unfortunately, JavaScript falls short of solving this problem. Once we’ve replaced our text with an image, it’s impossible to reverse that process specifically for printing purposes, so we need to find another solution.

Instead of trying to reverse our replacement process, we can do a little planning ahead. Along with inserting an <img> tag into our headings, we can also insert a <span> tag that contains the original heading text. And we can set that span’s display property to none, so that it doesn’t show up onscreen. Now we have two copies of our original text: One in a visible image, and one in an un-displayed span. By giving each of these elements identifiable class attributes (“replacement”, and “print-text,” respectively), and by adding in a print-specific style sheet, we can swap their display properties when they’re printed.

The following style sheet (download a sample CSS file here) could be used to generate an appropriate print version of your page:

  span.print-text {
    display: inline !important;
  }
  img.replacement {
    display: none;
  }

Once we’ve uploaded this style sheet -to our web server, we only need to change two lines in our JavaScript to make it work:

  var doNotPrintImages = false;
  var printerCSS = "replacement-print.css";

By setting doNotPrintImages to true, and printerCSS to the URL of the print style sheet we just created, the script will automatically insert the appropriate CSS <link> into our document’s <head> tag.

Flicker Free

Because our script can’t begin replacing elements until after the entire document has loaded, there will often be a quick flash of unstyled content as the browser waits for the replacement process to begin. This is less of a problem than it is a minor annoyance, but since it’s avoidable we might as well fix it. With the help of another small style sheet, we can do just that.

Before the document’s body begins loading we can dynamically insert a style sheet that will hide these elements entirely. Since linked CSS files are applied even as the document is rendering, no content will be visible during this period. Once our replacement technique is finished executing we can disable this style sheet and our newly stylized headings will be visible once more.

For example, if your page was set up to replace <h2> tags, the following style sheet (available here) would hide them until our replacement technique was finished:

  h2 {
    visibility: hidden;
  }

There is a slight problem with this approach, however. Our entire technique depends on the loading of a test picture to indicate whether the browser supports images. If the image never loads, our technique will never activate. And if our technique never activates, the style sheet that hides our unstyled headings will never be deactivated. Because of this, visitors who have disabled image support in their browsers, but who are still capable of using JavaScript and CSS, will see nothing but empty space where our headings should have been.

We’ll do our part to improve this poor minority’s already-difficult browsing experience by adding a short timeout value to the script. If the test image hasn’t been successfully loaded after one or two seconds (or however long you see fit), the script will automatically disable this style sheet, and the headings will reappear. Those one or two seconds are a slight inconvenience for this exceptionally rare person, but it solves the flicker problem for the other 99.99% of our visitors. What’s important is that we maintain accessibility for everyone.

To enable this optional customization, and to remove the brief flash of unstyled content, you must edit three lines in the JavaScript source:

  var hideFlicker = false;
  var hideFlickerCSS = "replacement-screen.css";
  var hideFlickerTimeout = 1000;

Set hideFlicker = true, and hideFlickerCSS to the URL of the CSS file that you just created to hide your headers.

hideFlickerTimeout should be set to the maximum number of milliseconds (i.e. 1/1000 seconds) that the script will let pass before disabling that style sheet.

Notes and Suggestions

Older versions of Mozilla, including Netscape 6.2, contained a bug where the browser would download images even if the user had instructed it not to display them. This obviously made no sense, and has been fixed since version 1.4. Although this technique will normally work without any problems in these browsers, it will incorrectly diagnose image support and fail when visitors using these browsers have images disabled. I don’t consider this overwhelmingly rare occurrence to be a serious drawback, but it’s worth noting for completeness. There is currently no workaround for this problem.

Use this technique with a translator service, like Google or Altavista’s Babelfish. As long as your font supports the foreign character set, the dynamic images will be translated as well.

The text that you replace does not have to be inside of a heading tag (<h1>, <h2>, etc.); it can be any element on the page. With some fairly simple adjustments, and some manipulation of float values, this technique could produce dynamic drop-caps for any paragraph you apply it to.

You can also replace <a> tags, giving your page stylized hyperlinks, although getting rollovers to work would require more customization.

Instead of replacing content with dynamically generated <img> tags, this technique could avoid using PHP altogether and instead insert dynamic Flash animations.

Acknowledgements

Peter-Paul Koch, for his JavaScript Image Replacement technique.

Simon Willison, for this <a href=“http://simonwillison.net/2003/Mar/25/getElementsBySelector/” title=“getElementsBySelector”>getElementsBySelector function.

Stuart Langridge, for unobtrusive JavaScript techniques.

About the Author

280 Reader Comments

Load Comments