代码之家  ›  专栏  ›  技术社区  ›  Sagiv b.g

动态创建时多次调用事件处理程序

  •  0
  • Sagiv b.g  · 技术社区  · 8 年前

    我在找一个更好的办法来解决我的问题。
    我有一个函数,它返回一个动态地将事件处理程序附加到文档的函数,它被包装在 init 功能:

    const init = (events) => {
      const generateListener = listenForKey => ({ keyCode, key}) => {
        if (keyCode === listenForKey) {
          console.log(`action - ${key}`);
        }
      }
    
      events.forEach(({name, keyCode}) => {
          document.addEventListener(name, generateListener(keyCode))
        });
    }
    

    现在,当我运行此代码时,它将按预期工作:

    const someEvents = [
      {
        name: 'keypress',
        keyCode: 13
      },
      {
        name: 'keypress',
        keyCode: 43
      }
    ];
    init(someEvents);
    

    当我需要跑步时问题就开始了 初始 再次(我确实需要):

    init(someEvents);
    // ... at a future point in time
    init(someEvents);
    

    在本例中,处理程序将被调用两次,因为它注册了两次。
    这是按设计的,因为浏览器认为这是一个新的数据,而我每次都在传递一个新的函数实例。
    你可以在这里看到(按 Enter + 键):

    const init = (events) => {
      const generateListener = listenForKey => ({ keyCode, key}) => {
        if (keyCode === listenForKey) {
          console.log(`action - ${key}`);
        }
      }
    
      events.forEach(({name, keyCode}) => {
          document.addEventListener(name, generateListener(keyCode))
        });
    }
    
    
    const someEvents = [
      {
        name: 'keypress',
        keyCode: 13
      },
      {
        name: 'keypress',
        keyCode: 43
      }
    ];
    init(someEvents);
    // this will register the events again and will invoke the handlers twice
    //as we are passing "new" argument (a new function instance)
    init(someEvents);
    <div>Click the '+' or 'Enter' keys </div>

    一种可能的解决方案是使用 object parameter 或者做一些类似的事情,比如将处理程序存储在窗口中,并将它们作为单个实例附加到文档中。这样,浏览器将忽略同一处理程序的多个注册:

    const init2 = (events) => {
      const generateListener = listenForKey => ({ keyCode, key}) => {
        if (keyCode === listenForKey) {
          console.log(`action - ${key}`);
        }
      }
    
      events.forEach(({name, keyCode}) => {
            const handlerName = `on${name}${keyCode}`;
            this[handlerName] = this[handlerName] || generateListener(keyCode);
          document.addEventListener(name, this[handlerName]);
        });
    }
    
    const someEvents = [
      {
        name: 'keypress',
        keyCode: 13
      },
      {
        name: 'keypress',
        keyCode: 43
      }
    ];
    
    init2(someEvents);
    init2(someEvents);
    

    这很好,但是我们污染了全局窗口对象。

    我觉得我可能会错过一些东西,所以我在寻找更好的方法。

    1 回复  |  直到 8 年前
        1
  •  1
  •   Mrchief    8 年前

    根据 MDN ,将丢弃参数相同且位于同一目标上的多个事件侦听器。

    您的问题是因为您正在通过匿名函数(arrow funcs)进行注册。

    现在,既然已经有了一个全局对象(即不需要创建更多的全局对象),就可以使用它来创建命名函数,如:

    const someEvents = [
      {
        name: 'keypress',
        keyCode: 13,
        onkeypress13 () {...}
      },
      {
        name: 'keypress',
        keyCode: 43,
        onkeypress43 () {...}
      }
    ];
    

    这应该会解决dup的注册问题。下面是一个工作片段(不需要keycode check if条件,但它显示每个事件处理程序只调用一次)

    const init = (events) => {
        events.forEach((event) => {
            document.addEventListener(event.name, event[`on${event.name}${event.keyCode}`])
        });
    }
    
    const someEvents = [{
        name: 'keypress',
        keyCode: 13,
        onkeypress13() {
        console.log('13 pressed')
        }
    },
    {
        name: 'keypress',
        keyCode: 43,
        onkeypress43() {
        console.log('43 pressed')
        }
    }
    ];
    
    init(someEvents);
    // this will register the events again and will invoke the handlers twice
    //as we are passing "new" argument (a new function instance)
    init(someEvents);
    <div>Click the '+' or 'Enter' keys </div>
    推荐文章