Make Your Own Custom jQuery Selector

    Craig Sharkie
    Craig Sharkie

    Of all the JavaScript libraries out there, jQuery arguably has the selector syntax closest to CSS, and that makes it easy for CSS authors to dive in, even though there are 52 selectors to choose from. If you like CSS’s :first-child, :hover, or :only-child, there are 34 similar “pseudo” pseudo-class selectors on the menu. Talk about spoiled for choice!Should you want more than jQuery’s native selectors—such as :animated, :even, :not(), and :visible—there’s a raft of custom selectors from the community (like :exactIgnoreCase(), :nothidden, or :loaded()) to fill any gaps.So this all means that there’s no need for you to worry about learning to write custom selectors—right? Wrong!The easier a task is to complete, the greater the chance that it will be completed. Here’s a test for you to try out: ask a new developer to use (let alone understand) this code:

    if ($(el).offset().top > $(window).scrollTop() - $(el).outerHeight(true) &&    $(el).offset().top < $(window).scrollTop() + $(el).outerHeight(true) + $(window).height()){  //do something}

    Now ask them to try this on for size:

    if ($(el).is(":inview")) {  //do something}

    No prizes for working out that each of those snippets does the same thing, and that the second version is going to see much more use. Both check to see if an element is currently in the viewport.If you find yourself with a lengthy condition to get a Boolean, or your true/false Boolean is showing up again and again, you might be on target to create a custom expression for your selector. And making them could hardly be easier:

    jQuery.extend(jQuery.expr[':'], { //$.extend($.expr[':'] is just as good  expression: function () {    //establish boolean to be returned, or    return false;  }});

    The expression is what will be called in your selector … it could be inview, loaded, or nothidden.The function is what you’d expect. We’ve added the return false, as that’s what you want the function to do at the minimum. Other than that, use any methods you need to return the Booleans that will power your selector.Let’s have a first run at our inview selector:

    jQuery.extend(jQuery.expr[':'], {  inview: function (el) {    if ($(el).offset().top > $(window).scrollTop() - $(el).outerHeight(true) &&        $(el).offset().top < $(window).scrollTop() + $(el).outerHeight(true) + $(window).height()) {      return true;    }    return false;  }});

    The code is far from optimized, but it’s working. For testing, I like to add selectors in their own script block between the jQuery include and my working script block. For production, though, you can include them like a plugin. Go ahead and add it to a page, and you can use it in several ways:



    $("p").filter(":inview").each(function(){  //do something});


    if ($("p#desc").is(":inview")) {  //do something}

    Now let’s take a look at some optimizations. Straight out of the box we can see some repetition that we can cache, including some repeated objects. As we’re only returning true or false, we can simply return our condition and we’ll be on our way:

    jQuery.extend(jQuery.expr[':'], {  inview: function (el) {    var $e = $(el),    $w = $(window),    top = $e.offset().top,    height = $e.outerHeight(true),    windowTop = $w.scrollTop(),    windowScroll = windowTop - height,    windowHeight = windowTop + height + $w.height();    return (top > windowScroll && top < windowHeight);  }});

    And we’re there! We’ve extended jQuery’s expression engine and our code is both optimized and readable.It’s not the only “in view”-style selector you’ll find on the Web, but ours is an evolution. It takes the height of the element into account, so that it keeps reporting true as long as the element is partially in the viewport.jQuery’s selectors are a natural for customization, and give you a great way to wet your feet before jumping into using .extend() to make plugins.

    Note: Want more?

    If you want to read more from Craig, check out jQuery: Novice to Ninja, the book he co-wrote with Earle Castledine.