Despite all of the advances in client-side scripting, the wonderful JavaScript libraries like Prototype and Scriptaculous, and the ease of writing AJAXy code in ASP.NET, there’s still one aspect of modern web development that can be a complete pain in the butt: accessibility for users without JavaScript. If you’re lucky – perhaps you’re developing an Intranet application, or the like – a simple <noscript>Error: This application requires JavaScript enabled</noscript> is all it takes. But other times, you need to take that extra step and make it work for those with and without JavaScript enabled.
There’s a lot of ways that this can be accomplished, but one of the more popular ways is with the SCRIPT/NOSCRIPT combo...
<script type="text/javascript">
document.write('Only users with JavaScript will see me.');
</script>
<noscript>
Only users without JavaScript will see me.
</noscript>
While this works fine in a lot of scenarios, it can get especially tricky when you want to put server-side controls on the SCRIPT side of things. A lot of developers resort to something like this...
<div id="javaScriptOnly" style="display:none">
Only users with JavaScript will see me.
<asp:LinkButton runat="server" ... />
</div>
<div id="noJavaScript" style="display:block">
Only users without JavaScript will see me.
<asp:Button runat="server" ... />
</div>
<script type="text/javascript">
document.getElementById('javaScriptOnly').style.display = 'block';
document.getElementById('noJavaScript').style.display = 'none';
</script>
... and of course, things quickly get much uglier once you do this in the real world.
One solution that I use is a simple, custom-control called ScriptOnly. It works just like this...
<inedo:ScriptOnly runat="server">
Only users with JavaScript will see me.
<asp:LinkButton runat="server" onClick="doSomething" ... />
</inedo:ScriptOnly>
<noscript>
Only users without JavaScript will see me.
<asp:Button runat="server" onClick="doSomething" ... />
</noscript>
JavaScript users see a LinkButton, while non-JavaScript users see a plain old submit button. What’s neat about this technique is that you can put any type of content - server-controls, html, script tags, etc - and that content will only be displayed for JavaScript users. In essense, it works like a reverse NOSCRIPT tag.
Behind the scenes, ScriptOnly is a very simple control...
[ParseChildren(false)]
public class ScriptOnly : Control
{
protected override void Render(HtmlTextWriter writer)
{
//Render contents to a StringWriter
StringWriter renderedContents = new StringWriter();
base.Render(new HtmlTextWriter(renderedContents));
//write out the contents, line by line
writer.WriteLine("<script type=\"text/javascript\">");
StringReader sr = new StringReader(renderedContents.ToString());
while (sr.Peek() >= 0)
{
// This could be optimized to write on one line; but
// I've found this makes it easier to debug when
// looking at a page's source
writer.WriteLine(
"document.writeln('{0}');",
jsEscapeText(sr.ReadLine()).Trim());
}
writer.WriteLine("</script>");
}
private string jsEscapeText(string value)
{
if (string.IsNullOrEmpty(value)) return value;
// This, too, could be optimzied to replace character
// by character; but this gives you an idea of
// what to escape out
return value
/* \ --> \\ */
.Replace("\\", "\\\\")
/* ' --> \' */
.Replace("'", "\\'")
/* " --> \" */
.Replace("\"", "\\\"")
/* (newline) --> \n */
.Replace("\n", "\\n")
/* (creturn) --> \r */
.Replace("\r", "\\r")
/* </script> string */
.Replace("</script>", "</scri'+'pt>");
}
}
When "pre-reistered" in your web.config, it works just as well as the NOSCRIPT tag.