如何改变DOM事件对象

10

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

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

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

我试图聪明地使用原始事件对象作为新对象的原型,以便仅遮盖我想要突变的属性。像这样:

Object.setPrototypeOf( customEvent, event );

但如果我这样做,对于customEvent的任何属性访问都会抛出:

Uncaught TypeError: Illegal invocation

如果我们使用一个代理处理程序(Proxy-handler),似乎确实可以工作,它还可以用来隐藏某些属性。 我在这里唯一的问题是,仍然无法在代理上执行像stopPropagation这样的函数(再次出现Uncaught TypeError: Illegal invocation)。

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


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


4
如果以任何形式提到 jQuery,你将会遭受七年的厄运。 - Anson Kao
如果你以任何形式提到jQuery,你将会遭受7年的不幸。 - alex
1个回答

5
我使用了 Proxy 来解决这个问题,结合一个包装器。
首先,设置一个函数,作为代理的包装器。包装器接收事件对象并设置其值以供后续使用。下面会进一步解释原因。
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
    }
  };
}

然后在代理服务器内,我有以下设置

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

当在您的代理对象上调用.target时,它将执行上述if体中描述的语句。它将返回一个新的目标,而不是事件的初始目标。
一旦您设置了代理,处理单击事件时,您必须构造一个代理对象。例如:
document.querySelector('#foo').addEventListener(
  'click',
  e => handleClick(new Proxy(e, new AdaptedEvent(e)))
);

例子:

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))));
<button id="foo">click me!</button>

但是,正如您提到的问题,在我尝试时也遇到了这个问题:
引用: 使用代理处理程序似乎可以解决这个问题,代理处理程序还可以用于隐藏某些属性。但我在这里遇到的唯一问题是,仍然无法在代理上执行像stopPropagation这样的函数(再次出现“非法调用”的错误)。
在下面的示例中,我将按钮替换为链接。假设我想使用.preventDefault()停止其冒泡。您可以看到它会抛出上述错误。

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))));
<a id="foo" href="www.google.com">click me!</a>

由于调用该函数时,您已失去了调用 preventDefault 的正确上下文。 解决此问题的方法是使用 .bind。 但是,您应该传递什么给 .bind? 是的,是本机的 Event 对象。 这就是我设置上述 AdaptedEvent 函数的原因,它接收一个事件对象作为参数。 现在,当调用属性时是一个函数(可以轻松检查时),您可以传递该对象。
当您单击链接时,阻止事件冒泡。

function AdaptedEvent(ev) {
  const initialEvent = ev;
  let newTarget = 'your new target';

  return {
    get: (target, prop) => {
      if (Object.prototype.toString.call(target[prop]) === '[object Function]') {
        return target[prop].bind(initialEvent);
      }
      else 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 AdaptedEvent(e))));
<a id="foo" href="www.google.com">click me!</a>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接