XPath 以第一个祖先标记为目标

XPath to target first ancestor tag

提问人:lonix 提问时间:9/19/2023 最后编辑:lonix 更新时间:9/20/2023 访问量:48

问:

一些 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>

我有数据()来定位第二个,然后我想得到它的祖先。ypathbutton

我可以像这样定位元素:path

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

但我不能瞄准它最近的祖先:button

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

演示在这里

如果我在 firefox 或 chromium devtools 中尝试这样做(对于包含上述标记的页面):

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

...它返回许多结果,而不仅仅是第一个祖先按钮。

html xml xpath

评论

0赞 Gilles Quénot 9/19/2023
您上一个 XPath 工作正常xidel
0赞 lonix 9/19/2023
@GillesQuénot 谢谢。所以我认为在线测试器“xpather.com”有问题。
0赞 Gilles Quénot 9/19/2023
github.com/benibela/xidel 命令行工具。我不明白如何在链接上触发 XPath
0赞 Martin Honnen 9/19/2023
您是否知道您的文档在没有命名空间中有一些 HTML 元素,但在 SVG 命名空间中有一些 SVG?您如何解析输入,作为 HTML、XML、支持或不支持命名空间?重要的是要知道,以判断是否例如 选择该 SVG 元素或不选择任何元素。至于你的尝试,那应该是.//svgsvg$x("//ancestor::button[1]", document.querySelector("path[d='y']"))$x("ancestor::button[1]", document.querySelector("path[d='y']"))
0赞 lonix 9/20/2023
@MartinHonnen 谢谢。这是一个 HTML 文档,正在浏览器中解析。顺便说一句,您的代码片段在浏览器中对我有用,谢谢。

答:

1赞 Jack Fleeting 9/19/2023 #1

你正在与命名空间纠缠不清。

处理此问题的一种方法是将 xpath 更改为

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

看看它是否有效。

评论

0赞 Siebe Jongebloed 9/19/2023
我认为svg也应该匹配吗?我怀疑按钮是否需要通过 匹配,因为它是 html。*[local-name()='svg']local-name()
0赞 Jack Fleeting 9/19/2023
@SiebeJongebloed我想这取决于你的环境;我使用与 OP 相同的演示站点对其进行了测试,它的工作方式与答案中的相同,同时从失败中消除。没有领带去尝试其他任何事情。local-name()button
0赞 Siebe Jongebloed 9/19/2023
演示站点显示您的 XPath 和注释是正确的。我仍然怀疑这是否正确。
0赞 lonix 9/20/2023
它适用于演示站点。但不是在浏览器中:返回一个空数组。我会摆弄它,看看我是否能让它工作。谢谢你给我一些工作!$x("//svg[*[local-name()='path'][@d='y']]/parent::*[local-name()='button']")
0赞 Jack Fleeting 9/20/2023
@lonix 你能发布链接吗?
2赞 Martin Honnen 9/20/2023 #2

在浏览器内部,并具有内置的 XPath 1.0 支持,我认为您需要使用命名空间来选择 SVG 元素,例如

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>

或者切换到 SaxonJS 和 XPath 3.1,例如,在查找 SVG 和 HTML 元素时使用不同的默认元素命名空间,例如

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>