Home > database >  Using Symbol as DOM / HTMLElement attributes
Using Symbol as DOM / HTMLElement attributes

Time:09-04

In general custom HTML element attributes are discouraged but in some cases one have the choice between either having a collection of nodes that one saves and traverse for comparison or setting a property. As for the former that is often enough not a good choice.

Usually I would use dataset for this, but what if that is not a good option due to the script being used on any site where one have no control of what that site itself does?

Say one want to tag all <video> elements with a custom status property could one use Symbol() attached directly to the element? I have tested using Symbol() on dataset but it seems to be lost sometimes. As in: I can set it, log it, next call it is sometimes there, sometimes not. (Which also makes a use-case harder to make) Not sure why. Might have something to do with it being a media element? Symbol() is perhaps not valid as "dataset property name"?

Question the is if it is OK to set it directly on the element?

const symbol = Symbol.for("test");
// in some function:

// instead of:
some_element.dataset[symbol] = "foo";
// do:
some_element[symbol] = "foo";

Have not had any trouble with the latter (as in property disappearing).

Or could one perhaps use something like:

Object.defineProperty(element, symbol, {
        readonly: true,
        value: "foo"
});

CodePudding user response:

If your worry is that the site you have no control over could theoretically modify the data attribute - the same could occur with a symbol if the other site was really intent on breaking things.

const element = document.querySelector('div');

// Your code
const symbol = Symbol.for("test");
element[symbol] = "foo";

// Site's code
const [sym] = Object.getOwnPropertySymbols(element);
element[sym] = "bar";

// Your code, later
console.log(element[symbol]);
<div></div>

or, the site itself could use Symbol.for too.

const element = document.querySelector('div');

// Your code
const symbol = Symbol.for("test");
element[symbol] = "foo";

// Site's code
element[Symbol.for('test')] = "bar";

// Your code, later
console.log(element[symbol]);
<div></div>

Defining the property as readonly could work, but it'd also make you unable to alter the property later, which you might find would eventually be a problem.

A better solution would be to, instead of putting a property onto the object, to create your own collection that associates the elements with your custom values. If your own collection is scoped to only your own script (like with an IIFE), it'll then be impossible for other scripts to interfere with. This is a great place for a WeakMap. For example:

// Your code
(() => {
  const map = new WeakMap();
  const element = document.querySelector('div');
  // Set the value:
  map.set(element, 'foo');
  
  // Retrieve the value:
  console.log(map.get(element));
})();
<div></div>

The WeakMap values will continue to work and be unmodifiable by outside sources as long as the elements still exist in memory.

  • Related