How to compare the CSS path of an element using JS?

Asked

Viewed 75 times

3

I don’t know if "path" is the best definition. But using the QuerySelector it is possible to obtain an element by specifying a "path", for example:

main section.sobre .view-more

But how can I do the opposite?

If I have an element, like <button class="view-more">, how can I make sure he is the main section.sobre .view-more?

The only way I could find was to make a QuerySelector and then compare this element with the current element:

window.document.addEventListener("click", function(e) {

  [].forEach.call(window.document.querySelectorAll("main section.sobre .view-more"), function(el) {
    if (el == e.target) {
      window.console.log("Isso é o main section.sobre .view-more");
    }
  });

}, true);
<html lang="en">

<body>
  <main>
    <section class="sobre">
      <button class="view-less">View Less</button>
      <button class="view-close">View Less</button>

      <button class="view-more">View More</button>
      <button class="view-more">View More</button>
    </section>
  </main>
</body>

</html>

The problem is if you search for multiple "paths" you will have to run multiple querySelectorAll. That is, if you want to know if the element is main .a, main .b, main .c, main .d, would have to make a querySelectorAll to Cad one, then compare. This is even worse if there are multiple elements with the same "path", as the case above, since you will have to iterate for yourself querySelectorAll.

This works, but it doesn’t seem right. There is another, more efficient and native way to achieve this goal?

  • Do you have any practical use of this ? What would you do differently based on the parent ? Wouldn’t it be a case of using a selector that includes the parent as well ?

  • I’m using Gopherjs, so it makes it difficult to give a practical use, but it’s similar to the example. For simplicity it is easier to add a single Eventlistener in "each Section" and monitor the elements in it. For example, on a single-page website, each Service receives a Reader. This allows you to add and remove the elements and continue with a single System. Dai would have a mapa map[caminho]Listener(e dom.Event), per page. Then just make within the single Eventlistener a if listen, ok := mapa[caminho(event)]; ok { listen(event) }. This is using the listener of the previously defined element.

2 answers

1


There is a function for this, matchesSelector

Syntax:

element.matchesSelector(selectorString)

Where element is any HTML element and selectorString is a string representing a valid CSS selector. The function returns a boolean, true if the CSS selector represents a possible path to the element, and false otherwise

Many browsers implement this function with its prefix, for example Chrome has the webkitMatchesSelector, you can use this polyfill that is in the MDN documentation:

if (!Element.prototype.matches)
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1            
        }

Example of use:

if (!Element.prototype.matches)
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1            
        }

document.addEventListener("click", function(e) {
  if (e.target.matches('main section.sobre .view-more'))
    console.log("Isso é o main section.sobre .view-more");
}, true);
<html lang="en">
<head>
</head>
<body>
  <main>
    <section class="sobre">
      <button class="view-less">View Less</button>
      <button class="view-close">View Less</button>

      <button class="view-more">View More</button>
      <button class="view-more">View More</button>
    </section>
  </main>
</body>
</html>

0

You can use the property .parent to go up the component tree and get the parent classes and tags and so mount your query only once.

window.document.addEventListener("click", function(e) {
        let parent1 = e.target.parentElement;
        let parent2 = parent1.parentElement;
        if(!parent1 || !parent2) return;

        let tree = parent2.nodeName.toLowerCase() + ' ' + parent1.nodeName.toLowerCase() + '.' + parent1.className +  ' .' + e.target.className;

        console.log(tree == 'main section.sobre .view-more');

}, true);
  • This solves more or less. If there are gaps in the "path" it will not work, if it is something like "main . view-more", missing "Section". Another problem is if you have the id or widget, like main button.view-more. In "general" cases this does not seem to work.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.