3.5 移除事件处理程序

有时候,我们需要停用以前注册的事件处理程序。可能是因为页面的状态发生了变化,导致相应的操作不再有必要。处理这种情形的一种典型做法,就是在事件处理程序中使用条件语句。但是,如果能够完全移除处理程序绑定显然更有效率。

假设我们希望折叠样式转换器在页面没有使用正常样式的情况下保持扩展状态,即当Narrow Column或 Large Print按钮被选中时,单击样式转换器的背景区域不应该引发任何操作。为此,可以在单击非默认样式转换按钮时,调用.off()方法移除折叠处理程序,如代码清单3-18所示。

代码清单3-18

  1. $(document).ready(function() {
  2. $('#switcher').click(function(event) {
  3. if (!$(event.target).is('button')) {
  4. $('#switcher button').toggleClass('hidden');
  5. }
  6. });
  7.  
  8. $('#switcher-narrow, #switcher-large').click(function() {
  9. $('#switcher').off('click');
  10. });
  11. });

现在,如果单击Narrow Column按钮,样式转换器(<div>)上的单击处理程序就会被移除。然后,再单击背景区域将不会导致它折叠起来。但是,按钮本身的作用却失效了!由于为使用事件委托而重写了按钮处理代码,因此按钮本身也带有样式转换器(<div>)的单击事件处理程序。换句话说,在调用$('#switcher').off('click')时,会导致按钮上绑定的两个事件处理程序都被移除。

3.5.1 为事件处理程序添加命名空间

显然,应该让对.off()的调用更有针对性,以避免把注册的两个单击处理程序全都移除。达成目标的一种方式是使用事件命名空间,即在绑定事件时引入附加信息,以便将来识别特定的处理程序。要使用命名空间,需要退一步使用绑定事件处理程序的非简写方法,即.on()方法本身。

我们为.on()方法传递的第一个参数,应该是想要截获的事件的名称。不过,在此可以使用一种特殊的语法形式,即对事件加以细分,参见代码清单3-19。

代码清单3-19

  1. $(document).ready(function() {
  2. $('#switcher').on('click.collapse', function(event) {
  3. if (!$(event.target).is('button')) {
  4. $('#switcher button').toggleClass('hidden');
  5. }
  6. });
  7. $('#switcher-narrow, #switcher-large').click(function() {
  8. $('#switcher').off('click.collapse');
  9. });
  10. });

对于事件处理系统而言,后缀 .collapse 是不可见的。换句话说,这里仍然会像编写.on('click')一样,让注册的函数响应单击事件。但是,通过附加的命名空间信息,则可以解除对这个特定处理程序的绑定,同时不影响为按钮注册的其他单击处理程序。

 稍后读者就会看到,还有另一种可以让.off()调用更有针对性的方式。不过,事件命名空间却是很有用的工具。第11章将会介绍到,在创建插件时使用这个工具会特别方便。

3.5.2 重新绑定事件

现在单击Narrow Column或Large Print按钮,会导致样式转换器的折叠功能失效。可是,我们希望该功能在单击Default按钮时恢复。为此,应该在Default按钮被单击时,重新绑定事件处理程序。

首先,应该为事件处理程序起个名字,以便多次使用,参见代码清单3-20。

代码清单3-20

  1. $(document).ready(function() {
  2. var toggleSwitcher = function(event) {
  3. if (!$(event.target).is('button')) {
  4. $('#switcher button').toggleClass('hidden');
  5. }
  6. };
  7. $('#switcher').on('click.collapse', toggleSwitcher);
  8. });

我们注意到,这里使用了另一种定义函数的语法,即没有使用函数声明(前置function关键字),而是将一个匿名函数表达式指定给了一个局部变量。除了两点微妙的差异(但在这里并不存在)之外,无论使用哪种语法,它们的功能都是等价的。这里使用函数表达式只是为了从形式上让事件处理程序与其他函数定义显得类似。

而且,我们知道传递给.on()的第二个参数是一个函数引用。在此需要强调一点,使用命名函数时,必须省略函数名称后面的圆括号。圆括号会导致函数被调用,而非被引用

在函数有了可以引用的名字之后,将来就可以再次绑定而无需重新定义它了,如代码清单3-21所示

代码清单3-21

  1. //未完成的代码
  2. $(document).ready(function() {
  3. var toggleSwitcher = function(event) {
  4. if (!$(event.target).is('button')) {
  5. $('#switcher button').toggleClass('hidden');
  6. }
  7. };
  8. $('#switcher').on('click.collapse', toggleSwitcher);
  9. $('#switcher-narrow, #switcher-large').click(function() {
  10. $('#switcher').off('click.collapse');
  11. });
  12. $('#switcher-default').click(function() {
  13. $('#switcher')
  14. .on('click.collapse', toggleSwitcher);
  15. });
  16. });

这样,切换样式转换器的行为当文档加载后会被绑定。当单击Narrow Column或Large Print 按钮时会解除绑定,而当此后再单击Normal按钮时,又会恢复绑定。

使用命令函数还有另外一个好处,即不必再使用事件命名空间。因为.off()可以将这个命名函数作为第二个参数,结果只会解除对特定处理程序的绑定。但这样就会遇到另一个问题,当在jQuery中把处理程序绑定到事件时,之前绑定的处理程序仍然有效。在这个例子中,每次点击Normal,就会有一个toggleSwitcher的副本被绑定到样式转换器。换句话说,在用户单击Narrow或Large Print之前(这样就可以一次性地解除对toggleSwitcher的绑定),每多单击一次都会多调用一次这个函数。

在绑定toggleSwitcher偶数次的情况下,单击样式转换器(不是按钮),好像一切都没有发生变化。事实上,这是因为切换了hidden类偶数次,结果状态与开始的时候相同。为了解决这个问题,可以在用户单击任意按钮时解除绑定,并在确定单击按钮的ID是switcher-default的情况下再重新绑定,参见代码清单3-22。

代码清单3-22

  1. $(document).ready(function() {
  2. var toggleSwitcher = function(event) {
  3. if (!$(event.target).is('button')) {
  4. $('#switcher button').toggleClass('hidden');
  5. }
  6. };
  7. $('#switcher').on('click', toggleSwitcher);
  8. $('#switcher button').click(function() {
  9. $('#switcher').off('click', toggleSwitcher);
  10. if (this.id== 'switcher-default') {
  11. $('#switcher').on('click', toggleSwitcher);
  12. }
  13. });
  14. });

对于只需触发一次,随后要立即解除绑定的情况也有一种简写方法——.one(),这个简写方法的用法如下:

  1. $('#switcher').one('click', toggleSwitcher);

这样会使切换操作只发生一次,之后就再也不会发生。