我尝试将新的SVG元素添加到某些节点。为此,要添加元素的节点必须由包含在其中的文本内容中的字符串来找到,例如找到在 public void showToast(Context context, String message)
{
Toast.makeText(context,message,Toast.LENGTH_LONG).show();
}
标记内具有"id0"
的任何节点。
以下是我的HTML层次结构示例:
<text>
我当然不知道正确的解决方案,但是我认为是这样的:
<pre>
<svg>
<g>
<g>
<text> id3 </text>
<text> 73% </text>
<svg> ... </svg>
</g>
<g>
<svg> ... </svg>
</g>
<g>
<text> id0 </text>
<text> 11% </text>
<svg> ... </svg>
</g>
<g>
<text> id1 </text>
<text> 66% </text>
<svg> ... </svg>
</g>
<g>
<svg> ... </svg>
</g>
</g>
</svg>
</pre>
如果标签d3.select('svg').select('g').selectAll('g').each(function (d, i) {})
.select('g').select('text').filter(function () {
return (d3.select(this).text() === 'id0')
})
.select(function () {
return this.parentElement;
})
.append('svg')
.attr('width', 400)
.attr('height', 400)
包含<text>
,则返回到父节点并向其添加SVG元素。但是在行"id0"
上发生了错误:
类型“窗口”上不存在属性“ parentElement”。
当我使用return this.parentElement;
或parentElement
时也会发生类似的错误。
答案 0 :(得分:2)
xpath的另一种选择是,它允许通过文本进行搜索:
// if you know there's only one...
const singleResult = document.evaluate(`//*[name()="text" and contains(text(), "id0")]`, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
console.log(singleResult.nodeName, singleResult.textContent)
// if there might be multiple results
const multipleResults = document.evaluate(`//*[name()="text" and contains(text(), "id_multiple")]`, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
for (let i=0; i < multipleResults.snapshotLength; i++) {
console.log(multipleResults.snapshotItem(i).nodeName, multipleResults.snapshotItem(i).textContent)
}
<svg>
<g>
<g>
<text> id_multiple </text>
<text> 73% </text>
<svg></svg>
</g>
<g>
<svg></svg>
</g>
<g>
<text> id0 </text>
<text> 11% </text>
<svg></svg>
</g>
<g>
<text> id_multiple </text>
<text> 66% </text>
<svg></svg>
</g>
<g>
<svg></svg>
</g>
</g>
</svg>
返回的迭代器(/ snaphots)对我来说是意外的-绝对读过这个出色的答案:https://stackoverflow.com/a/32468320/2586761和文档:MDN: document.evaluate。
请注意,由于“ common HTML nodes and svg nodes belong to different namespaces”,您需要选择*[name()="svg"]
之类的SVG节点。
关于查找文本,我建议使用contains(text(),'needle')
而不是更明确的text()='needle'
,因为needle
周围的任何空格都会导致选择器返回null
。 / p>
有趣的xpath与CSS注释: What is the difference between cssSelector & Xpath and which is better with respect to performance for cross browser testing?
请注意,document.evaluate
不支持IE
答案 1 :(得分:1)
D3中没有通过其文本内容选择元素的内置方法,这是由于D3内部使用Element.querySelector()
和Element.querySelectorAll()
从DOM中选择元素。这些方法将CSS Selector字符串作为由Selectors Level 3规范定义的单个参数。不幸的是,无法根据其内容选择元素(这曾经可以通过:contains()
伪类来实现,但是已经消失了)。
因此,要构建选择,您必须求助于将函数传递到.select()
,该函数选择并返回您感兴趣的<text>
元素。可以通过多种方法进行,但是我会喜欢提出一种不太明显但优雅的方法。这利用鲜为人知的NodeIterator
接口,该接口可用于在DOM中满足过滤条件的节点列表上创建迭代器。
NodeIterator
实例是通过调用Document.createNodeIterator()
创建的,该实例带有三个参数:
NodeFilter.SHOW_TEXT
。.acceptNode()
接口的NodeFilter
方法的对象。该方法按文档顺序显示给指定类型的每个节点,并且对于任何匹配的节点,必须返回NodeFilter.FILTER_ACCEPT
,对于其他节点,必须返回NodeFilter.FILTER_REJECT
。此时,您的实现将寻找id值与实际文本元素的文本内容的匹配。然后可以在创建的节点迭代器上调用.nextNode()
,以遍历匹配节点的列表。对于您的任务,可能是以下几行:
document.createNodeIterator(
this, // The root node of the searched DOM sub-tree.
NodeFilter.SHOW_TEXT, // Look for text nodes only.
{
acceptNode(node) { // The filter method of interface NodeFilter
return new RegExp(value).test(node.textContent) // Check if text contains string
? NodeFilter.FILTER_ACCEPT // Found: accept node
: NodeFilter.FILTER_REJECT; // Not found: reject and continue
}
})
.nextNode() // Get first node from iterator.
.parentElement; // Found node is a "pure" text node, get parent <text> element.
一旦有了该节点,就可以轻松应用对该元素进行的任何修改,即追加元素,设置属性...如果您不是在寻找唯一值,而是在寻找多个匹配同一字符串的元素,这也很容易适应于处理多个节点。您只需要返回迭代器找到的节点的数组,然后可以将其直接传递到D3的.selectAll()
,以创建多个节点的选择。
对于正在运行的演示,请查看以下代码段:
function nodeIterator(value) {
return function() {
return document.createNodeIterator(
this, // The root node of the searched DOM sub-tree.
NodeFilter.SHOW_TEXT, // Look for text nodes only.
{
acceptNode(node) { // The filter method of interface NodeFilter
return new RegExp(value).test(node.textContent) // Check if text contains string
? NodeFilter.FILTER_ACCEPT // Found: accept node
: NodeFilter.FILTER_REJECT; // Not found: reject and continue
}
})
.nextNode() // Get first node from iterator.
.parentElement; // Found node is a "pure" text node, get parent <text> element.
}
}
const filter = nodeIterator("id0");
const sel = d3.select("svg").select(filter);
// Manipulate the selection:...
// sel.append("g")
// .attr("transform", "...");
console.log(sel.node());
<script src="https://d3js.org/d3.v5.js"></script>
<svg>
<g>
<g>
<text> id3 </text>
<text> 73% </text>
<svg></svg>
</g>
<g>
<svg></svg>
</g>
<g>
<text> id0 </text>
<text> 11% </text>
<svg></svg>
</g>
<g>
<text> id1 </text>
<text> 66% </text>
<svg></svg>
</g>
<g>
<svg></svg>
</g>
</g>
</svg>