Salesforce: A better way to work with jQuery selectors and Visualforce Component Ids

Irregular Expressions

I get very sad when discussing this particular topic. There are a variety of ways of get Visualforce component Ids and using them in JavaScript but all of them keep me awake at night. Srsly. A commenter on one of my posts got me thinking about how we can do this better and I’ve come up with a way that I think is great. Hopefully you’ll agree.

This post means that my older posts here and here are now retired in favour of this method.

If the world was on the brink of nuclear war with no clear path to peace what could you count on to save the day? Regular Expressions of course. If a meteor the size of Pluto was about to crash into Earth and Bruce Willis was too old to land on it and blow it up what could we count on to rid us of the troublesome rock. Yes that’s right, Regular Expressions. I think you can guess where I’m going with this.

jQuery has the ability to understand very simple regular expressions in it’s attribute selectors. The full documentation can be found here.

To solve our particular problem however the code is simple:

[code language=”xml”]
<apex:page>
<head>
<style>
a,span{
display:block;
}
</style>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

<script>
jQuery(document).ready(function($){
$(‘#btn’).click(function(e){
e.preventDefault();

console.log(‘The following element was found when looking for an id of \’output1\’:’);
console.log($(‘[id$=output1]’)); /* Here’s where we’re grabbing the element. */
});
});
</script>

</head>

<apex:outputText value="She sells seashells by the seashore." id="output1"/>
<apex:outputText value="Peter Piper picked a pack of pickled peppers." id="output2"/>

<a href="" id="btn">Click me.</a>

</apex:page>
[/code]

The important part here is the selector $(‘[id$=output1]’) which says, “Find the id value that ends with ‘output1′”. This comes with a warning though! Do not duplicate the Visualforce Id that you give to your elements otherwise this piece of code will find all of them.

When I first wrote this post I used a selector extension library that gives you the full power of JavaScript-based regular expression but Ryan Fritts has rightly shown that the above will deal with 99% of use cases and is simpler. For those of you that need to deal with the extra 1% I’ve implemented a wrapper to regex selector as an example. It does exactly what jQuery is doing above and gives you access to the regex flags as documented here.

Thanks again Ryan!

24 thoughts on “Salesforce: A better way to work with jQuery selectors and Visualforce Component Ids”

  1. Woot. I inspired a revolution. I do like this although id extend it to return the jQuery object – as i can’t envision a usecase for just needing the component regex unless you’re querying it.

    function component(id) {
    return jQuery(‘:regex(id,\:’ + id + ‘([^\:]*)$)’);
    }

    Reply
    • Although I wonder why extend it into regex. Is there a performance bonus over id$= ?

      function component(id) { return jQuery(‘[id$=:”‘ + id + ‘”]’); }

      Reply
      • Performance-wise I’m not sure. The reason I’ve used this method is because of flexibility, this isn’t my only use case for this library so I need to be able to vary things like the regex flags.

        You are right though, I think it’s important that developers are aware that there are attribute selector modifiers, I’ll add something into the article.

        Reply
  2. I’ve actually been looking at this and there is potentially a way to optionally extend this so it is usable in table/repeat where the id would match each row’s entry. This would allow you to pass a restriction (to a row perhaps) to limit result set.

    function component(id, restrictor) {
    return jQuery(restrictor === null ? document : restrictor).find(‘:regex(id,\:’ + id + ‘([^\:]*)$)’);
    }

    or

    function component(id, restrictor) {
    return jQuery(restrictor === null ? document : restrictor).find(‘[id$=”:’ + id + ‘”]’);
    }

    Reply
  3. amendment – to properly handle omission of restrictor

    function component(id, restrictor) {
    return jQuery(restrictor === undefined || restrictor == null ? document : restrictor).find(β€˜:regex(id,\:’ + id + β€˜([^\:]*)$)’);
    }

    or

    function component(id, restrictor) {
    return jQuery(restrictor === undefined || restrictor == null ? document : restrictor).find(β€˜[id$=”:’ + id + ‘”]β€˜);
    }

    Reply
    • Yip Ryan has highlighted this in the comments, I’ll add the option in to the post. The reason that I proposed it like this is because of my use case which requires the ability to flip the regex flags. In all honesty the attribute selectors should be the primary solution with the code in this post dealing with edge cases.

      Reply
      • I would still expose a general purpose function for consistent usage pattern and handling iterative components (repeat, table)… just normally not consuming the optional second parameter below

        function component(id, restrictor) {
        return jQuery(restrictor === undefined || restrictor == null ? document : restrictor).find(β€˜[id$=”:’ + id + ‘”]β€˜);
        }

        Reply
  4. Nice solution. I’ve just been using class selectors personally instead of Ids. Not sure if selecting by id is faster than class, but regardless I like your clever approach.

    Reply
    • Thanks dude πŸ™‚ yeah selecting ids is much faster since it uses a special piece of functionality available in all browsers made for Id searching.

      Reply
  5. I’ve long been a fan of doing the selector on the styleClass rather than the id, but this may be a tinge better.

    Reply
  6. I tried using your technique to show/hide text using a mouseover and mouseout without success. Here is the code:

    var j$ = jQuery.noConflict();
    j$(document).ready(function(){
    j$(‘[id$=hideshow]’).mouseover(function(){
    j$(‘[id$=description]’).hide();
    });
    j$(‘[id$=hideshow]’).mouseout(function(){
    j$(‘[id$=description]’).show();
    });
    });

    Table

    Reply

Leave a Comment