7

I want to wrap an existing click event in some extra code.

Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.

Here's what I've tried:

   //Take the click events off the accordion elements and wrap them to trigger validation
    $('.accordion h1').each(function (index, value) {
        var currentAccordion = $(value);
        //Get reference to original click
        var originalClick = currentAccordion.click;

        //unbind original click
        currentAccordion.unbind('click');

        //bind new event           
        currentAccordion.click(function () {
            //Trigger validation
            if ($('#aspnetForm').valid()) {
                current = parseInt($(this).next().find('.calculate-step').attr('data-step'));             
                //Call original click.
                originalClick();
            }
        });
    });

jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.

EDIT: Updated code. This works but it is a bit ugly!

   //Take the click events off the accordion elements and wrap them to trigger validation
    $('.accordion h1').each(function (index, value) {
        var currentAccordion = $(value);
        var originalClick = currentAccordion.data("events")['click'][0].handler;
        currentAccordion.unbind('click');
        currentAccordion.click(function (e) {
            if ($('#aspnetForm').valid()) {
                current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
                $.proxy(originalClick, currentAccordion)(e);
            }
        });
    });
3
  • Could you use $(currentAccordion).trigger('click'); and not save a reference to the event? Commented Oct 13, 2011 at 10:18
  • I think that will trigger my newly attached event? Commented Oct 13, 2011 at 10:19
  • Yes it will - sorry, I hadn't fully understood what you were trying to do. Commented Oct 13, 2011 at 10:31

4 Answers 4

4

I think this:

var originalClick = currentAccordion.click;

Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()

Sign up to request clarification or add additional context in comments.

4 Comments

You're right, so I need to find out how to get the original handler.
You can perhaps do that using currentAccordian.data("events") - it gives you back the list of events that there are handlers for and the handlers, but I don't recall the exact way that works.
Cheers, between you and Steve there's a working solution but it's a bit ugly :-)
4

I finally came up with something reliable:

$(".remove").each(function(){
    // get all our click events and store them
    var x = $._data($(this)[0], "events");
    var y = {}
    for(i in x.click)
    {
    if(x.click[i].handler)
    {
    y[i] = x.click[i].handler;
    }
    }

    // stop our click event from running
    $(this).off("click")

    // re-add our click event with a confirmation
    $(this).click(function(){
    if(confirm("Are you sure?"))
    {
        // if they click yes, run click events!
        for(i in y)
        {
        y[i]()
        }
        return true;
    }
    // if they click cancel, return false
    return false;
    })
})

This may seem a bit weird (why do we store the click events in the variable "y"?)

Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.

http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/

1 Comment

You may need to handle click events added inline, e.g. <a onclick="something()">do something</a> separately by grabbing var inline_click = this.onclick then checking for inline_click after the for loop and running it if(inline_click){ inline_click(); }
2

I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.

In jQuery, you use the $.proxy() method to do this.

$.proxy(originalClick, value);
originalClick();

Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.

Hope that helps :)

1 Comment

I think I will try creating some hooks on the accordion after seeing the resulting code.
1

currentAccordion.click is a jQuery function, not the actual event.

Starting with a brute-force approach, what you'd need to do is:

  • Save references to all the currently bound handlers
  • Unbind them
  • Add your own handler, and fire the saved ones when needed
  • Make sure new handlers bound to click are catched too

This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.

Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.