2

I am triggering a custom event on an element using jQuery and I want the event handler to be able to pass data (in the form of an object) back to the method that called it. The trigger and the handler are in different scopes and files and cannot be merged or shared in a conventional manner. (The trigger method is part of a utility library I am writing and the event handler is part of my front-end view template).

I know this code doesn't work, but I am writing it to kind of illustrate what I am hoping to try. The pattern is based of of work I have done in .NET.

var data = { foo: 'bar' }
element.trigger('some-event', data)
if(data.foo != 'bar')
    alert('someone changed foo!')

And the handler...

element.bind('some-event', function(event, data)
{
    if(some_condition)
        data.foo = 'biz'
});

The specific implementation is not terrible important to me as long as I don't have rearrange my code to stick both the trigger and the bind in the same scope.

How do I get return value back from my event handler?


EDIT

To provide a little more context, the triggering method is responsible for obtaining and processing data, and finally rendering it out as markup to the page. Before it does that, it raises the custom event with the same processed data object so that other components of the app can have the chance to do something with the processed data.

In certain cases, it would be beneficial for the event handlers to modify that set of data, or event signal to the triggering method that the data has already been handled and does not need additional processing or rendering.

Something like below. In this example, the handler might change the way the data is displayed based upon some property value. It may also choose to render the data out a different way (e.g. input) rather than the default rendering in the triggering function (e.g. label).

This implementation is an example, but the end goals of returning an object from the handler, or modifying the data in the handler such that the triggering method can access it are common to my actual project.

var data = load_data();
element.trigger('loading_data', data);
if(data.abort_render!==true)
{
    element.append('<label>Foo</label>')
    element.append('<span>' + data.foo + '</span>')
}

And the handler...

element.bind('loading-data', function(event, data)
{
    if(data.is_password == true)
    {
        data.foo = '*******'   //changed property persisted to triggering method and processed normally
    }
    if(data.type == 'abc')
    {
        element.append('<input value="' + data.foo + '"/>');
        data.abort_render = true;   //signals to the triggering method that it should not render the data to the page
    }
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jeff
  • 13,943
  • 11
  • 55
  • 103
  • 1
    In jQuery, this seems fairly trivial to accomplish. See this answer: http://stackoverflow.com/a/20707308/870729 – random_user_name Sep 29 '15 at 19:46
  • I know how to pass data from the `trigger` to the handler. I need to pass data back from the handler to the method where the event was raise. I don't believe the question you referenced addresses the latter. – Jeff Sep 29 '15 at 19:50
  • It does address both directions. Look at the `on` portion of the answer: `passenger.on('brake.car', function(evt, car, brakeAmount){ ...` is analogous to your `container.on(element, function(event, data) { ...` – random_user_name Sep 29 '15 at 19:53
  • Jeff - I've modified my answer to incorporate your details. – random_user_name Sep 29 '15 at 20:53

1 Answers1

2

There is a way. Below is the jQuery method (modified from Example 2 in this answer: https://stackoverflow.com/a/20707308/870729 )

// Refactor below for efficiency - replace 'body' with closest known container element
jQuery('body').on(element, function(event, data) {
    if (data.foo != 'bar') {
        alert('someone moved my foo!');
    }
});

For a more robust solution, try this:

// Bind the event to the element
jQuery('body').on(element, 'some-event', function(event, data) {
    // Call the callback function, pass in the data
    eventCallback(data);
});

// Call this function to set up the data and trigger the event
function triggerMyEvent() {
    var data = { foo: 'bar' };
    element.trigger('some-event', data); 
}

// The callback function set in the event binding above
function eventCallback(data) {
    if(data.foo != 'bar')
        alert('someone changed foo!');
}

The challenge may be in the flow of the logic / code. Below is the code (from your question) - I've added some comments to try and help explain the flow:

// Command 1: This will run first
var data = load_data();
// Command 2: This will run second
element.trigger('loading_data', data);
// Command 3: This will run independently of the trigger callback
// and the data will not be modified based on the trigger.
// You will want to restructure your code to run this within the
// trigger callback
if(data.abort_render!==true)
{
    element.append('<label>Foo</label>')
    element.append('<span>' + data.foo + '</span>')
}

// Event handler (will run independently of Command 3)
jQuery('body').on(element, 'loading_data', function(event, data) {
    // THIS will run independently of the "Command 3" code above
    // and may or may not be done before Command 3 is run.
    // That's why this needs to trigger the code that handles
    // the data checks and various outputs based on the data
    handleLoadData(data);
}

// A specific function designed to be run AFTER event handler, 
// specifically to handle the various data settings that may be set.
function handleLoadData(data) {
    if(data.is_password == true) {
        data.foo = '*******'   //changed property persisted to triggering method and processed normally
    }

    if(data.type == 'abc') {
        element.append('<input value="' + data.foo + '"/>');
        data.abort_render = true;   //signals to the triggering method that it should not render the data to the page
    }

    // This code will need to be moved here, because data may 
    // not be updated when Command 3 is run above. Alternatively,
    // You could put it in another function, and call that function
    // from here.
    if(data.abort_render!==true) {
        element.append('<label>Foo</label>')
        element.append('<span>' + data.foo + '</span>')
    }
}

Will NOT run in the order you expect. See the comments above to help

Community
  • 1
  • 1
random_user_name
  • 25,694
  • 7
  • 76
  • 115
  • I think I must be missing something. I don't see anywhere in the other question where the event handler is returning data. The example you provide here also doesn't return anything from the event handler to the calling scope. You have stuck the `alert` inside the event handler which is a fundamental change from my question. Are you suggesting that I add a second event handler further up the DOM tree in order to capture the event a second time? That might work... – Jeff Sep 29 '15 at 20:03
  • @Jeff - yes, you'll have to. The location of your code in your question I mistook for "sample code", but truly must either live inside the `on` callback, or else must be called by the `on` callback. I've modified the answer to provide a bit more context. – random_user_name Sep 29 '15 at 20:04
  • @Jeff - also, this is a fairly abstract concept you're asking about, can you provide more specifics to the use-case - that might help dial in the answer? – random_user_name Sep 29 '15 at 20:08