使用JavaScript覆盖(包装)现有的jQuery click事件

4

假设我已经有一个按钮,并通过jQuery为其附加了一个点击事件:

var $button = $('#test').click(function () { console.log('original function') });

现在,假设我想要覆盖这个点击事件,以便在函数之前和之后添加一些逻辑。我尝试使用以下函数进行绑定和包装。

Function.prototype.bind = function () {
   var fn = this;
   var args = Array.prototype.slice.call(arguments);
   var object = args.shift();

   return function () {
       return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)));
   }
}

function wrap(object, method, wrapper) {
   var fn = object[method];

   return object[method] = function() {
       return wrapper.apply(this, [fn.bind(this)].concat(
        Array.prototype.slice.call(arguments)));
   }
}

所以,我使用该方法所在对象、该方法和我想要执行的匿名函数来调用wrap函数。我的想法是:

wrap($button 'click', function (click) {
     console.log('do stuff before original function');
     click();
     console.log('do stuff after original function');
 });

这只是调用原始函数。我之前曾成功地在对象的方法上使用过这种方法。类似于:查看这个 Plunker

请问有人可以帮我在我的具体示例中实现这个吗?

谢谢


你能否使用.off()解除绑定,然后传入你的新函数呢?比如$('whatever').off().click(newFunc)?... 嗯,重新阅读了一下问题,不确定我最初的评论是否能够给你想要的答案? - ann0nC0d3r
@ann0nC0d3r 正在将问题轻描淡写化。 - charlietfl
1
@charlietfl... 你应该读完我的整个评论 ;) - ann0nC0d3r
@ann0nC0d3r,我真的只是想知道如何(如果可以的话)覆盖类似的方法。谢谢。 - davy
假设原始事件是由jQuery添加的,事件处理程序在jQuery内部被缓存。这个缓存没有在API中记录,并且可能会发生变化,但你可能可以找出如何访问并覆盖事件处理程序。 - charlietfl
2个回答

4
您可以创建一个jQuery函数,从数据中获取原始事件处理程序函数,删除click事件,然后添加新的事件处理程序。此函数将具有两个参数(每个函数)的before和after处理程序。
$(function() {

    jQuery.fn.wrapClick = function(before, after) {
        // Get and store the original click handler.
        // TODO: add a conditional to check if click event exists.
        var _orgClick = $._data(this[0], 'events').click[0].handler,
            _self = this;

        // Remove click event from object.
        _self.off('click');

        // Add new click event with before and after functions.
        return _self.click(function() {
            before.call(_self);
            _orgClick.call(_self);
            after.call(_self);
        });
    };

    var $btn = $('.btn').click(function() {
        console.log('original click');
    });

    $btn.wrapClick(function() {
        console.log('before click');
    }, function() {
        console.log('after click');
    });

});

Here is a Codepen


3

经过漫长的搜索,我得出了与@Corey相同的答案,以下是考虑多个事件进行操作的类似方法:

function wrap(object, method, wrapper) {
          
  var arr = []
  var events = $._data(object[0], 'events')
  
  if(events[method] && events[method].length > 0){ // add all functions to array
    events[method].forEach(function(obj){
      arr.push(obj.handler)
    })
  }
  
  
  
  if(arr.length){
      function processAll(){ // process all original functions in the right order
        arr.forEach(function(func){
          func.call(object)
        })
      }
  
      object.off(method).on(method, function(e){wrapper.call(object,processAll)}) //unregister previous events and call new method passing old methods
  }
   
    
}


$(function(){
  $('#test').click(function () { console.log('original function 1') });
  var $button = $('#test').click(function () { console.log('original function 2') });
  wrap($button, 'click', function (click,e) {
     console.log('do stuff before original functions');
     click()
     console.log('do stuff after original functions');
 });
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>


<div id='test'>click me</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接