如何为 HTMLELement 创建代理

Ryt*_*Xia 5 javascript proxy dom ecmascript-6 es6-proxy

问题

如何为浏览器原生 DOM 对象创建代理?


背景

我想拦截元素样式的设置。所以我为 DOM 对象创建一个代理。但是,当我使用某些函数(例如getComputedStyle().

  const setHandler = (target: any, prop: PropertyKey, value: any, _receiver?: any) => {
    if (/*some condition*/) {
      target[prop] = value
    }
    return true
  }

  const getHandler = (target: any, prop: PropertyKey, _receiver?: any) => {
    return target[prop]
  }

  const style = new Proxy(el.style, {
    get: getHandler,
    set: setHandler
  })
  const classList = new Proxy(el.classList,{
    get: getHandler,
    set: setHandler
  })

  const proxy = new Proxy(el/*HTMLElement*/, {
    get: (target, prop, _receiver) => {
      if (prop === 'target') {
        return target
      }
      if (prop === 'style') {
        return style
      }
      if (prop === 'classList') {
        return classList
      }
      return getHandler(target, prop, target)
    },
    set: setHandler
  })

  const style = getComputedStyle(el)

Run Code Online (Sandbox Code Playgroud)

el是本机浏览器 DOM 对象。在我的代码中,有很多参数为 的方法el,这些方法可能会修改 el.

我想阻止其中一些方法修改el,因此我尝试代理该el对象。

但是在代理之后,DOM 对象的一些方法不能在代理对象上使用(如getComputedStyle())。


演示

我在下面创建了一个演示。

(function() {
  const node = document.querySelector('#demo')
  const proxy = new Proxy(node, {
    getPrototypeOf(target){
      return Object.getPrototypeOf(target)
    },
    get(target, prop, receiver){
      let value = target[prop]
      if (typeof value === 'function') {
        value = Function.prototype.bind.call(value, target)
      }
      return value
    },
    set(target, prop,value, receiver){
      target[prop] = value
    },
    apply(target, args, newTarget) {
      return Object.apply(target,args)
    },
  })
  console.log(proxy)
  console.log(Object.getPrototypeOf(proxy))         
 
  console.log(proxy.style)

  // error: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.
  const style = getComputedStyle(proxy)
  console.log(style)
})()
Run Code Online (Sandbox Code Playgroud)
<div id='demo'>demo</div>
Run Code Online (Sandbox Code Playgroud)