24

I am working in a Javascript library that brings in jQuery for one thing: an "ends with" selector. It looks like this:

$('[id$=foo]')

It will find the elements in which the id ends with "foo".

I am looking to do this without jQuery (straight JavaScript). How might you go about this? I'd also like it to be as efficient as reasonably possible.

danwellman
  • 9,068
  • 8
  • 60
  • 88
Brian Genisio
  • 47,787
  • 16
  • 124
  • 167
  • 1
    Can you provide more information ? What kind of dom elements are you targeting ? Is it just DIVs and SPANs or anything ? – DhruvPathak Mar 09 '11 at 16:21
  • As far as I can tell, they are all input tags, but the query above does not specify that. I am re-working an existing library, so I can't be completely sure. – Brian Genisio Mar 09 '11 at 16:28

6 Answers6

32

Use querySelectorAll, not available in all browsers (like IE 5/6/7/8) though. It basically works like jQuery:

http://jsfiddle.net/BBaFa/2/

console.log(document.querySelectorAll("[id$=foo]"));
Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
pimvdb
  • 151,816
  • 78
  • 307
  • 352
1

You will need to iterate over all elements on the page and then use string functions to test it. The only optimizations I can think of is changing the starting point - i.e. not document.body but some other element where you know your element will be a child of - or you could use document.getElementsByTagName() to get an element list if you know the tag name of the elements.

However, your task would be much easier if you could use some 3rd-party-javascript, e.g. Sizzle (4k minified, the same selector engine jQuery uses).

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
1

So, using everything that was said, I put together this code. Assuming my elements are all inputs, then the following code is probably the best I am going to get?

String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

function getInputsThatEndWith(text) {

    var result = new Array();
    var inputs = document.getElementsByTagName("input");

    for(var i=0; i < inputs.length; i++) {
        if(inputs[i].id.endsWith(text))
            result.push(inputs[i]);
    }

    return result;
}

I put it on JsFiddle: http://jsfiddle.net/MF29n/1/

Brian Genisio
  • 47,787
  • 16
  • 124
  • 167
1

@ThiefMaster touched on how you can do the check, but here's the actual code:

function idEndsWith(str)
{ 
  if (document.querySelectorAll)
  {
    return document.querySelectorAll('[id$="'+str+'"]');
  }
  else
  {
    var all,
      elements = [],
      i,
      len,
      regex;

    all = document.getElementsByTagName('*');
    len = all.length;
    regex = new RegExp(str+'$');
    for (i = 0; i < len; i++)
    {
      if (regex.test(all[i].id))
      {
        elements.push(all[i]);
      }
    }
    return elements;
  }
}

This can be enhanced in a number of ways. It currently iterates through the entire dom, but would be more efficient if it had a context:

function idEndsWith(str, context)
{
  if (!context)
  {
    context = document;
  }
  ...CODE... //replace all occurrences of "document" with "context"
}

There is no validation/escaping on the str variable in this function, the assumption is that it'll only receive a string of chars.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
1

Suggested changes to your answer:

RegExp.quote = function(str) {
     return str.replace(/([.?*+^$[\]\\(){}-])/g, "\\$1");
}; // from https://stackoverflow.com/questions/494035/#494122

String.prototype.endsWith = function(suffix) {
    return !!this.match(new RegExp(RegExp.quote(suffix) + '$'));
};

function getInputsThatEndWith(text) {

    var results = [],
        inputs = document.getElementsByTagName("input"),
        numInputs = inputs.length,
        input;

    for(var i=0; i < numInputs; i++) {
        var input = inputs[i];
        if(input.id.endsWith(text)) results.push(input);
    }

    return results;
}

http://jsfiddle.net/mattball/yJjDV/

Implementing String.endsWith using a regex instead of indexOf() is mostly a matter of preference, but I figured it was worth including for variety. If you aren't concerned about escaping special characters in the suffix, you can remove the RegExp.quote() bit, and just use
new RegExp(suffix + '$').

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
0

If you know the type of DOM elements you are targeting, then get a list of references to them using getElementsByTagName , and then iterate over them.

You can use this optimization to fasten the iterations:

  • ignore the elements not having id.

  • target the nearest known parent of elements you want to seek, lets say your element is inside a div with id='myContainer', then you can get a restricted subset using document.getElementById('myContainer').getElementsByTagName('*') , and then iterate over them.

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
DhruvPathak
  • 42,059
  • 16
  • 116
  • 175