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

3

I do not know if "path" is the best definition. But using QuerySelector you can get an element specifying a "path", such as:

main section.sobre .view-more

But how can I do the opposite?

If I have an element, such as <button class="view-more"> , how can I check to see if it is main section.sobre .view-more ?

The only way I found it would be 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 are searching for multiple "paths" you will have to execute multiple querySelectorAll . That is, if you want to know if the element is main .a , main .b , main .c , main .d , you would have to querySelectorAll to cad one, then compare. This is even worse if there are multiple elements with the same "path", as above, since it will have to iterate through querySelectorAll itself.

This works, but it does not seem right to me. Is there another, more efficient, and native way to achieve this goal?

    
asked by anonymous 26.10.2018 / 16:31

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 that represents 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 their prefix, for example, chrome has 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 usage:

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>
    
25.11.2018 / 19:04
0

You can use the .parent property to go up in the component tree and fetch the parent classes and tags and thus 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);
    
26.10.2018 / 16:56