我遇到了Zone.js和非角度DOM元素事件的问题。
当我们将javascript函数分配给HTML标记中的任何DOM元素,然后以编程方式将相同的DOM元素重新分配给其他或相同的函数时,就会发生这种情况。
e.g。你有这个标记
<button id="button1" onclick="test1()">button 1</button>
然后在JS的某处你用其他函数重新分配button1 onclick
document.getElementById('button1').onclick = test3;
在这种情况下,会进行两次函数调用。第一个是原始分配的方法,然后第二个是新分配的方法。
我创建了一个PLUNKER来演示这种行为。
在示例中,我们在屏幕上有一个角度应用程序和2个非角度按钮。每个按钮最初分配一个功能,只是将一些文本打印到控制台。
一个按钮(onclick事件上的按钮1)只打印测试1方法。
另一个按钮(onclick事件上的按钮2)打印测试2方法。并且还为button1分配一个新功能。这个新函数打印测试3方法,名为。
情景 -
让应用程序加载。当您看到应用程序作品时,请执行以下操作: -
1.打开调试器控制台
2.单击按钮2
3.检查控制台
4.单击按钮1.
5.检查控制台。
期望。
•点击按钮2,我们应该看到测试2方法。
•单击按钮1,我们应该看到测试3方法。
实际的
•单击按钮2,我们会看到测试2方法。
•点击按钮1,我们看到测试1方法被调用。后跟测试3
方法叫做。
PLUNKER链接
https://plnkr.co/edit/ymKcHjWPrDiG73NfWBle?p=preview
在调试时,我看到一个方法称为DOM元素事件(来自onclick的test1),另一个方法(test3)由zonejs调用
(对于这个调试,我在本地机器上的zone.js文件中添加了console.log)
我看到zonejs注册了与屏幕上任何DOM元素相关的所有事件。
那么,这是我面临的问题吗?或者是预期的行为还是我在某个地方出错了?请帮忙解决一些信息/指示。
答案 0 :(得分:1)
没有角度但是zonejs导致了这种行为。使用一些试验错误。
https://plnkr.co/edit/cnDy4utsWYbhaXIBluNw?p=preview
window.onload
test2()
方法以删除并添加button1 答案 1 :(得分:1)
问题是区域补丁onclick
并使用addEventListener/removeEventListener
注册/删除按钮的回调。但onclick
注册了自己的独立处理程序,无法使用removeEventListener
删除。因此,您最终会为click
事件提供两个处理程序:
function patchProperty(obj, prop, prototype) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
...
desc.set = function (newValue) {
...
var previousValue = target[_prop];
if (previousValue) {
// !!!!!! ------------------ it doesn't remove onclick here ---------- !!!!!!
target.removeEventListener(eventName, previousValue);
}
if (typeof newValue === 'function') {
var wrapFn = function (event) {
console.log(' zone wrap function called for--\n');
console.log('targetID '+target.id);
console.log('event is '+eventName);
var result = newValue.apply(this, arguments);
if (result != undefined && !result) {
event.preventDefault();
}
return result;
};
target[_prop] = wrapFn;
// !!!!!! ------------------ but adds new handler here ---------- !!!!!!
target.addEventListener(eventName, wrapFn, false);
这种情况很容易在没有区域的情况下重现:
function onClickHandler() {
console.log('onClickHandler');
}
document.addEventListener("DOMContentLoaded", function() {
const button = document.querySelector('button');
button.removeEventListener('click', button.onclick);
button.addEventListener('click', ()=> {
console.log('addEventListener')
});
});
<button onclick="onClickHandler()">Hello</button>
答案 2 :(得分:1)
其中一个决议是修补&#34; desc.set&#34; zone.js的方法来解决这个问题。
function patchProperty(obj, prop, prototype) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
...
desc.set = function (newValue) {
...
var previousValue = target[_prop];
if (previousValue) {
target.removeEventListener(eventName, previousValue);
}//Start Fix
else{
if(originalDescGet){
if (typeof target['removeAttribute'] === 'function') {
target.removeAttribute(prop);
}
}
}//End fix