I have a bunch of Custom Elements that begin with 'food-cta-'. I am looking for a way in JavaScript/jQuery to be able to select these elements. This is similar to how I can use $('*[class^="food-cta-"]')
to select all the classes that start with food-cta-
. Is it possible to do a search for elements that start with 'food-cta-'?
Note that I will be injecting this search onto the page, so I won't have access to Angular.
Example of Custom Elements:
<food-cta-download>
<food-cta-external>
<food-cta-internal>
EDIT: The code I am looking at looks like:
<food-cta-download type="primary" description="Download Recipe">
<img src="">
<h2></h2>
<p></p>
</food-cta-download>
The app uses AngularJS to create Custom Elements which I believe is called Directives.
I have a bunch of Custom Elements that begin with 'food-cta-'. I am looking for a way in JavaScript/jQuery to be able to select these elements. This is similar to how I can use $('*[class^="food-cta-"]')
to select all the classes that start with food-cta-
. Is it possible to do a search for elements that start with 'food-cta-'?
Note that I will be injecting this search onto the page, so I won't have access to Angular.
Example of Custom Elements:
<food-cta-download>
<food-cta-external>
<food-cta-internal>
EDIT: The code I am looking at looks like:
<food-cta-download type="primary" description="Download Recipe">
<img src="">
<h2></h2>
<p></p>
</food-cta-download>
The app uses AngularJS to create Custom Elements which I believe is called Directives.
Share Improve this question edited May 31, 2016 at 18:23 Evan Carroll 1 asked May 31, 2016 at 17:26 JonJon 8,55131 gold badges96 silver badges150 bronze badges 10- 2 @EvanCarroll, they're custom elements, which is a newer standard for HTML. – zzzzBov Commented May 31, 2016 at 17:31
- 1 @EvanCarroll, or it's a custom html element which will inherit from HTMLElement or HTMLUnknownElement if not registered – Patrick Evans Commented May 31, 2016 at 17:31
-
1
You can add in the link function of each directive
element.addClass('food-cta')
and then all the elements with those directive will have the same class, but AFAIK you can't select element this way as you do with class - You have to list the elementsfood-cta-download, food-cta-external, food-cta-internal {background:red;}
– Alon Eitan Commented May 31, 2016 at 17:34 - @AlonEitan Unfortunately I am unable to change any of the Angular code otherwise that would have been a wonderful solution! – Jon Commented May 31, 2016 at 17:55
- Do you already have a list of all the possible tag names it could use? – Patrick Roberts Commented May 31, 2016 at 17:56
4 Answers
Reset to default 3You can use XPath with the expression
//*[starts-with(name(),'food-cta-')]
Where
//*
is wildcard for all nodesstarts-with()
is a XPath function to test a string starts with some valuename()
gets the QName (node name)- and
'food-cta-'
is the search term
Pass it into document.evaluate
and you will get a XPathResult that will give you the nodes that were matched.
var result = document.evaluate( "//*[starts-with(name(),'food-cta-')]", document, null, XPathResult.ANY_TYPE, null );
Note you can use any node as the root, you do not need to use document
. So you could for instance replace document
with the some div:
var container = document.getElementById("#container");
var result = document.evaluate( "//*[starts-with(name(),'food-cta-')]", container, null, XPathResult.ANY_TYPE, null );
Demo
let result = document.evaluate( "//*[starts-with(name(),'food-cta-')]", document, null, XPathResult.ANY_TYPE, null );
let nodes = [];
let anode = null;
while( (anode = result.iterateNext()) ){
nodes.push( anode.nodeName );
}
console.log(nodes);
<div id="container">
<br>
<food-cta-download type="primary" description="Download Recipe">
<img src="">
<h2></h2>
<p></p>
</food-cta-download>
<span>Some span</span>
<food-cta-something>
<img src="">
<h2></h2>
<p></p>
</food-cta-something>
<div>In between
<food-cta-sub>
<img src="">
<h2></h2>
<p></p>
</food-cta-sub>
</div>
<food-cta-hello>
<img src="">
</food-cta-hello>
<food-cta-whattt>
</food-cta-whattt>
</div>
Try this..
let customElements = $('*')
.filter((index,element) => /FOOD-CTI-/.test(element.tagName));
Note, .tagName
should return the result in uppercase. This should get you a jQuery object of the elements you want. It will traverse the entire DOM though. It'll be slow.
This uses the "all selector".
Caution: The all, or universal, selector is extremely slow, except when used by itself.
You can traverse less then entire dom too, by specifying something like $("body *")
. Not sure where you have put the Custom Elements, and where they're allowed.
As an aside, I wouldn't use Custom Elements, microformats are a better idea at least now, they're also better supported, and they're less likely to change.
You probably have to just go to the elements in question and check if their tagName begins with that given string...
var myPrefix = "mycustom-thing-";
$("body").children().each(function() {
if (this.tagName.substr(0, myPrefix.length).toLowerCase() == myPrefix) {
console.log(this.innerHTML); // or what ever
}
})
https://jsfiddle/svArtist/duLo2d0z/
EDIT: Included for efficiency's sake:
If you can predict where the elements will be, you can of course specify that circumstance.
In my example, the elements in question were direct children of body
- so I could use .children()
to get them. This would not traverse lower levels.
Reduce the need for traversal by the following:
Start on the lowest needed level ($("#specific-id")
rather than $("body")
)
If the elements are all to be found as direct children of a container:
- Use
$.children()
on the container to obtain just the immediate children
Else
- Use
$.find("*")
If you can tell something about the containing context, filter by that
For example $("#specific-id").find(".certain-container-class .child-class *")
Why not extend jquery selectors?
$(':tag(^=food-cta-)')
Would be possible with the following implementation:
$.expr[':'].tag = function tag(element, index, match) {
// prepare dummy attribute
// avoid string processing when possible by using element.localName
// instead of element.tagName.toLowerCase()
tag.$.attr('data-tag', element.localName || element.tagName.toLowerCase());
// in :tag(`pattern`), match[3] = `pattern`
var pattern = tag.re.exec(match[3]);
// [data-tag`m`="`pattern`"]
var selector = '[data-tag' + (pattern[1] || '') + '="' + pattern[2] + '"]';
// test if custom tag selector matches element
// using dummy attribute polyfill
return tag.$.is(selector);
};
// dummy element to run attribute selectors on
$.expr[':'].tag.$ = $('<div/>');
// cache RegExp for parsing ":tag(m=pattern)"
$.expr[':'].tag.re = /^(?:([~\|\^\$\*])?=)?(.*)$/;
// some tests
console.log('^=food-cta-', $(':tag(^=food-cta-)').toArray());
console.log('*=cta-s', $(':tag(*=cta-s)').toArray());
console.log('food-cta-hello', $(':tag(food-cta-hello)').toArray());
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<br>
<food-cta-download type="primary" description="Download Recipe">
<img src="">
<h2></h2>
<p></p>
</food-cta-download>
<span>Some span</span>
<food-cta-something>
<img src="">
<h2></h2>
<p></p>
</food-cta-something>
<div>In between
<food-cta-sub>
<img src="">
<h2></h2>
<p></p>
</food-cta-sub>
</div>
<food-cta-hello>
<img src="">
</food-cta-hello>
<food-cta-whattt>
</food-cta-whattt>
</div>
This supports a pseudo-CSS-style attribute selector with the syntax:
:tag(m=pattern)
Or just
:tag(pattern)
where m
is ~,|,^,$,*
and pattern
is your tag selector.