What would be the vanilla JS equivalent of :has in this jQuery selector?
$('.main-container').children('.analytics:has(a)').not('#promo')
Within .main-container
, I'm trying to select all .analytics
elements without an id of "promo" that contain <a>
tags.
What I've tried:
document.querySelectorAll('.main-container .analytics:not(#promo)')
This will give me close to what I want, but I still have to filter out those .analytics
parents that do NOT have <a>
tags.
What would be the best way to approach this using vanilla JS?
What would be the vanilla JS equivalent of :has in this jQuery selector?
$('.main-container').children('.analytics:has(a)').not('#promo')
Within .main-container
, I'm trying to select all .analytics
elements without an id of "promo" that contain <a>
tags.
What I've tried:
document.querySelectorAll('.main-container .analytics:not(#promo)')
This will give me close to what I want, but I still have to filter out those .analytics
parents that do NOT have <a>
tags.
What would be the best way to approach this using vanilla JS?
Share Improve this question asked Mar 20, 2018 at 20:11 asw1984asw1984 7631 gold badge7 silver badges19 bronze badges 3 |4 Answers
Reset to default 10- Query the document for using your desired selector, in this case:
.analytics:not(#promo)
- Convert the NodeList to an Array
- Filter the array using the predicate:
element => element.querySelector('your-selector')
element.querySelector('your-selector')
will evaluate tonull
(which is falsey) if no child element is found
Generally as a function
function has(nodeList, selector) {
return Array.from(nodeList).filter(e => e.querySelector(selector))
}
const nodeList = document.querySelectorAll('.main-container > .analytics:not(#promo)')
has(nodeList, 'a').forEach(e => e.style.background = "red")
<div class="main-container">
<div class="analytics">
<a>Should be red</a>
</div>
<div class="analytics">
Should not be red
</div>
<div id="promo" class="analytics">
<a>Should not be red</a>
</div>
</div>
As a NodeList.prototype
NodeList.prototype.has = function(selector) {
return Array.from(this).filter(e => e.querySelector(selector))
}
document
.querySelectorAll('.main-container > .analytics:not(#promo)')
.has('a')
.forEach(e => e.style.background = 'red')
<div class="main-container">
<div class="analytics">
<a>Should be red</a>
</div>
<div class="analytics">
Should not be red
</div>
<div id="promo" class="analytics">
<a>Should not be red</a>
</div>
</div>
You could select the <a>
and then get their parentNodes:
var a = document.querySelectorAll('.main-container .analytics:not(#promo) a');
var yourElements = [];
for (var i = 0; i < a.length; i++) {
yourElements.push(a[i].parentNode);
}
yourElements.forEach(e => e.style.background = "red");
<div class="main-container">
<div class="analytics">
<a>Should be red</a>
</div>
<div class="analytics">
Should not be red
</div>
<div id="promo" class="analytics">
<a>Schould not be red</a>
</div>
</div>
EDIT: just noticed this only works if the <a>
is a direct child of your wanted element.
There is no equivalent selector for :has
, you'll have to use an initial selection and then filter them
var el = document.querySelectorAll('.main-container > .analytics:not(#promo)');
var res = [];
for (let x = 0; x < el.length; x++){
if (el[x].querySelector('a')) res.push(el[x]);
}
//res has has the array of elements needed.
Josh Larson's polyfill-css-has provides a querySelectorAllWithHas
method based on vanilla JavaScript.
querySelectorAll
and only apply the rules wherethis.getElementsByTagName('a').length > 0
? – Balázs Varga Commented Mar 20, 2018 at 20:17