开发者

JQuery - replace handler for live event

开发者 https://www.devze.com 2023-02-28 03:55 出处:网络
My original question I\'ve managed to answer by cobbling other answers together from this site (thank you StackOverflow!) - just posting here to help others wondering.

My original question I've managed to answer by cobbling other answers together from this site (thank you StackOverflow!) - just posting here to help others wondering.

I have a live event like:

function addValidator(selector, validator) {
    // Add the selector and validator to a hash map
    $(selector).live('change', validateTag);
}

The table rows are going to vary in number at any time and I don't want to tightly couple the table creation code with the validation code.

Later another validation function comes along not knowing anything about the first, and I need to avoid that same live selector firin开发者_JAVA技巧g the validateTag function twice, which is going to aggregate all validation messages and present a single UI about a given input.

So - each time someone calls addValidator I need to actually REPLACE the live event handler, or more specifically, expand on its selector (since I'm still calling validateTag, I just need the live handler to cover more ground). I'll post the answer I came up with below, happy to hear more.

-- Update to clarify what I'm doing

I have several pages with dynamic tables that could include any number of rows. I don't want the validation and the table management code to be tightly coupled. I want to be able to set out inputs in the table rows like:

<input class="required email"/>

And have validation validate it for both the required and email rules onchange(). In code:

Valid.addValidator('input.required', Valid.required);
Valid.addValidator('input.email', Valid.email);

Obviously as rows are added and removed I could keep reapplying the events to the inputs, but that tightly couples the table builder code and the validation code.

Another hack is to just build a single live event like:

$('input, select, textarea').live('change', Valid.validateTag);

And then use a hashmap to look up what exactly applies to this specific tag, but that assumes the only things I'll ever validate are inputs, selects and textareas, and that the only kind of validation I'll ever want to do is onchange.

What I really want is to let any kind of validation rule match any tags by selector, without either of the above. The second hack isn't too bad, but it is a false assumption and is inflexible to custom controls that don't use one of those 3 basic tags.


You can pass in the existing event type ('click') and the original handler (in this case validateTag) to the die() function to remove the handler.


Maybe you could use namespaced events:

$(selector).live('change.namespace', validateTag);
$(selector).unbind('.namespace');


Updated after a lot of tinkering and taking suggestions from the other answers here.

addRule: function(selector, validator) {
    // Save the rule
    Valid.rules.push({selector: selector, validator: validator});

    // Hunt for an existing live selector from prev addRule calls and expand on it if need be
    var existingLiveEvents = $(document).data('events');
    if (existingLiveEvents.change) {
        var existing = existingLiveEvents.change.find(function (item) { return item.origHandler == Valid.validateThis; });
        if (existing) {
            // Expand the selector to include past rules
            selector = existing.selector + ', ' + selector;

            // And kill off the old validate selector
            $(existing.selector).die('change', Valid.validateThis);
        }
    }

    $(selector).live('change', Valid.validateThis);
}

validateThis() uses the jQuery .is() method to check each rule for whether this tag is affected by that rule, and if so runs the validator on it.

If you look carefully, there's also a .find() call in there from an array library I made - it does what you'd expect: Iterate an array until a comparator function you pass in returns true. Then it hands you the item it stopped on, or null.

If people are interested in this library I'm happy to consider going open source with it. The jQuery validation plugin is interesting, but it doesn't seem to handle the dynamic sets of input I'm using. There's also some other magic in my library that gets the label for an input based on a ruleset.


Maybe try and decouple your implementation in the following manner:

var Valid = (function(){

   var supportedEvents = ['click', 'keyup', 'change'],
       listeners = {};

   for(var i=0; i<supportedEvents.length; ++i) {
      listeners[supportedEvents[i]] = [];
   }

   function addListener(type, classes, fn) {
      listeners[type].push({classes:classes, fn:fn});
   }

   $('.validate').live(supportedEvents.join(' '), function(e){

      for(var i=0; i<listeners[e.type].length; ++i) {
         if ($(this).hasClass(listeners[e.type][i].classes)) {
            listeners[e.type][i].fn.call(this, e);
         }
      }

   });

   return {
      supportedEvents: supportedEvents,
      addListener: addListener
   };

})();

Working example: http://jsfiddle.net/4npzm/1/

Notes:

  • If you have a more localised container, you could use .delegate instead.
  • This can be easily extended to include support for adding listeners that are "attached" to multiple classes (instead of asking .hasClass once, loop through the possible classes), or even wildcards (if (classes === '*' || other_match_patterns))
  • You can easily add support for each listener to have an id (belonging to a map {}, instead of an array [], and then removing listeners becomes easy, and can be done individually without holding a reference to the function)
  • Supporting more event types is as simple as adding an array element
  • The API is nicely packaged up in one variable

This keeps the core processing of holding and referencing event handlers within the scope of your own code, as opposed to the approach of attaching the events, and then "losing control" over them, which makes further processing difficult - and means you have to bind and unbind things...

This isn't a final version, just a brief outline of the functionality, although to me it seems intuitive and flexible.

0

精彩评论

暂无评论...
验证码 换一张
取 消