【问题标题】:event currentTarget changes after setTimeout事件 currentTarget 在 setTimeout 之后发生变化
【发布时间】:2016-09-22 21:28:15
【问题描述】:

考虑:

let sel=document.getElementById('mys');

sel.onchange=function(e) {
  console.log(e.currentTarget===null); // false
  setTimeout(e => {
     console.log(e.currentTarget===null); // true
  }, 0, e);
 }
<select id="mys">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>
  • 为什么 e.currentTarget 在超时后会发生变化?这是浏览器(chrome)的错误吗?

  • 如何将事件的精确克隆传输到超时函数?我尝试了简单的克隆,但 currentTarget 不可写并且不能被覆盖..

【问题讨论】:

  • 只对可运行代码使用sn-ps
  • 看起来你需要关闭:jsfiddle.net/vrfv6gy7
  • 有趣,这是一个闭包吗?我认为闭包根本不会将 e 传递给 setTimeout ...您正在做的是 IIFE 吗?为什么会这样?
  • from MDN: “[Event.currentTarget] 标识事件的当前目标,因为事件遍历 DOM”。这不是静态属性。在事件完成冒泡后,它的计算结果将是null,这是有道理的。尝试改用event.target。 (只评论而不是回答,因为我不知道我是否正确。)

标签: javascript html


【解决方案1】:

受到Joseph Marikle's comment的启发,我找到了真正的原因:

DOM Standard § 2.9. Dispatching events 描述了算法,我在这里部分复制:

To **dispatch** an event to a target […] run these steps:
[…]
5. If target is not relatedTarget or target is event’s relatedTarget, then:
    […]
    9. While parent is non-null:
        […]
        8. Otherwise, set target to parent and then:
            […]
        9. If parent is non-null, then set parent to the result of invoking parent’s get the parent with event.
        […]
    […]
    12. Set event’s eventPhase attribute to CAPTURING_PHASE.
    […]
    14. For each tuple in event’s path, in reverse order:
        1. […] invoke […]
    15. For each tuple in event’s path, in order:
        1. If tuple’s target is non-null, then set event’s eventPhase attribute to AT_TARGET.
        2. Otherwise, set event’s eventPhase attribute to BUBBLING_PHASE.
        3. […] invoke […]
[…]
6. Set event’s eventPhase attribute to NONE.
7. Set event’s currentTarget attribute to null.
[…]

To **invoke** […] run these steps:
[…]
5. Initialize event’s currentTarget attribute to struct’s invocation target.
[…]

因此,在事件处理完所有阶段(捕获、at_target、冒泡)后,currentTarget 将被分配null

【讨论】:

  • 哇。这是我第一次注意到 currentTarget 为空。他们为什么要这样做?
  • 因为在整个 capture/at/bubbling 阶段完成后,没有currentTarget了,因为没有目标被处理了。
  • 哇 x2。我刚刚发现在所有事件处理程序中都传递了相同的确切事件对象。这意味着您可以将属性附加到e,并且它们可以被下一个处理程序访问。
  • @DenilsonSáMaia 但是currentTarget 不是null'ed with onMouseEnter,对此有何解释?
【解决方案2】:

event.currentTarget 和一些其他属性在退出事件处理程序后发生变化。

在事件处理程序中进行上下文切换 (setTimeout(... ,0);) 很常见,似乎正确传递包括 currentTarget 等在内的事件的唯一方法是克隆它,如下所示。

请注意,这只是“足够好”......克隆并不完全是原始事件,而且它的上下文不同......例如执行 clone.stopPropogation() 是没有意义的......

如果您需要更改克隆上的 currentTarget 等删除 !d.writable || !d.configurable || !d.enumerable ||

let sel=document.getElementById('mys');

function cloneEvent(e) {
	function ClonedEvent() {};
	let clone=new ClonedEvent();
	for (let p in e) {
		let d=Object.getOwnPropertyDescriptor(e, p);
		if (d && (!d.writable || !d.configurable || !d.enumerable || d.get || d.set)) {
			Object.defineProperty(clone, p, d);
		}
		else {
			clone[p] = e[p];
		}  
	}
	Object.setPrototypeOf(clone, e);
	return clone;
}

sel.onchange=function(e) {
	console.log(e.currentTarget);
	let clone=cloneEvent(e);
	setTimeout(e => {
		console.log(e.currentTarget);
	}, 0, clone);
}
<select id="mys">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
</select>

【讨论】:

  • 似乎只是将它分配给一个变量/常量并在 setTimeout 回调中使用它,它就可以工作。 const ct = e.currentTarget,然后在 setTimeout 回调中使用ct
【解决方案3】:

let sel=document.getElementById('mys');

sel.onchange=function({currentTarget}) {
  console.log(currentTarget===null); // false
  setTimeout(() => {
     console.log(currentTarget===null); // false
  }, 0);
 }
<select id="mys">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>

【讨论】:

    【解决方案4】:

    我建议您尝试使用其他浏览器,因为代码可以与我的代码一起使用,我认为代码 sn-p 没有任何问题。

    【讨论】:

    • 这如何回答这个问题?
    猜你喜欢
    • 2012-04-26
    • 1970-01-01
    • 2013-04-15
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多