blog comments 0 del.icio.us bookmarks 0 diggs 0 Google results 1

3.4
PostRank

ScriptOnly - The Opposite of a NOSCRIPT

Alex Papadimoulis' Periodically Updated, Syndicated Website From Alex Papadimoulis' Periodically Updated, Syndicated Website, 9 months ago, 0 views

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.

comments

No comments yet.

You must be logged in to add your own comment.