We probably all know about the annoying captcha images that a lot of blogs uses for separating humans from machines (spam robots). I use a captcha image to avoid comment spam on this blog because I get a lot, but I really don’t like to use it. I don’t like the fact that it makes is more difficult for my visitors to write comments, which is my only way of measuring the quality of the individual posts.

What I want is an invisible unobtrusive captcha method that automatically makes sure the user is human. So I wrote a simple method that does just that. It works by adding a small JavaScript to the page that adds a hidden form field when the form is submitted. The value of the hidden field must be the same as a server-side variable to validate. There must also be a property that returns a Boolean value that indicates whether or not the user is human.

/// <summary>

/// Initializes the captcha and registers the JavaScript

/// </summary>

private void InititializeCaptcha()

{

  if (ViewState["captchavalue"] == null)

  {

    ViewState["captchavalue"] = Guid.NewGuid().ToString();

  }

 

  System.Text.StringBuilder sb = new System.Text.StringBuilder();

  sb.AppendLine("function SetCaptcha(){");

  sb.AppendLine("var form = document.getElementById('" + Page.Form.ClientID + "');");

  sb.AppendLine("var el = document.createElement('input');");

  sb.AppendLine("el.type = 'hidden';");

  sb.AppendLine("el.name = 'captcha';");

  sb.AppendLine("el.value = '" + ViewState["captchavalue"] + "';");

  sb.AppendLine("form.appendChild(el);}");

 

  Page.ClientScript.RegisterClientScriptBlock(GetType(), "captchascript", sb.ToString(), true);

  Page.ClientScript.RegisterOnSubmitStatement(GetType(), "captchayo", "SetCaptcha()");

}

 

/// <summary>

/// Gets whether or not the user is human

/// </summary>

private bool IsCaptchaValid

{

  get

  {

    if (ViewState["captchavalue"] != null)

    {

      return Request.Form["captcha"] == ViewState["captchavalue"].ToString();

    }

 

    return false;

  }
}

Examples of use

To use the captcha you have to call InitializeCaptcha from the Page_Load handler. Then just check the IsCaptchaValid property before you save the comment.

protected void Page_Load(object sender, EventArgs e)

{

  InititializeCaptcha();

}

 

/// <summary>

/// Handles the submit buttons onclick event

/// </summary>

void btnSave_Click(object sender, EventArgs e)

{

  if (IsCaptchaValid)

  {

    SaveComment();

  }
}

Since ASP.NET 1.0 you had the ability to toggle the visibility of any HTML tag with the runat=”server” attribute. The only prerequisite is to add the runat=”server” and ID attributes, but that is not always possible or desirable. If you add elements dynamically to the DOM tree using JavaScript it is impossible and if you have a lot of elements to toggle individually it may not be desirable.

A way to accomplish the visibility toggle of non-server elements is to use JavaScript to do the trick, but we also want to be able to do it server-side from the code-behind. It can be done by adding a few methods to the Page, master page, user controls or a custom base page. We need a method to show elements and one to hide elements and then a private method to write out the correct JavaScript.

From the code-behind we can then call the methods to show and hide UI elements as shown in the following code snippets.

HideElements("name", "email");

ShowElements("name");

The JavaScript being generated from the above code snippets looks like this:

<script type="text/javascript">
<!--
document.getElementById('name').style.display='block';
document.getElementById('email').style.display='none';
// -->
</script>

The Code


protected override void OnPreRender(EventArgs e)

{

  base.OnPreRender(e);

  RegisterScript();

}

 

/// <summary>

/// Displays a hidden element on the page.

/// </summary>

/// <param name="id">The Id's of the elements to show.</param>

protected virtual void ShowElements(params string[] id)

{

  foreach (string s in id)

  {

    Elements[s] = true;

  }

}

 

/// <summary>

/// Hides a hidden element on the page.

/// </summary>

/// <param name="id">The Id's of the elements to hide.</param>

protected virtual void HideElements(params string[] id)

{

  foreach (string s in id)

  {

    Elements[s] = false;

  }

}

 

/// <summary>

/// Writes the JavaScript to the page if any elements

/// have been added to the collection.

/// </summary>

private void RegisterScript()

{

  if (ViewState["Elements"] != null && Elements.Count > 0)

  {

    StringBuilder sb = new StringBuilder();

 

    foreach (string key in Elements.Keys)

    {

      string display = Elements[key] ? "block" : "none";

      sb.AppendFormat("document.getElementById('{0}').style.display='{1}';{2}", key, display, "\n");

    }

 

    ClientScript.RegisterStartupScript(this.GetType(), "toggleelements", sb.ToString(), true);

  }

}

 

/// <summary>

/// The collection of elements to show or hide.

/// </summary>

private Dictionary<string, bool> Elements

{

  get

  {

    if (ViewState["Elements"] == null)

      ViewState["Elements"] = new Dictionary<string, bool>();

 

    return ViewState["Elements"] as Dictionary<string, bool>;

  }

}

Implementation

Download the BasePage.cs below and add it to the App_Code folder. Then make sure that the pages where you want this functionality inherits from BasePage instead of System.Web.UI.Page. That’s all you need to do to make it work. You can also take the method used in the BasePage.cs and add them manually to the page, master page or user control.

Download

BasePage.zip (0,81 KB)