【问题标题】:Issue with zone.js and non angular dom element eventszone.js 和非角度 dom 元素事件的问题
【发布时间】:2017-07-19 13:17:10
【问题描述】:

我遇到了 Zone.js 和非角度 DOM 元素事件的问题。

当我们将 javascript 函数分配给 HTML 标记中的任何 DOM 元素,然后以编程方式将相同的 DOM 元素重新分配给其他或相同的函数时,就会发生这种情况。

例如你有这个标记

<button id="button1" onclick="test1()">button 1</button>

然后在 JS 的某个地方,你用其他一些函数重新分配 button1 onclick

 document.getElementById('button1').onclick = test3;

在这种情况下,会进行两个函数调用。第一个是原始分配的方法,然后第二个是新分配的方法。

我创建了一个 PLUNKER 来演示这种行为。

在示例中,我们在屏幕上有一个 Angular 应用程序和 2 个非 Angular 按钮。每个按钮最初都分配了一个功能,该功能仅将一些文本打印到控制台。

一个按钮(onclick 事件上的按钮 1)只打印 调用的测试 1 方法。

另一个按钮(onclick 事件上的按钮 2)打印 调用的测试 2 方法。 并且还为 button1 的 onclick 分配了一个新函数。这个新函数打印 test 3 method called.

场景-

让应用程序加载。当您可以看到 应用工作 时,请执行以下操作:-
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 元素相关的事件。

那么,这是我面临的问题吗?或者这是预期的行为还是我在哪里出错了?请提供一些信息/指针。

【问题讨论】:

  • Plunker 似乎什么也没做。不过可能是(临时)Plunker 问题。
  • zone.js 仅影响在区域内进行的事件绑定。 Angular 在这样的区域内运行。如果您在室外进行,则不涉及区域。
  • @GünterZöchbauer, NgZone 不参与。但根区仍然监听所有事件
  • @Maximus 为什么要这样做?这是新事物吗?
  • @GünterZöchbauer,我的意思是它修补和拦截所有异步事件。它与 Angular 无关

标签: angular dom zone.js


【解决方案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>

【讨论】:

  • 是的..明白了。谢谢 :) 所以说 zone.js 有问题是正确的??
  • 很难说。从表面上看,是的,zone.js 似乎没有正确删除处理程序,但我不知道有什么想法。创建一个 github 问题,我们将看到。如果您这样做,请在此处提供参考。
  • 好吧,我们继续检查 zone.js 代码,代码本身有对问题的引用。 github.com/angular/zone.js/issues/525
  • 我将它添加到我之前的评论中。再次,github.com/angular/zone.js/issues/525
【解决方案2】:

不是 angular 而是 zonejs 导致了这种行为。处理一些试验错误。

https://plnkr.co/edit/cnDy4utsWYbhaXIBluNw?p=preview

  1. 将内联事件侦听器移至window.onload
  2. 更新了 test2() 方法以删除和添加按钮 1 的侦听器

【讨论】:

  • 是的..在我的问题评论中,我们进行了同样的讨论。所以在遗留系统中,这会产生一个问题......你遇到过这样的问题吗?如何克服它们?
【解决方案3】:

解决方法之一是修补 zone.js 的“desc.set”方法来解决此问题。

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

【讨论】:

  • 这个解决方案似乎有效。谢谢 :) 我会用这个问题更新 GitHub 并将链接放在这里。
猜你喜欢
  • 2019-04-08
  • 1970-01-01
  • 2018-10-14
  • 2014-02-18
  • 2013-10-22
  • 1970-01-01
  • 1970-01-01
  • 2020-04-23
  • 1970-01-01
相关资源
最近更新 更多