skip to Main Content

Some HTML:

<div>
  
  <div>
    <button>
      <svg xmlns="http://www.w3.org/2000/svg">
        <path d="x" />
      </svg>
    </button>    
  </div>
  
  <div>
    <button>                                      <!-- I want this -->
      <svg xmlns="http://www.w3.org/2000/svg">
        <path d="y" />                            <!-- I have this -->
      </svg>
    </button>
  </div>
  
</div>

I have the data (y) to target the second path, and then I want to get its ancestor button.

I can target the path element like this:

//svg/path[@d='y']

But I can’t target its closest ancestor button:

//svg/path[@d='y']/ancestor::button

A demo is here.

And if I try this in firefox or chromium devtools (for the page containing the above markup):

$x("//ancestor::button[1]", document.querySelector("path[d='y']"))

…it returns many results, not just the first ancestor button.

2

Answers


  1. You’re getting entangled with namespaces.

    One way to handle this is to change your xpath to

    //svg[*[local-name()='path'][@d='y']]/parent::*[local-name()='button']
    

    and see if it works.

    Login or Signup to reply.
  2. Inside the browser, and with the built-in XPath 1.0 support, I think you need to use namespaces to select the SVG elements e.g.

    var namespaceResolver = function(prefix) {
      if (prefix === 'svg')
        return 'http://www.w3.org/2000/svg';
      else
        return null;
    }
    
    var path = document.evaluate('//svg:svg/svg:path[@d = "y"]', document, namespaceResolver, XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue;
    
    
    var ancestorButton = document.evaluate('ancestor::button[1]', path, namespaceResolver, XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue;
    
    console.log(ancestorButton.outerHTML);
    <div>
      
      <div>
        <button>
          <svg xmlns="http://www.w3.org/2000/svg">
            <path d="x" />
          </svg>
        </button>    
      </div>
      
      <div>
        <button>                                      <!-- I want this -->
          <svg xmlns="http://www.w3.org/2000/svg">
            <path d="y" />                            <!-- I have this -->
          </svg>
        </button>
      </div>
      
    </div>

    Or switch to SaxonJS and XPath 3.1 and use for instance a different default element namespace when looking for SVG and for HTML elements e.g.

    const xhtmlNamespace = 'http://www.w3.org/1999/xhtml';
    
    const svgNamespace = 'http://www.w3.org/2000/svg';
    
    var path = SaxonJS.XPath.evaluate('//svg/path[@d = "y"]', document, { xpathDefaultNamespace : svgNamespace });
    
    
    var ancestorButton = SaxonJS.XPath.evaluate('ancestor::button[1]', path, { xpathDefaultNamespace : xhtmlNamespace });
    
    
    console.log(ancestorButton.outerHTML);
    <script src="https://martin-honnen.github.io/Saxon-JS-2.5/SaxonJS2.js"></script>
    <div>
      
      <div>
        <button>
          <svg xmlns="http://www.w3.org/2000/svg">
            <path d="x" />
          </svg>
        </button>    
      </div>
      
      <div>
        <button>                                      <!-- I want this -->
          <svg xmlns="http://www.w3.org/2000/svg">
            <path d="y" />                            <!-- I have this -->
          </svg>
        </button>
      </div>
      
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search