Custom jQuery selector for finding usable elements

Posted by Dan on Oct 15, 2009 @ 2:31 PM

While working on some code today, I faced a problem I've run into a few times in the past where I needed to find the first usable input element within a specific context. What I mean by "usable" is an element which is visible to the user and not disabled.

Finding the first usable input element can be handy when you're using tabbed form, as when the user changes tabs, you can place the focus in the first visible input element. In my use case, I was implement a "reset" on a form where the fields being displayed could vary. I wanted to automatically place the focus in the first visible field after the user triggered a "reset" of the form.

If you're using jQuery v1.3 (or higher) this custom selector is very simple, just add the following to your page:

// finds elements that are usable (i.e. visible on the screen and are not disabled)
$.expr[":"].usable = function (node, index, prop, nodes){
  var $n = $(node);
  // return if the element is viewable or not            
  return ($n.attr("disabled") !== true) && $n.is(":visible");
};

If you're using jQuery v1.2 (or lower) the code is slightly more complex because the ":visible" selector doesn't check with the parent elements to make sure they are all visible. So, in order to make our code work with jQuery v1.2 we'll need to first create a custom selector for which will find elements that are truly visible:

// finds elements that are viewable
$.expr[":"].viewable = function (node, index, prop, nodes){
  var r = false;

  function viewable(n){
    var $n = $(n);
    return (($n.css("display") !== "none") && ($n.css("visibility") !== "hidden"));
  }

  // if not usable, stop processing
  if( !viewable(node) ) return false;

  $(node).parents().each(function (){
    r = viewable(this);
    // if not viewable, stop processing chain
    return r;
  });

  // return if the element is usable or not            
  return r;
};

The new ":viewable" selector should pretty much behave the same way that the ":visible" selector does in jQuery v1.3.

The only difference in this version of the ":usable" selector is that we're going to use our custom ":viewable" selector instead of the ":visible" selector:

// finds elements that are usable (i.e. visible on the screen and are not disabled)
$.expr[":"].usable = function (node, index, prop, nodes){
  var $n = $(node);
  // return if the element is viewable or not            
  return ($n.attr("disabled") !== true) && $n.is(":viewable");
};

The ":usable" selector can be used in several ways:

// find all usable form elements
$(":input:usable")

// find the first usable form element
$(":input:usable:first")

While the most common usage of the selector will probably be :input:usable:first, I can see where occasionally you might want to get all the input elements visible to the user.

Hope this helps someone!

Categories: jQuery

2 Comments

  • Wouldn't this work?

    $(':input:visible:enabled').filter(':first')
  • yeah as of jQuery 1.3, <code>$("input:enabled:visible:first").focus();</code> would select the first visible input. And you could limit to to just text too with: <code>$("input:text:enabled:visible:first").focus();</code>



    <code>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                        "http://www.w3.org/TR/html4/loose.dtd">;
    <html>
    <head>
     <script src="http://code.jquery.com/jquery-latest.js">&...;

     <script>
     $(document).ready(function(){
        $("input:enabled").val("this is it");
        $("input:enabled:visible:first").focus();
     });
     </script>

    </head>
    <body>
        <form>
            <input name="email" disabled="disabled" />
            <input name="phone" style="display:none"/>
            <input name="id" />
            <input name="name" />
        </form>
    </body>
    </html>
    </code>

Comments for this entry have been disabled.