javascriptjqueryjquery-eventsjquery-on

Dynamically Modifying Value of an Existing Event Handler


Quick background: I'm updating existing code to separate event handlers from html objects and, in an onload function I'm then assigning all necessary handlers.

$('input[name^="interactiveElement_"]').each(function() {
    var fieldName = "interactiveElement_";
    var elementNumber = $(this).attr("name").substring(fieldName.length,$(this).attr("name").indexOf("_",fieldName.length));
    var subElementNumber = $(this).attr("name").substring((fieldName+itemNumber+'_').length,$(this).attr("name").lastIndexOf('_'));
    var rowNumber = $(this).attr("name").substring($(this).attr("name").lastIndexOf('_')+1);

    $(this).on("click.custNamespace", function() {
        updateThisElementsMetadata(elementNumber, subElementNumber, rowNumber);
        updatePreview(elementNumber);
    });
});

Now for the hard part. In this interface, users will be able to trigger clones of existing elements. These clones need to have some of their handler arguments updated to new values.

Before separating out the events, I was doing that with this:

var regex = /([^0-9]+[0-9]+[^0-9a-zA-Z]+[0-9]+[^0-9a-zA-Z]+)([0-9]+)([^0-9]+)?/g; 
$(element).attr("onclick",
                $(element)
                    .attr("onclick")
                    .replace(regex, "$1"+newValue+"$3"));

and so on for each possible event these elements could have.

But, now using jQuery's on(event, handler) I no longer have visibility to what the handler is.

I've tried this (following this question - Get value of current event handler using jQuery):

jQuery._data(element).events.click[0].handler 

But, this returns the function with variable names not values.

function () {
    updateThisElementsMetadata(elementNumber, subElementNumber, rowNumber);
    updatePreview(elementNumber);
}

Where I would have hoped for:

function () {
    updateThisElementsMetadata(1, 2, 1);
    updatePreview(1);
}

Looking in the console log I see that jQuery._data(element).events.click[0] has the values in handler => < function scope > => Closure but it doesn't seem like there is dot notation to access that, or even an array that I can dynamically cycle through.

If you tell me this isn't possible, I could change all the functions' args to just be $(this) and parse out the necessary values from it in each function, or I guess I could have a helper function... but if I could keep a similar setup to what was there it would ease other dev's learning curve.

Final Solution

To reduce duplicate code, I created a Javascript function/object that parses out necessary info from name/id tag (instead of data- attributes to reduce redundant info). Whenever an event handler is triggered it will first parse out the necessary values and then run the function w/ them.

$('input[name^="interactiveElement_"]').on("click.custNamespace", function() {
    var inputField = inputFieldClass(this);
    updateThisElementsMetadata(inputField.elementNumber, inputField.subElementNumber, inputField.rowNumber);
    updatePreview(inputField.elementNumber);
});


var inputFieldClass = function(field) {
    var fieldIdentity = $(field).attr("name") === undefined ?  $(field).attr("id") :  $(field).attr("name");
    var fieldName = fieldIdentity.substring(0,fieldIdentity.indexOf("_")),
        elementNumber = fieldIdentity.substring(fieldName.length + 1,fieldIdentity.indexOf("_",fieldName.length + 1)),
        subElementNumber = fieldIdentity.substring((fieldName+'_'+elementNumber+'_').length,fieldIdentity.lastIndexOf('_')),
        rowNumber = fieldIdentity.substring(fieldIdentity.lastIndexOf('_')+1);

    return {
        fieldName : fieldName,
        elementNumber : elementNumber,
        subElementNumber : subElementNumber,
        rowNumber : rowNumber,
        getInputName : function () {
            return this.name + "_" + this.elementNumber + "_" + this.subElementNumber + "_" + this.rowNumber;
        }
    };
};

Solution

  • What I was suggesting in the comments was something like this:

    $('input[name^="interactiveElement_"]').on("click.custNamespace", function() {
    
        var fieldName = "interactiveElement_";
        var elementNumber = $(this).attr("name").substring(fieldName.length,$(this).attr("name").indexOf("_",fieldName.length));
        var subElementNumber = $(this).attr("name").substring((fieldName+itemNumber+'_').length,$(this).attr("name").lastIndexOf('_'));
        var rowNumber = $(this).attr("name").substring($(this).attr("name").lastIndexOf('_')+1);
    
        updateThisElementsMetadata(elementNumber, subElementNumber, rowNumber);
        updatePreview(elementNumber);
    });
    

    Also, instead of parsing everything from the element's name, you could use data- attributes to make it cleaner (e.g. data-element-number="1", etc.).