我尝试向一些节点添加新的SVG元素。为此目的,必须通过包含在文本内容中的字符串来查找要添加元素的节点,例如查找在"id0"
标记中具有<text>
的任何节点。
下面是我的HTML层次结构的示例:
<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>
包含"id0"
,则返回到父节点并向其添加一个SVG元素。但是在return this.parentElement;
行上会发生一个错误:
属性“parentElement”在“窗口”类型上不存在。
使用parentElement
或parent
时也会发生类似的错误。
发布于 2019-09-24 03:43:57
D3中没有通过文本内容选择元素的内置方式,这是因为D3内部使用Element.querySelector()
和Element.querySelectorAll()
从DOM中选择元素。这些方法将CSS选择器字符串作为由3级选择器规范定义的单个参数。不幸的是,没有任何方法可以根据元素的内容来选择元素(这曾经是通过:contains()
伪类来实现的,尽管这个类已经消失了)。
因此,要构建您的选择,您必须向.select()
传递一个函数,该函数选择并返回您感兴趣的<text>
元素。这样做有很多种方法,但我想提出一种不那么明显但却很优雅的方法。这利用了鲜为人知的NodeIterator
接口,它可以用于从DOM创建一个符合过滤器标准的节点列表上的迭代器。
NodeIterator
实例是通过调用来创建的,它包含三个参数:
NodeFilter.SHOW_TEXT
。NodeFilter
接口的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>
发布于 2020-01-16 00:08:42
另一种选择是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和docs:MDN: document.evaluate。
注意,由于"普通HTML节点和svg节点属于不同的命名空间",您需要选择SVG节点,比如*[name()="svg"]
。
关于查找文本,我建议使用contains(text(),'needle')
而不是更显式的text()='needle'
,因为needle
周围的任何空格都会导致选择器返回null
。
有趣的xpath vs CSS评论:cssSelector和Xpath之间有什么区别,哪个在跨浏览器测试的性能方面更好呢?
请注意,document.evaluate
不支持IE
https://stackoverflow.com/questions/58052522
复制