Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a vanilla JS equivalent of jQuery .has()?

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?

like image 836
asw1984 Avatar asked Mar 20 '18 20:03

asw1984


People also ask

What can replace jQuery () in a function call?

You can use the function getElementsByTagName() which queries the DOM for elements of a certain tag and returns a collection of elements.

Is jQuery vanilla JavaScript?

Vanilla JS helped the developers in creating dynamic websites. Then came jQuery, a library of tools created by developers around the world, using Javascript. In simple words, jQuery is a lightweight and easy to use JavaScript library that helps in creating complex functionalities with few lines of coding.

What is the jQuery equivalent for on () of JavaScript?

In jQuery, you can listen to events for dynamically added elements using the on() function. The equivalent in JavaScript is addEventListener() function.


3 Answers

  1. Query the document for using your desired selector, in this case: .analytics:not(#promo)
  2. Convert the NodeList to an Array
  3. Filter the array using the predicate: element => element.querySelector('your-selector')

element.querySelector('your-selector') will evaluate to null (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>
like image 151
Raphael Rafatpanah Avatar answered Oct 11 '22 15:10

Raphael Rafatpanah


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.

like image 23
Sebastian Speitel Avatar answered Oct 11 '22 13:10

Sebastian Speitel


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.
like image 38
Musa Avatar answered Oct 11 '22 13:10

Musa