如何改变DOM事件对象

时间:2018-01-15 10:27:15

标签: javascript dom javascript-events dom-events

我处于某种情况,我必须写自己的事件冒泡。因此,我需要将给定的 Event-Object (最初从Browser / DOM创建)传递给多个函数/实例,但我需要更改.target属性。

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

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

我试图变得非常聪明,只使用原始事件对象作为新对象的原型,只是遮蔽我想要变异的属性。像

Object.setPrototypeOf( customEvent, event );

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

Uncaught TypeError: Illegal invocation

如果我们使用代理处理程序,它似乎也可以工作,它也可用于遮蔽某些属性。我在这里遇到的唯一问题是,仍然无法通过代理执行stopPropagation之类的函数( Uncaught TypeError:Illegal invocation )。

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

如果你提到任何形式或形状的jQuery,你将遭受7年的不幸。

1 个答案:

答案 0 :(得分:1)

我使用了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
    }
  };
}

然后在代理中,我有

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

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

设置代理后,必须在处理click事件时构造代理对象。像

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之类的函数( Uncaught TypeError:Illegal invocation )。

在下面的示例中;我已将按钮替换为链接。假设我想使用.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对象。这就是我设置上面接收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>