官术网_书友最值得收藏!

Removing an event handler

There are times when we will be done with an event handler we previously registered. Perhaps the state of the page has changed such that the action no longer makes sense. It is typically possible to handle this situation with conditional statements inside our event handlers, but it may be more elegant to unbind the handler entirely.

Suppose that we want our collapsible style switcher to remain expanded whenever the page is not using the normal style. While the Narrow Column or Large Print button is selected, clicking on the background of the style switcher should do nothing. We can accomplish this by calling the the .off() method to remove the collapsing handler when one of the non-default style switcher buttons is clicked:

$(document).ready(function() {
  $('#switcher').click(function(event) {
    if (!$(event.target).is('button')) {
      $('#switcher button').toggleClass('hidden');
    }
  });
 $('#switcher-narrow, #switcher-large').click(function() {
 $('#switcher').off('click');
 });
});

Listing 3.18

Now when a button such as Narrow Column is clicked, the click handler on the style switcher <div> is removed, and clicking on the background of the box no longer collapses it. However, the buttons don't work anymore! They are affected by the click event of the style switcher <div> as well, because we rewrote the button-handling code to use event delegation. This means that when we call $('#switcher').off('click'), both behaviors are removed.

Giving namespaces to event handlers

We need to make our .off() call more specific so that it does not remove both of the click handlers we have registered. One way of doing this is to use event namespacing. We can introduce additional information when an event is bound that allows us to identify that particular handler later. To use namespacing, we need to return to the non-shorthand method of binding event handlers, the .on() method itself.

The first parameter we pass to .on() is the name of the event we want to watch for. We can use a special syntax here, though, that allows us to subcategorize the event:

$(document).ready(function() {
 $('#switcher').on('click.collapse', function(event) {
    if (!$(event.target).is('button')) {
      $('#switcher button').toggleClass('hidden');
    }
  });
  $('#switcher-narrow, #switcher-large').click(function() {
 $('#switcher').off('click.collapse');
  });
});

Listing 3.19

The .collapse suffix is invisible to the event handling system; click events are handled by this function, just as if we wrote .on('click'). However, the addition of the namespace means that we can unbind just this handler without affecting the separate click handler we wrote for the buttons.

Tip

There are other ways of making our .off() call more specific, as we will see in a moment. However, event namespacing is a useful tool in our arsenal. It is especially handy in the creation of plugins, as we'll see in later chapters.

Rebinding events

Now clicking on the Narrow Column or Large Print button causes the style switcher collapsing functionality to be disabled. However, we want the behavior to return when the Default button is pressed. To do this, we will need to rebind the handler whenever Default is clicked.

First, we should give our handler function a name so that we can use it more than once without repeating ourselves:

$(document).ready(function() {
 var toggleSwitcher = function(event) {
    if (!$(event.target).is('button')) {
      $('#switcher button').toggleClass('hidden');
    }
  };
 $('#switcher').on('click.collapse', toggleSwitcher);
});

Listing 3.20

Note that we are using a new syntax here for defining a function. Rather than use a function declaration (defining the function by leading with the function keyword and naming it), we use an anonymous function expression, assigning a no-name function to a local variable. Aside from a couple of subtle differences that don't apply in this case, the two syntaxes are functionally equivalent. Here, our use of the function expression is a stylistic choice to make our event handlers and other function definitions resemble each other more closely.

Also, recall that we are passing .on() a function reference as its second argument. It is important to remember when referring to a function that we must omit parentheses after the function name; parentheses would cause the function to be called rather than referenced.

Now that the function can be referenced, we can bind it again later without repeating the function definition:

// Unfinished code
$(document).ready(function() {
  var toggleSwitcher = function(event) {
    if (!$(event.target).is('button')) {
      $('#switcher button').toggleClass('hidden');
    }
  };
  $('#switcher').on('click.collapse', toggleSwitcher);
  $('#switcher-narrow, #switcher-large').click(function() {
    $('#switcher').off('click.collapse');
  });
  $('#switcher-default').click(function() {
    $('#switcher')
 .on('click.collapse', toggleSwitcher);
  });
});

Listing 3.21

Now the toggle behavior is bound when the document is loaded, unbound when Narrow Column or Large Print is clicked, and rebound when Normal is clicked after that.

Since we have named the function, we no longer need to use namespacing. The .off() method can take a function as a second argument; in this case, it unbinds only that specific handler. However, we have run into another problem. Remember that when a handler is bound to an event in jQuery, previous handlers remain in effect. In this case, each time Normal is clicked, another copy of the toggleSwitcher handler is bound to the style switcher. In other words, the function is called an extra time for each additional click until the user clicks Narrow or Large Print, which unbinds all of the toggleSwitcher handlers at once.

When an even number of toggleSwitcher handlers are bound, clicks on the style switcher (but not on a button) appear to have no effect. In fact, the hidden class is being toggled multiple times, ending up in the same state it was when it began. To remedy this problem, we can unbind the handler when a user clicks on any button, and rebind only after ensuring that the clicked button's ID is switcher-default:

$(document).ready(function() {
  var toggleSwitcher = function(event) {
    if (!$(event.target).is('button')) {
      $('#switcher button').toggleClass('hidden');
    }
  };
 $('#switcher').on('click', toggleSwitcher);
  $('#switcher button').click(function() {
 $('#switcher').off('click', toggleSwitcher);
 if (this.id == 'switcher-default') {
 $('#switcher').on('click', toggleSwitcher);
 }
  });
});

Listing 3.22

A shortcut is also available for the situation in which we want to unbind an event handler immediately after the first time it is triggered. This shortcut, called .one(), is used as follows:

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

This would cause the toggle action to occur only once.

主站蜘蛛池模板: 巩留县| 砀山县| 太保市| 玉树县| 慈溪市| 且末县| 塘沽区| 大宁县| 专栏| 涿鹿县| 蒙自县| 朝阳县| 武鸣县| 黑水县| 岗巴县| 昌江| 库尔勒市| 文水县| 古蔺县| 张家港市| 犍为县| 喀喇沁旗| 石阡县| 逊克县| 买车| 东乡县| 昌平区| 许昌市| 双辽市| 民乐县| 上饶县| 嘉义县| 兰西县| 泗洪县| 柘城县| 古田县| 云龙县| 仲巴县| 香格里拉县| 鹿邑县| 洛阳市|