ScriptOnly - The Opposite of a NOSCRIPT
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.