简体   繁体   中英

In javascript, how can I wrapper an onclick callback?

I want to decorate or wrapper a specific onclick callback such that my code is executed after the original installed callback is called.

I started with this in order to do it in a semi-python'ic way using function generator with closure:

var original_onclick = node.onclick;
function wrapper_onclick() {
    original_onclick.apply(this,arguments);
    my_code();
}
node.onclick = wrapper_onclick;

However, when I use the above code, the original callbacks aren't working properly. Am I passing in the wrong first parameter (context parameter?) this . Do I need to tweak the arguments? Is this already in list arguments ?

This is exactly why lots of people worked to find a proper solution for cross browser event listening. Some browsers don't fire the function in the right context, some browsers don't pass the event argument and so on. It's a lot easier to use a framework, but if you insist on using no frameworks...

First if you have really horrible code like:

<a href="javascript:someFunction( this , evaledVariable );otherFn( ohDearVariable )">

Then sorry, no one can help you.

Otherwise...

In any case, you can't attach events directly to a node by simply setting the .onclick attribute and expect it to behave equally cross browser or even work properly in the browser you're developing with. You must use both addEventListener and attachEvent methods.

The most simplest way of forking for cross browser events is:

var addListener = function( node , event , listener ) {

    if ( node.addEventListener ) {

        node.addEventListener( event , listener , false );

    } else if (node.attachEvent ) {

        node.attachEvent( "on" + event , listener );

    }

}

Same for removing listeners:

var removeListener = function( node , event , listener ) {

    if ( node.addEventListener ) {

        node.removeEventListener( event , listener , false );

    } else if (node.attachEvent ) {

        node.detachEvent( "on" + event , listener );

    }

}

And use it thus:

var nodeListener = function( e ) {
    alert(e.target);
    // run once for demo
    removeListener( node , "click" , nodeListener );
}
// notice we omit the "on" part.
addListener( node , "click" , nodeListener );

So if you want to wrap listeners appended to node the WRONG onclick way:

var wrongOnClick = node.onclick;

var listener = function( e ) {
    before();
    wrongOnClick.call( this , e );
    after();
};

addListener( node , "click" , listener );

This will serve most modern browsers including IE, though actually a lot more optimalisation and memory leak prevention code is needed for a good implementation, but instead of writing this code yourself or having others do it for you, use a framework/tookit! Because by the time you have a good implementation running that works cross browser, you will have more code bloat then when you actually used a framework...

** Addendum **

As a hack, I still suggest to use the addEventListener method and keep references to your listeners.

var aListener = function( e ) { }
node.addEventListener( "click" , aListener , false );

var wrappedListener = function ( e ) {
   before( e );
   aListener( e );
   after( e );
}
node.removeEventListener( "click" , aListener , false );
node.addEventListener( "click" , wrappedListener , false );

This obviously only works with your listeners, if you want it to wrap existing listeners in a page (listeners from others), then there's simply no way of knowing how people appended their listeners to nodes. If they used addEventListener/attachEvent libraries or the dirty example in the beginning of this post, you simply cannot reliably wrap them without manual coding and looking up the references or manually exposing them.

Based on help from Matt and BGerrissen, the following code works great for chrome:

var original_onclick = node.onclick;
function wrapper_onclick(e ) {
  var ret = original_onclick(e);
  remove_story_onclicks();
  return ret;
}
node.onclick = wrapper_onclick;

Still not exactly sure how to do a bulletproof wrapper like the following in python:

def wrappee(foo,bar,baz):
   print foo,bar,baz

def wrapper(*args,**kwargs):
    print 'before'
    wrappee(*args,**kwargs)
    print 'after'

wrapper('a','b','c')

which prints

before
a b c
after

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM