I want to programmatically add attributes to DOM elements (e.g. HTML or SVG elements) dynamically at run time, i.e. based on user input. If the attribute is a standard one, I will use the name as is, but if it is not, i.e. if it requires transforming into a custom attribute, then I want to add the "data-" prefix to it.
So, is there a way of determining whether or not a string represents a "standard/normal" DOM attribute?
For example, say I have an SVG rectangle as follows:
<rect x="10" y="10" width="80" height="40" stroke="black"/>
Note that the fill color is not (yet) specified.
The user requests adding two attributes:
- an attribute named "fill" with the value "red", which should yield
fill="red"
(note the absence of a prefix) - an attribute named "price" with the value "expensive", which should yield
data-price="expensive"
(note thedata-
prefix)
How do I dynamically distinguish between these two? The fill
attribute isn't present in my current DOM element so I can't check if there is a pre-existing value for an attribute of that name. I also don't believe I can even check whether an error is thrown if I create the attribute without the prefix because, as far as I can tell, at least my current browser (Chrome v61.0) will allow me to add an attribute of price="expensive"
(without the data-
prefix) even though that is not best practice.
I want something like the following:
const elementNodeName = "rect";
const attributeName = "height"; // or, e.g. "price"
const nameIsValid = testAttributeNameValidity(elementNodeName, attributeName);
if (!nameIsValid) attributeName = 'data-' + attributeName;
Is there any pre-existing testAttributeNameValidity
-type functionality out there?
I've tagged this with javascript
as I'm looking for a Javascript solution.
** UPDATE: Solution from other SO answer works in HTML but not SVG **
Based on the suggested link in the comments, I tried using attributeName in document.createElement(elementNodeName)
which is supposed to return true
for a valid attribute name and false
for a non-valid one. When I use the valid attribute name "id" for an HTML element (<p>
), this approach works.
In contrast, I still haven't gotten this approach to work in SVG. I tried converting createElement
to createElementNS
, and that still does not seem to fix the problem. See my test code below. The critical functions in the test code are testHtmlAttributeNameValidity
and testSvgAttributeNameValidity
. If you run the code and check the output in the console, it shows that it works properly for HTML, i.e. it produces id="someId" data-price="expensive"
. However, for SVG, it only produces the undesired output: data-fill="yellow" data-price="expensive"
.
If it makes a difference, I'm working in Chrome v61.
Test code:
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>
Correctly working code should change the rectangle fill from black to yellow, which does not happen.
The comment from @ccprog helped me solve this problem. In summary, for SVG DOM elements, I needed the original check plus a boolean-OR'd check that is identical but with the addition of the
.style
property.As shown in the question, the validity of an HTML DOM element attribute name can be determined by checking as follows:
However, this approach will not work for SVG DOM elements. Specifically, the following will determine the validity of true attribute names like "id", but will not determine the validity of style-like attribute names like "fill":
However, simply duplicating that test and adding the
.style
property to one of the two checks will allow both categories of attribute names to be checked, as follows:This is demonstrated in the following code which is essentially identical to the code in the question except for the above-noted change. The ability to determine the validity of both categories of attribute name is reflected in the appearance of both yellow fill and thick stroke width in the visible rectangle while at the same time adding the custom "data-price" attribute to the element. Note in particular how HTML DOM elements and SVG DOM elements need to be checked differently.