//Monday, 23 June 2008

CSS Image Replacement Techniques

Web designers like to use images for text because they can use non-standard fonts and have them nicely anti-aliased. There are a number of techniques out there that allow you to make this kind of image text accessible and search-engine friendly.

One technique I have seen used is a negative text-indent (see http://phark.typepad.com/phark/2003/08/accessible_imag.html).

For example, if you wanted to use an image for your h1 tag you could write something like this:


<h1 id="hello-world">Hello World</h1>

and then in your CSS you set a background-image for the hello-world element and set text-indent to a very large negative number so that it's rendered off the screen.

#hello-world
{
width: 100px;
height: 25px;
background: url(helloworld.gif) no-repeat;
text-indent: -9999px;
}

Most users will see the image but without the style sheet the h1 renders as normal. So search engines and screen readers should read your page in the expected way.
Note: I have read in articles that this technique does indeed work with the JAWS screen reader and Google seems to index the text OK so I guess it doesn't view it as a "black-hat" technique.

There is a flaw with this technique and that is that most browsers allow you to disable images. If you were to build a page where most of the text was replaced using the text-indent technique and someone did browse it with images turned off they would see an almost entirely blank page. Some people might consider this a moot point because after all who in the age of high speed internet access really disables image downloads? However, in my opinion there does seem to be something intuitively wrong with this technique. It just doesn't feel right to me having a load of text mysteriously floating somewhere off screen.

So, what's the alternative? Well, it seems that there are no perfect solutions to this problem. There are a number of different solutions that I've seen suggested. For example, if your image has a flat background and you can fit it in you can make the text small and hide it in the image. Then have a contrasting background color underneath so that it is visible with images turned off (http://mondaybynoon.com/2006/10/23/my-latest-take-on-image-replacement/). I've also seen an article (http://www.drunkenfist.com/304/2007/03/19/an-alernative-to-negative-text-indent-for-image-replacement-css/) that suggests using opacity in CSS to make the text transparent though this will only work in modern browsers and requires an additional element in your markup.

Another alternative that requires an additional element purely for presentational purposes is as follows:

<h1 id="hello-world">Hello World<span></span></h1>
#hello-world
{
position: relative;
width: 100px;
height: 25px;
overflow: hidden;
}
#hello-world span
{
display: block;
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 25px;
background: url(helloworld.gif) no-repeat;
}

The original explanation (I think) of this technique can be found here (http://levinalex.net/files/20030809/alternatefir.html). Again this doesn't feel right. Mainly because your markup is less semantic and your code become a lot more bloated when compared with the text-indent method. However this technique does work nicely if you do happen to have images switched off. Note: If you use a transparent gif then you'll be able to see the text because it's sitting underneath the image.

So which method should you use? I don't think there is a definitively right or a wrong answer because all techniques I have seen have some kind of drawback. Which method is right for you will depend on your target audience.

I've created a test page that demonstrates the techniques I have discussed in this article:

Examples of CSS Image replacement techniques

//Wednesday, 18 June 2008

Get the FCKEditor value from javascript

As a postscript to my previous post about customizing the Subsonic Scaffold I also had cause whilst building an admin interface to pull out the XHTML value from the editor in a Javascript function. This took a little bit of searching for so I thought I would post it here:

FCKeditorAPI.GetInstance("ctl00_phAdmin_Scaffold1_PageContent").GetXHTML(true);

Where ctl00_phAdmin_Scaffold1_PageContent is the ID of your FCKEditor control.

I found the answer here:

http://www.fckeditor.net/forums/viewtopic.php?f=6&t=9536&start=0&st=0&sk=t&sd=a

I guess I should have gone straight to the Javascript API docs but I tend to just Google questions like this. Since my first search was the title of this post hopefully this will help people to find the answer quicker than I did.

Customizing the Subsonic scaffold control

The Subsonic data access layer comes with a control called a Scaffold (the Scaffold concept comes from Ruby on Rails apparently) which allows you to quickly build a simple administration interface for your database tables.

The basic syntax is as follows:

<cc1:Scaffold ID="Scaffold1" runat="server" TableName="Products"></cc1:Scaffold>

Where SubsonicProvider is the name of the SubSonicService Provider from your web.config and TableName is the table you want to edit. The control handles rendering a gridview and a form for editing the fields in your table. By default the control will display all fields from the table in both the grid view and detail view. If you have a big table this quite quickly becomes unwieldy, particularly in the case of the grid view. The control provides two properties HiddenGridColumns and HiddenEditorColumns which allow you to specify which columns to hide. Place a comma-delimited list of the fields you want to hide in each property.

If you are providing an administration interface that allows your users to maintain content on their website then it is quite likely you will want to be able to present them with a XHTML editor for the appopriate fields. To achieve this I customized the original Scaffold control to include another property RichTextColumns. Again this is a comma-separated list that contains the fields in the table that you would like to present with a rich text editor.

My open source XHTML editor of choice is FCKEditor which is easy to configure and install and seems to create decent XHTML.

In the fields declaration I've added the following beneath the declaration of the other internal string lists:


private readonly List _richTextColumnList = new List();
In the BindEditor method between:




if(ctrlType == typeof(TextBox))
{
...
}
and




else if(ctrlType == typeof(CheckBox))
{
...
}
I've added the following:





else if (ctrlType == typeof(FredCK.FCKeditorV2.FCKeditor))
{
FredCK.FCKeditorV2.FCKeditor editor = (FredCK.FCKeditorV2.FCKeditor)control2;
editor.Value = reader[column.ColumnName].ToString();
}




Then in the GetEditorControl method in the section that deals with string fields I've added the part that creates the XHTML editor:



switch (col.DataType)
{
case DbType.Guid:
case DbType.AnsiString:
case DbType.String:
case DbType.StringFixedLength:
case DbType.Xml:
case DbType.Object:
case DbType.AnsiStringFixedLength:
if (Utility.IsMatch(colName, ReservedColumnName.CREATED_BY) Utility.IsMatch(colName, ReservedColumnName.MODIFIED_BY))
{
cOut = new Label();
}
else
{
if (_richTextColumnList.Contains(colName))
{
FredCK.FCKeditorV2.FCKeditor editor = new FredCK.FCKeditorV2.FCKeditor();
editor.ToolbarStartExpanded = false;
editor.ForcePasteAsPlainText = true;
editor.Height = 400;
editor.UseBROnCarriageReturn = true;
cOut = editor;
}
else
{

TextBox t = new TextBox();
if (Utility.GetEffectiveMaxLength(col) > 250)
{
t.TextMode = TextBoxMode.MultiLine;
t.Columns = 60;
t.Rows = 4;
}
else
{
t.Width = Unit.Pixel(250);
if (colName.EndsWith("guid"))
{
t.Text = Guid.NewGuid().ToString();
t.Enabled = false;
}
}
cOut = t;
}
}
break;

Then finally I've added the RichTextColumns property which works in a similar way to the other HiddenEditorColumns and HiddenGridColumns properties:



[DefaultValue(""), Description("A comma delimited list of column names which are displayed in the Editor using an XHTML editor."), Bindable(true), Category("Data")]
public string RichTextColumns
{
set
{
this._richTextColumnList.Clear();
foreach (string str in Utility.Split(value))
{
this._richTextColumnList.Add(str.ToLower());
}
}
}




And that's it! This has enabled us to quickly produce simple database administration interfaces for tables that store XHTML content.

SyntaxHighlighter