window 节点上的过滤事件与 DOM 节点上的单个事件

Filtering events on window vs individual events on DOM nodes

考虑以下代码示例:

import R from 'ramda';
import {Observable} from 'Rx';

var allClicks_ = Observable.fromEvent(window, 'click').share();

var getClicks = function(klass) {
  return allClicks_.filter(e => {
    return R.contains(klass, e.target.classList);
  });
};

getClicks('red').subscribe(x => {
  render('RED: ' + x.target.className);
});

getClicks('blue').subscribe(x => {
  render('BLUE: ' + x.target.className);
});

我没有向“.red”和“.blue”添加点击事件侦听器,而是向window 添加了事件侦听器并过滤了“.red”和“.blue”上的事件。

现在这样的代码会出什么问题?它比向单个 DOM 节点添加事件侦听器更(或更不)有效吗?或者它没有性能优势?

编辑:共享热门 Observable,以便仅附加一个事件处理程序。

这是委托事件处理程序的示例。这个模式非常有用。事实上,jQuerydojo 这样的库非常有用,内置了对这种模式的支持(参见 jQuery.on and dojo.onselector 参数)。

向每个 DOM 节点添加事件处理程序实际上是一个 O(n) 操作,而这是一个 O(1) 操作。随着匹配 DOM 节点数量的增长,委托事件处理程序模式实现了更大的好处。

会出现什么问题?

  • 如果在您的顶级元素(在本例中为 window)和您的目标元素之间附加了一个事件处理程序,并且该事件处理程序执行 ev.stopPropagation(),那么您的委托处理程序将永远不会看到该事件。

  • 如果您的过滤器功能过于复杂和缓慢,那么浏览器将不得不花费比平时更多的时间运行过滤器。

  • 添加事件处理程序后,您将获得 DOM 个节点的事件。这通常被视为 好事 。但是,如果出于某种原因您没有预料到它,那么它可能会让您失望。

请注意,在您的特定示例中,实际上注册了 两个 click 处理程序。您可以通过 share:

将其缩减为单个实例
var allClicks_ = Observable.fromEvent(window, 'click').share();