I am using the <data>
element in HTML, which has decent support, but not sufficient for my purposes. The only extra functionality in HTMLDataElement
is a getter for value
, which returns the respective attribute.
Of course this is trivial to implement, just use the following code (after feature detection, of course)
class HTMLDataElement extends HTMLElement {
constructor() {
super();
}
get value() {
return this.getAttribute(`value`);
}
}
This works great. Only one problem: when using native APIs such as getElementById
, querySelector
, etc., the returned Node is not of instance HTMLDataElement
. How can I make it so, if this is even possible?
To be clear, I'd like to be able to do document.querySelector('foo').value
, which would act the same with or without browser support for <data>
.
(I'm well aware that I can just use .getAttribute('value')
instead of .value
. The point is I don't want to.)
After reaching out to Jonathan Neal* on Twitter, he provided a great example of how this can be done.
if (!this.HTMLDataElement) {
this.HTMLDataElement = this.HTMLUnknownElement;
const valueDescriptor = Object.getOwnPropertyDescriptor(HTMLDataElement.prototype, 'value');
Object.defineProperty(
HTMLDataElement.prototype,
'value',
valueDescriptor || {
get() {
return 'DATA' === this.nodeName && this.getAttribute('value');
}
}
);
}
console.log('data:', document.querySelector('data').value);
console.log('xdata:', document.querySelector('xdata').value);
<data value="this should work">
<xdata value="this should not work">
* For the unfamiliar, Jonathan Neal is large contributor of PostCSS plugins, and has created many JavaScript polyfills himself.