我处于某种情况,我必须写自己的事件冒泡。因此,我需要将给定的 Event-Object (最初从Browser / DOM创建)传递给多个函数/实例,但我需要更改.target
属性。
问题:几乎所有属性的属性描述符都拒绝任何突变,删除或更改。此外,似乎无法使用Object.assign
克隆或复制整个事件对象,或使用Object.getOwnPropertyNames
或Object.keys
读取属性。
当然可以手动通过分配将所有属性复制到新对象中,但这看起来很愚蠢。
我试图变得非常聪明,只使用原始事件对象作为新对象的原型,只是遮蔽我想要变异的属性。像
Object.setPrototypeOf( customEvent, event );
但是,如果我这样做,任何对customEvent
属性的访问都会抛出:
Uncaught TypeError: Illegal invocation
如果我们使用代理处理程序,它似乎也可以工作,它也可用于遮蔽某些属性。我在这里遇到的唯一问题是,仍然无法通过代理执行stopPropagation
之类的函数( Uncaught TypeError:Illegal invocation )。
问题:操作/扩展DOM事件对象的最佳做法是什么?
如果你提到任何形式或形状的jQuery,你将遭受7年的不幸。
答案 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>