Billy Moon
Billy Moon

Reputation: 58531

Create custom jQuery filter

I just learned that you can create custom filters for jquery very easily and put together a filter to use regular expression match function on text content or any attribute.

I prepend the attribute to be matched to a normal regex, I know this is strange, please don't be thrown by this, or think less of me for it.

The problem I am having is when there are brackets in the regex that is to be matched. Sometimes it is fine, sometimes it causes syntax error.

fine: :match(src/jquery.+\.(js)/i)') error: :match(src/jquery.+\.js()/i)')

Can anyone tell me the reason for this behavior from this code?

// create new jquery filter
jQuery.expr[':'].match = function(e,i,p) { // element, index, params

    // split params by custom format
    m = p[3].match(/^([a-z]*)(\/?)(.+)\2([a-z]*)$/)

    // set the attribute to be checked
    myAttr = m[1] ? m[1] : 'text'

    // define regex, including switches
    myRegExp = new RegExp(m[3],m[4]);

    // check for and return matched elements
    if(myAttr == 'text')
        return $(e).text().match(myRegExp)
    else
        return $(e).attr(myAttr) ? $(e).attr(myAttr).match(myRegExp) : false
};

// used like this
// :match(attr/regex/switches)
// or with default match (text) and case insensitivity switch (i):
// :match(/HeLlO WoRl/i)
// or in simplest form
// :match(RegexPattern)

// alert how many jquery plugins of the form jquery.something.js
alert($('script:match(src/jquery.+\.js/i)').length)

Due to the excellent answer I received to this question, I decided to re-work my filter into a plugin function called match. Does anyone have any comments of how to improve it? Will it work for all circumstances?

// will save as file called jquery.match.js
// will wrap with
// (function($){
$.fn.match = function(regex, attr){ // optional attr
    return this.filter(function(){
        var subject = attr ? $(this).attr(attr) : $(this).text()
        return subject && subject.match(regex) ? 1 : 0
    })
}
// and end with
// })

// used like this: $('body *').match(/some content/)
// or to match attributes like this: $('script').match(/jquery/i,'src')

Upvotes: 1

Views: 2798

Answers (1)

skabbes
skabbes

Reputation: 900

Your code appears to be failing because of the extra parenthesis in the second regex. Since jQuery is parsing the expression before you can, and has no knowledge of your predefined format, it will see :match(src/jquery.+\.js()/i) as :match(src/jquery.+\.js() + /i). Note that the second parenthesis is being interpreted as a close to the match.

There might be a way to tell jQuery to accept escaped versions of that ) but I'm not sure how.

Alternatively

You can just use the jQuery filter function like so.

$('script').filter( function(){
    return $(this).attr("src").match( /jquery/i ); 
});

To achieve a similar level of generality like your plugin was providing, you can create a higher order function to do the job.

function createFilterFunction(regex, /*optional*/ attr){
    return function(){
        if( !attr ){
            return $(this).text().match(myRegex);
        }
        return $(this).attr(attr) ? $(this).attr(attr).match(regex) : false;
    };
}

Then you could use it like such:

var isJquery = createFilterFunction( /jquery.+\.js()/i, "src" );
alert( $('script').filter( isJquery ).length );

Side Note

When writing javascript, if you don't include var in front of your variables, they will "leak" into the global namespace. So, your variables m, myAttr, and myRegExp will all be visible to anyone who uses your script, and more importantly it will change their values of m, myAttr, and myRegExp if they exist. Declare them like var m = ... instead.

Upvotes: 1

Related Questions