【问题标题】:How to mutate a DOM event object如何改变 DOM 事件对象
【发布时间】:2018-06-23 23:16:18
【问题描述】:

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

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

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

我试图变得非常聪明,只使用原始事件对象作为新对象的原型,以隐藏我想要变异的属性。喜欢

Object.setPrototypeOf( customEvent, event );

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

未捕获的类型错误:非法调用

如果我们使用代理处理程序,它似乎确实有效,它也可以用来遮蔽某些属性。我在这里遇到的唯一问题是,仍然无法通过代理执行像 stopPropagation 这样的函数(Uncaught TypeError: Illegal invocation 再次)。

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


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

【问题讨论】:

  • "You will suffer from 7 years of misfortune if you mention jQuery in any form or shape." :rolling_on_the_floor_laughing:

标签: javascript dom dom-events


【解决方案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 中描述的语句。它将返回一个新目标,而不是事件的初始目标。

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

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 对象。这就是为什么我设置了上面的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>

【讨论】:

    猜你喜欢
    • 2011-11-21
    • 2011-05-22
    • 2018-05-19
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多