使用代理访问自定义元素的数据集

Using a proxy to access the dataset of a custom element

想象一下这个微不足道的自定义元素:

<my-el data-cute-number="7" id="foo"></my-el>

document.getElementById('foo').dataset.cuteNumber,正如预期的那样,returns 字符串“7”。我想创建一个代理来访问数据集属性,为我执行转换为 Number 因为我在组件代码中使用了很多 属性 并且希望避免重复手动转换它每次我访问它。我也不想在组件本身上为新的 属性(例如 get cuteNumber() { return Number(this.dataset.cuteNumber); })创建额外的 getter,因为我必须手动进行所有同步(因为我会还需要 setter),确保避免无限更新循环等

据我了解代理,这正是代理可以帮助我的地方。

customElements.define('my-el', class extends HTMLElement {
  constructor() {
    super();
    const proxy = new Proxy(this.dataset, {
      get: function(context, prop, receiver) {
        console.log(`Proxy getter executing for ${prop}`);
        switch (prop) {
          case 'cuteNumber':
            return Number(context[prop]);
            break;
          default: 
            return context[prop];
        }
      }
    });
  }
})

console.log(typeof document.getElementById('foo').dataset.cuteNumber);
<my-el data-cute-number="7" id="foo"></my-el>

这就是我卡住的地方。

访问dataset当前不会触发代理(内部console.log没有显示)。

谁能指出我正确的方向?甚至可以代理元素的 dataset 吗?

创建代理不会改变目标对象,它不会成为代理。代理是一个环绕目标的新对象。在您的代码中,您只是丢弃了 proxy 而从不使用它 - .dataset 属性 不受影响。您需要覆盖它或创建一个新的 属性:

customElements.define('my-el', class extends HTMLElement {
  get dataset() {
//^^^^^^^^^^^^^
    return new Proxy(super.dataset, {
//  ^^^^^^
      get: function(target, prop, receiver) {
        console.log(`Proxy getter executing for ${prop}`);
        if (prop == 'cuteNumber')
          return Number(target.cuteNumber);
        return Reflect.get(target, prop, receiver);
      }
    });
  }
});

console.log(typeof document.getElementById('foo').dataset.cuteNumber);
<my-el data-cute-number="7" id="foo"></my-el>
customElements.define('my-el', class extends HTMLElement {
  constructor() {
    super();
    this.numberdata = new Proxy(this.dataset, {
//  ^^^^^^^^^^^^^^^^^
      get: function(target, prop, receiver) {
        return Number(target[prop]);
      }
    });
  }
});

console.log(typeof document.getElementById('foo').numberdata.cuteNumber);
<my-el data-cute-number="7" id="foo"></my-el>