在追加之前实例化正确类型 SVGTextElement 的文本元素
Instantiating text elements of correct type SVGTextElement before appending
我需要根据给定数据以编程方式实例化几个文本元素。这些文本元素应该被一个矩形包围,该矩形的宽度与具有最大宽度的文本元素相同。像这样:
目前,我正在使用一种方法来创建文本节点,如下所示:
let textnodes = data.map(d => {
let svgText = document.createElementNS(d3.namespaces['svg'],'text')
let textNode = document.createTextNode(d.text)
svgText.appendChild(textNode)
return svgText
})
之后,我计算宽度并最终应用最大函数,其中 getTextWidth
是 self-made 方法:
Math.max(...textnodes.map(t=> {
return t.firstChild ? this.getTextWidth(
t.firstChild.nodeValue,
this.config.attributesFontSize + "px " + this.config.attributesFont ) : 0
}))
我的问题是:有没有办法创建一个 SVGTextElement
作为 D3 的类型提供而不是使用 document.createElementNS(...)
?我问的原因是,SVGTextElement
让我有可能使用 getBBox()
方法,该方法免费提供 width
属性。理想情况下,我想像这样实例化我的文本节点:
data.map({ d =>
let text = new SVGTextElement()
text.setAttribute(...)
...
})
但是,不允许使用这样的构造函数。
问题有多种解决方案:
第一个可能的解决方案是将结果(根据称为 type assertion 的 TypeScript)转换为 SVGTextElement
,因为您知道您对 document.createElementNS()
的调用保证产生完全相同的类型:
const svgText = document.createElementNS(d3.namespaces['svg'], "text") as SVGTextElement;
然而,有一个更优雅的解决方案,让编译器为您完成工作。在 DOM type definitions for the .createElementNS
method there is an overload 特别是在 SVG 命名空间中创建元素:
createElementNS<K extends keyof SVGElementTagNameMap>(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: K): SVGElementTagNameMap[K];
在此定义中,SVGElementTagNameMap
是一个将 SVG 命名空间的所有元素名称映射到相应类型的接口。因此,上述类型定义应被解读为:如果 namespaceURI
恰好是 "http://www.w3.org/2000/svg"
并且 qualifiedName
是 SVGElementTagNameMap
接口中包含的键,则编译器推断return 类型为该接口中 qualifiedName
引用的元素类型。
const svgText = document.createElementNS("http://www.w3.org/2000/svg", "text");
对于上面的行,svgText
将是 SVGTextElement
类型,正如您所需要的那样。
旁注:由于某些我无法理解的原因,您必须将命名空间指定为字符串,您不能通过 d3.namespaces.svg
或任何其他引用来引用该字符串。也许有人可以填写缺失的信息以进一步改进这个答案。
如果你想要一个 D3-ish 解决方案,你可以使用 d3.create()
which will create a detached element and return a new selection containg that single element. The compiler will not be able to automatically infer the type, though, because you need to specify svg:
as the prefix for the SVG namespace. You can, however, resort to generics to specify the type as can be seen from the type defintion 作为方法:
export function create<NewGElement extends Element>(name: string): Selection<NewGElement, undefined, null, undefined>;
您的代码可能如下所示:
const svgTextSel = create<SVGTextElement>("svg:text");
我需要根据给定数据以编程方式实例化几个文本元素。这些文本元素应该被一个矩形包围,该矩形的宽度与具有最大宽度的文本元素相同。像这样:
目前,我正在使用一种方法来创建文本节点,如下所示:
let textnodes = data.map(d => {
let svgText = document.createElementNS(d3.namespaces['svg'],'text')
let textNode = document.createTextNode(d.text)
svgText.appendChild(textNode)
return svgText
})
之后,我计算宽度并最终应用最大函数,其中 getTextWidth
是 self-made 方法:
Math.max(...textnodes.map(t=> {
return t.firstChild ? this.getTextWidth(
t.firstChild.nodeValue,
this.config.attributesFontSize + "px " + this.config.attributesFont ) : 0
}))
我的问题是:有没有办法创建一个 SVGTextElement
作为 D3 的类型提供而不是使用 document.createElementNS(...)
?我问的原因是,SVGTextElement
让我有可能使用 getBBox()
方法,该方法免费提供 width
属性。理想情况下,我想像这样实例化我的文本节点:
data.map({ d =>
let text = new SVGTextElement()
text.setAttribute(...)
...
})
但是,不允许使用这样的构造函数。
问题有多种解决方案:
第一个可能的解决方案是将结果(根据称为 type assertion 的 TypeScript)转换为
SVGTextElement
,因为您知道您对document.createElementNS()
的调用保证产生完全相同的类型:const svgText = document.createElementNS(d3.namespaces['svg'], "text") as SVGTextElement;
然而,有一个更优雅的解决方案,让编译器为您完成工作。在 DOM type definitions for the
.createElementNS
method there is an overload 特别是在 SVG 命名空间中创建元素:createElementNS<K extends keyof SVGElementTagNameMap>(namespaceURI: "http://www.w3.org/2000/svg", qualifiedName: K): SVGElementTagNameMap[K];
在此定义中,
SVGElementTagNameMap
是一个将 SVG 命名空间的所有元素名称映射到相应类型的接口。因此,上述类型定义应被解读为:如果namespaceURI
恰好是"http://www.w3.org/2000/svg"
并且qualifiedName
是SVGElementTagNameMap
接口中包含的键,则编译器推断return 类型为该接口中qualifiedName
引用的元素类型。const svgText = document.createElementNS("http://www.w3.org/2000/svg", "text");
对于上面的行,
svgText
将是SVGTextElement
类型,正如您所需要的那样。旁注:由于某些我无法理解的原因,您必须将命名空间指定为字符串,您不能通过
d3.namespaces.svg
或任何其他引用来引用该字符串。也许有人可以填写缺失的信息以进一步改进这个答案。如果你想要一个 D3-ish 解决方案,你可以使用
d3.create()
which will create a detached element and return a new selection containg that single element. The compiler will not be able to automatically infer the type, though, because you need to specifysvg:
as the prefix for the SVG namespace. You can, however, resort to generics to specify the type as can be seen from the type defintion 作为方法:export function create<NewGElement extends Element>(name: string): Selection<NewGElement, undefined, null, undefined>;
您的代码可能如下所示:
const svgTextSel = create<SVGTextElement>("svg:text");