如何突变DOM事件对象

jAn*_*ndy 5 javascript dom dom-events

我处在一种情况下,我不得不编写自己的事件冒泡。因此,我需要将给定的事件对象(最初是从浏览器/ DOM创建的)传递给多个函数/实例,但是例如,我需要更改.target属性。

问题:几乎所有属性的属性描述符都拒绝任何突变,删除或更改。此外,它似乎不是可以克隆或复制整个事件对象使用Object.assign或使用属性读取Object.getOwnPropertyNamesObject.keys

当然可以通过分配属性将所有属性手动复制到新对象中,但这似乎很愚蠢。

我试图变得非常聪明,只是将原始的事件对象用作新对象的原型,以仅遮盖我要更改的属性。喜欢

Object.setPrototypeOf( customEvent, event );
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做,则可以使用以下方式访问customEventthrow 属性:

未捕获的TypeError:非法调用

如果我们使用代理处理程序(也可以用于遮盖某些属性),它似乎确实起作用。我这里唯一的问题是,仍然无法像stopPropagation通过代理一样执行功能(Uncaught TypeError:再次非法调用)。

问题:操纵/扩展DOM事件对象的最佳实践是什么?


如果以任何形式或任何形式提及jQuery,您将遭受7年的不幸之苦。

Kar*_*elG 5

我使用了一个Proxy结合包装器来解决这个问题。

首先,设置一个充当代理包装器的函数。包装器接收一个 Event 对象并设置其值以供以后使用。下面将进一步解释其原因。

function AdaptedEvent(ev) {
  const initialEvent = ev;
  let newTarget = 'your new target'; // can be dynamic if you wish.

  // returning with a handler for proxy
  return {
    get: (target, prop) => {
      // proxy'ing stuff
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

然后在代理内部,我有

get: (target, prop) => {
  if (prop === 'target') {
    return newTarget ? newTarget : initialEvent.target;
  }
  return target[prop];
}
Run Code Online (Sandbox Code Playgroud)

.target在代理对象上调用时,它将执行上面 if-body 中描述的语句。它将返回一个新目标,而不是事件的初始目标。

设置代理后,您必须在处理单击事件时构造代理对象。喜欢

document.querySelector('#foo').addEventListener(
  'click',
  e => handleClick(new Proxy(e, new AdaptedEvent(e)))
);
Run Code Online (Sandbox Code Playgroud)

例子:

function AdaptedEvent(ev) {
  const initialEvent = ev;
  let newTarget = 'your new target'; // can be dynamic if you wish.

  // returning with a handler for proxy
  return {
    get: (target, prop) => {
      // proxy'ing stuff
    }
  };
}
Run Code Online (Sandbox Code Playgroud)
get: (target, prop) => {
  if (prop === 'target') {
    return newTarget ? newTarget : initialEvent.target;
  }
  return target[prop];
}
Run Code Online (Sandbox Code Playgroud)

但正如你提到的一个我在尝试时也看到的问题:

如果我们使用代理处理程序,它似乎确实有效,该代理处理程序也可用于隐藏某些属性。我在这里遇到的唯一问题是,仍然无法stopPropagation通过代理执行类似的函数(未捕获的类型错误:再次非法调用)。

在下面的例子中;我已将按钮替换为链接。假设我想通过使用来停止它的冒泡.preventDefault()。您可以看到它会抛出上述错误。

document.querySelector('#foo').addEventListener(
  'click',
  e => handleClick(new Proxy(e, new AdaptedEvent(e)))
);
Run Code Online (Sandbox Code Playgroud)
function AdaptedEvent(ev) {
  const initialEvent = ev;
  let newTarget = 'your new target';

  return {
    get: (target, prop) => {
      if (prop === 'target') {
        return newTarget ? newTarget : initialEvent.target;
      }
      return target[prop];
    }
  };
}

function handleClick(event) {
  console.log(event.target);
  console.log(event.which);
}

// binding click event.
document.querySelector('#foo').addEventListener('click', e => handleClick(new Proxy(e, new AdaptedEvent(e))));
Run Code Online (Sandbox Code Playgroud)

这样做的原因是,当调用该函数时,您已经丢失了调用的正确上下文preventDefault。一个解决方案是使用.bind. 但是你应该传递什么给 呢.bind?是的,原生Event对象。这就是为什么我设置了上面的内容AdaptedEvent函数来接收 Event 对象作为参数。现在,当对属性的调用是函数时(很容易被检查),您可以传递对象。

当您单击链接时,事件冒泡停止。

<button id="foo">click me!</button>
Run Code Online (Sandbox Code Playgroud)
function AptEvent(ev) {
  const initialEvent = ev;
  let newTarget = 'your new target';

  return {
    get: (target, prop) => {
      if (prop === 'target') {
        return newTarget ? newTarget : initialEvent.target;
      }
      return target[prop];
    }
  };
}

function handleClick(event) {
  event.preventDefault();
  console.log(event.target);
  console.log(event.which);
}

// binding click event.
document.querySelector('#foo').addEventListener('click', e => handleClick(new Proxy(e, new AptEvent(e))));
Run Code Online (Sandbox Code Playgroud)