我想在运行时以编程方式将属性动态地添加到 DOM 元素(例如.HTML或 SVG 元素),即基于用户输入。如果该属性是标准属性,我将按原样使用该名称,但如果不是,即如果它需要转换为自定义属性,那么我想为其添加"data-"前缀。
那么,有没有办法确定字符串是否表示"标准/普通"DOM 属性?
例如,假设我有一个 SVG 矩形,如下所示:
<rect x="10" y="10" width="80" height="40" stroke="black"/>
请注意,尚未指定填充颜色。
用户请求添加两个属性:
- 名为"fill"的属性,值为"red",应产生
fill="red"
(注意缺少前缀) - 名为"price"的属性,值为"昂贵",应产生
data-price="expensive"
(请注意data-
前缀)
如何动态区分这两者?fill
属性在我当前的 DOM 元素中不存在,因此我无法检查该名称的属性是否存在预先存在的值。我也不相信如果我创建没有前缀的属性,我什至可以检查是否抛出错误,因为据我所知,至少我当前的浏览器(Chrome v61.0)将允许我添加属性price="expensive"
(不带data-
前缀),即使这不是最佳实践。
我想要如下的东西:
const elementNodeName = "rect";
const attributeName = "height"; // or, e.g. "price"
const nameIsValid = testAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
是否有任何预先存在的testAttributeNameValidity
类型功能?
我已经用javascript
标记了这个,因为我正在寻找一个Javascript解决方案。
**更新:来自其他SO答案的解决方案适用于HTML,但不适用于SVG **
根据评论中建议的链接,我尝试使用attributeName in document.createElement(elementNodeName)
它应该为有效的属性名称返回true
,为无效的属性名称返回false
。当我对 HTML 元素使用有效的属性名称"id"(<p>
)时,这种方法有效。
相比之下,我仍然没有在 SVG 中采用这种方法。我尝试将createElement
转换为createElementNS
,但这似乎仍然不能解决问题。请参阅下面的测试代码。测试代码中的关键功能是testHtmlAttributeNameValidity
和testSvgAttributeNameValidity
。如果您运行代码并在控制台中检查输出,则表明它适用于 HTML,即它生成id="someId" data-price="expensive"
.但是,对于 SVG,它只会产生不需要的输出:data-fill="yellow" data-price="expensive"
。
如果它有所作为,我正在使用Chrome v61。
测试代码:
const testHtmlAttributeNameValidity = (elementNodeName, attributeName) => {
return (attributeName in document.createElement(elementNodeName));
};
const testHtmlAttributeName = (attributeName, attributeValue) => {
const elementNodeName = "p";
const nameIsValid = testHtmlAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
const element = document.querySelector(elementNodeName);
element.setAttribute(attributeName, attributeValue);
};
testHtmlAttributeName("id", "someId");
testHtmlAttributeName("price", "expensive");
console.log(document.querySelector('div').innerHTML);
const svgNS = "http://www.w3.org/2000/svg";
const testSvgAttributeNameValidity = (elementNodeName, attributeName) => {
return (attributeName in document.createElementNS(svgNS, elementNodeName));
};
const testSvgAttributeName = (attributeName, attributeValue) => {
const elementNodeName = "rect";
const nameIsValid = testSvgAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
const element = document.querySelector(elementNodeName);
element.setAttribute(attributeName, attributeValue);
};
testSvgAttributeName("fill", "yellow");
testSvgAttributeName("price", "expensive");
console.log(document.querySelector('svg').innerHTML);
#someId {
color: green;
}
<svg height="55">
<rect x="10" y="10" width="80" height="40" stroke="red"/>
</svg>
<div>
<p>Hello world</p>
</div>
正常工作的代码应将矩形填充从黑色更改为黄色,这不会发生。
@ccprog的评论帮助我解决了这个问题。总之,对于 SVG DOM 元素,我需要原始检查加上一个相同但添加了.style
属性的布尔 OR'd 检查。
如问题所示,HTML DOM 元素属性名称的有效性可以通过检查来确定
,如下所示:// correct for HTML:
return (
attributeName in document.createElement(elementNodeName)
);
但是,此方法不适用于 SVG DOM 元素。具体来说,以下内容将确定真实属性名称(如"id")的有效性,但不会确定类似样式的属性名称(如"fill")的有效性:
// insufficient for SVG, i.e. only partially correct:
return (
attributeName in document.createElementNS(svgNS, elementNodeName)
);
但是,只需复制该测试并将.style
属性添加到两个检查之一,就可以检查两个类别的属性名称,如下所示:
// correct for SVG:
return (
attributeName in document.createElementNS(svgNS, elementNodeName).style ||
attributeName in document.createElementNS(svgNS, elementNodeName)
);
这在下面的代码中得到了证明,除了上述更改之外,它与问题中的代码基本相同。确定两个类别的属性名称的有效性的能力反映在可见矩形中黄色填充和粗描边宽度的外观上,同时将自定义的"data-price"属性添加到元素中。特别要注意 HTML DOM 元素和 SVG DOM 元素需要以不同的方式进行检查。
const testHtmlAttributeNameValidity = (elementNodeName, attributeName) => {
return (attributeName in document.createElement(elementNodeName));
return (
attributeName in document.createElement(elementNodeName)
);
};
const testHtmlAttributeName = (attributeName, attributeValue) => {
const elementNodeName = "p";
const nameIsValid = testHtmlAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
const element = document.querySelector(elementNodeName);
element.setAttribute(attributeName, attributeValue);
};
testHtmlAttributeName("id", "someHtmlId");
// This should result in "id" attribute being changed, changing the
// font color to green.
testHtmlAttributeName("color", "orange");
// While "color" IS a CSS property name, it is NOT an HTML DOM element
// attribute name. Therefore, this test should result in "color" NOT being
// recognized as a valid attribute name, resulting in the
// custom attribute name "data-color", with no effect on the
// actual styling of the element, i.e. this test should NOT
// turn the text orange. This test is included here
// to provide a contrast to what should happen with SVG DOM
// element style-like attributes like "fill", as shown below
// in the section testing SVG DOM element attribute names.
testHtmlAttributeName("price", "expensive");
// This should result in a new attribute with the name "data-price"
// and the value "expensive".
console.log(document.querySelector('div').innerHTML);
const svgNS = "http://www.w3.org/2000/svg";
const testSvgAttributeNameValidity = (elementNodeName, attributeName) => {
return ( // ********** THIS IS THE IMPORTANT CHANGE **********
attributeName in document.createElementNS(svgNS, elementNodeName).style ||
attributeName in document.createElementNS(svgNS, elementNodeName)
);
};
const testSvgAttributeName = (attributeName, attributeValue) => {
const elementNodeName = "rect";
const nameIsValid = testSvgAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
const element = document.querySelector(elementNodeName);
element.setAttribute(attributeName, attributeValue);
};
testSvgAttributeName("id", "someSvgId");
testSvgAttributeName("fill", "yellow"); // *** Now correctly identified as valid
testSvgAttributeName("price", "expensive");
console.log(document.querySelector('svg').innerHTML);
#someHtmlId {
color: green;
}
#someSvgId {
stroke-width: 5px;
}
<svg height="55">
<rect x="10" y="10" width="80" height="40" stroke="red"/>
</svg>
<div>
<p>Hello world</p>
</div>