如何将事件绑定到没有DOM对象的函数

How to Bind events to a function without a DOM object

本文关键字:DOM 对象 函数 事件 绑定      更新时间:2023-09-26

好的,所以我一直在做这个小技巧,以实现在不同时间向一个特定函数添加多个事件所需的功能。

$("#handler").bind("click",function(){ alert(1); });
$("#handler").bind("click",function(){ alert(2); });
$("#handler").bind("click",function(){ alert(3); });

当我简单地调用$("#handler").click()时,它会触发所有这些事件。

问题是,我可以做到这一点,而不制作一些任意的html对象来绑定它吗?

使用标准javascript函数执行此操作的等效方法是什么?

function handler(){ alert(1); }
function handler(){ alert(2); }
function handler(){ alert(3); }
handler();

^这只是点燃了最后一个,而不是全部三个。如何将多个事件绑定到一个函数而不是DOM元素?

如果您真的只是在寻找一种方法,将单个函数组合到一个更大的"函数集"中,然后可以在以后一次触发,那么这可以通过javascript中的Set对象来实现。示例:

// define the Set object that you want to store the functions inside:
var handlerFunctionCollection = new Set();

// add individual functions to the Set:
handlerFunctionCollection.add( function(){ console.log(1); } );
handlerFunctionCollection.add( function(){ console.log(2); } );
handlerFunctionCollection.add( function(){ console.log(3); } );
// trigger all functions in set using forEach on Set 
// and call() method on stored Function object:
handlerFunctionCollection.forEach(function(handlerFunction) {
    if(handlerFunction instanceof Function) {
        handlerFunction.call();
    }
});

注意,我对你的问题的解释是基于你说的:

当我简单地调用$("#handler").click()时,它会触发所有这些事件。

这表明您实际上并不是在寻找将函数直接绑定到事件的方法,而是在一个地方触发多个函数的方法,因为否则您就不会手动触发click事件。

Array对象或类似集合上使用Set的好处是,Set的成员是唯一的,因此,如果您多次尝试添加相同的函数(意外或其他),则添加的Function对象在Set中只会出现一次,因此,在触发forEach时,它只会被触发一次,因为它在CCD_ 11中只存在一次,而不是每次添加它。

在JavaScript中,每个函数实际上都是一个函数对象

在Javascript中,如果定义了多个同名函数,那么最后一个函数将实际运行。JavaScript functions在许多其他语言中与polymorphic不同,在其他语言中,funcA(parm1)funcA(parm1, parm2)可能是两个独立的函数,其中一个函数的运行取决于传递的参数数量。

在您的情况下,您需要以能够处理函数范围内不同情况的方式声明一个函数。

您正在寻找ObserverPublisher/Subscriber ("PubSub")的概念。

这些都是非常容易创建自己
这里有一个稍微清理过的仅限应用程序的代码版本(可能在DOM上运行,或者其他什么),用于运行下面的演示代码。

const trigger = $("#Trigger");
const system = Publisher();
// add my listeners
system
.on("trigger", triggerContentUpdate)
.on("update", updateView)
.on("update", logUpdate); // note how I have two things bound to the same event
// something that kicks off the "trigger" event
// could be a timer, or an AJAX call, or anything else
trigger.onclick = () => system.fire("trigger");

假设你有一个Publisher库,并且在你的应用程序中有上面的监听器函数,这几乎是你必须编写的所有JS才能使下面的演示工作。

我在实践中编写的库,以及演示的实现如下。

/*** LIBRARY CODE ***/
// Channel.js
const Channel = () => {
  const listeners = new Set();
  const channel = {
    listen(listener) {
      listeners.add(listener);
      return channel;
    },
    ignore(listener) {
      listeners.delete(listener);
      return channel;
    },
    notify(data) {
      listeners.forEach(react => react(data));
      return channel;
    }
  };
  return channel;
};
// Publisher.js
const Publisher = () => {
  const channels = {};
  function getChannel(name) {
    const channel = channels[name] || Channel();
    channels[name] = channel;
    return channel;
  }
  const publisher = {
    on(name, subscriber) {
      getChannel(name).listen(subscriber);
      return publisher;
    },
    off(name, subscriber) {
      getChannel(name).ignore(subscriber);
      return publisher;
    },
    fire(name, data) {
      getChannel(name).notify(data);
      return publisher;
    }
  };
  return publisher;
}
/*** END OF LIBRARY CODE ***/
/*** APPLICATION CODE ***/
// const Publisher = require("publisher"); or import Publisher from "publisher";
const $ = selector => document.querySelector(selector);
const trigger = $("#Trigger");
const input = $("#Content");
const output = $("#Result");
const log = $("#Log");
const system = Publisher();
system
  .on("trigger", () => {
    const content = input.value;
    system.fire("update", content);
  })
  .on("update", content => output.value = content)
  // normally, I never use innerHTML, and my logic and view are never attached, but this is dirt-simple now
  .on("update", content => log.innerHTML += `<li>[${ (new Date()).toISOString() }]: ${content}</li>`);
trigger.onclick = () => system.fire("trigger");
/*** END OF APPLICATION CODE ***/
<h3>Input</h3>
<div>
  <input id="Content" type="text">
  <button id="Trigger">Update</button>
</div>
<h3>Output</h3>
<output id="Result"></output>
<h3>Log</h3>
<ul id="Log"></ul>

当然,我使用的是相对现代的语法,但没有什么是不能在旧的兼容代码中重写的,也没有什么是无法转换并赋予polyfill的,可以一直到IE7/8支持。

如果我想让一些任意对象成为一个发布者,我也可以很快做到。