0

I try to write a small jQuery script and run into a problem. I have an array of objects, which contain html and some events, which are supposed to be triggered once the HTML is loaded into the DOM.

For testing purposes, I've reduced the array of objects to one object:

<!doctype html>
<html>
    <head><title>Test</title></head>
    <body>
        <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
        <div id="wrapper"></div>
        <script>
            var element = {
                id      : 'xyz',
                html    : '<div id="xyz"><label>ABC</label><input type="text"></div>',
                events  : [
                            {
                                'selector':'label',
                                'event_name':'click',
                                'callback':function( event, element ){
                                    alert( '[EVENT] Click <label>' );
                                    console.log( event );
                                    console.log( element );
                                }
                            },
                            {
                                'selector':'input',
                                'event_name':'change',
                                'callback':function( event, element ){
                                    alert( '[EVENT] Change <input>' );
                                    console.log( event );
                                    console.log( element );
                                }
                            }
                    ]
            }

            $( document ).ready( function(){
                $( element.html ).appendTo( '#wrapper' );
                for( var i = 0; i < element.events.length; i++ ){
                    //var eventReg = $.extend({},element.events[i]); //Its not a object-clone-problem
                    var eventReg = element.events[i];
                    console.log( eventReg.event_name );
                    console.log( eventReg.selector );
                    console.log( '#####################' );
                    $( '#'+ element.id ).on( eventReg.event_name, eventReg.selector, function( event ){eventReg.callback( event, this )} );
                }
            });
        </script>
    </body>
</html>

The element-object has a id "xyz" (which is also the id of the div, which will be inserted). Once the document is ready, i add the HTML (element.html) to the div#wrapper. Now I run through all the objects in the element.events. These objects contain a selector, an event name and a callback function. Since also the event is dynamically, I want to use the $.on() method to register the event. I bind the event on the div#xyz, which is now in the DOM and contains the elements. OK, I think you understand, what I try.

Now: My problem is when I run the script I expect to receive a different alert() when I click the <label> and a different alert() when I change the <input> value. But in both events, the same callback function (the last one of my array) is registered...

So click on <label> calls also the callback() of the <input>-change event. Does anyone can give me a hint, what am I missing? Thanks a lot.

EDIT The Solution

Juan noticed, this question is actually a duplicate. Well it is in technical terms but I had problems to apply the solution. Fortunatly, Juan already gave me the answer in the comments. I needed a bit, but than I figured it out and want to add it here, since it might be of some help for others:

$( '#'+ element.id ).on( eventReg.event_name, eventReg.selector, (function(cb){return function( event ){cb( event, this )}} )(eventReg.callback));
websupporter
  • 325
  • 1
  • 8
  • 1
    It's because eventReg is shared by your event handlers and points to the last one by the time that anonymous function is called. See the question I linked it to. See below for the simplest way to fix your use case, I call it "freezing a closure". It creates an extra closure each time through the loop with an immediately self calling function. – Ruan Mendes Oct 06 '15 at 12:25
  • 1
    `(function(cb){return function( event ){cb( event, this )})})(eventReg.callback);` – Ruan Mendes Oct 06 '15 at 12:27
  • Thanks a bunch Juan! It took me a bit, but now I get it :) I use this part instead of ´function( event ){eventReg.callback( event, this )}´ since it returns the function. – websupporter Oct 06 '15 at 12:35
  • 1
    must be `(function(cb){return function( event ){cb( event, this )}})(eventReg.callback);` – websupporter Oct 06 '15 at 12:37
  • Nice, I knew it could be simplified but was lazy :) – Ruan Mendes Oct 06 '15 at 12:38

1 Answers1

0
<!doctype html>
<html>
    <head><title>Test</title></head>
    <body>
        <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
        <div id="wrapper"></div>
        <script>
            var element = {
                id      : 'xyz',
                html    : '<div id="xyz"><label>ABC</label><input type="text"></div>',
                events  : [
                            {
                                'selector':'label',
                                'event_name':'click',
                                'callback':function( event, element ){
                                    alert( '[EVENT] Click <label>' );
                                    console.log( event );
                                    console.log( element );
                                }
                            },
                            {
                                'selector':'input',
                                'event_name':'change',
                                'callback':function( event, element ){
                                    alert( '[EVENT] Change <input>' );
                                    console.log( event );
                                    console.log( element );
                                }
                            }
                    ]
            }

            $( document ).ready( function(){
                $( element.html ).appendTo( '#wrapper' );
                for( var i = 0; i < element.events.length; i++ ){
                    //var eventReg = $.extend({},element.events[i]); //Its not a object-clone-problem
                    var eventReg = element.events[i];
                    console.log( eventReg.event_name );
                    console.log( eventReg.selector );
                    console.log( '#####################' );
                    $( '#'+ element.id ).on( eventReg.event_name, eventReg.selector, function( event ){eventReg.callback( event, this )} );
                }
            });
        </script>
    </body>
</html>

Try this please. I believe all you are missing is on line 37 "elements" should be "element". Try checking your JS console for errors next time, this popped up.

Edit: Here is the working code: https://jsfiddle.net/zwx4quuh/

Alex Yurkowski
  • 1,676
  • 1
  • 12
  • 26
  • no sorry. typo in my code, but not the problem. thank you. – websupporter Oct 06 '15 at 12:11
  • updated the code above and still run into the problem. – websupporter Oct 06 '15 at 12:12
  • Clicking the label brings up an alert with the above code when I tested it. Changing the input box brings up an alert as well, note: I had to take focus off the input box (click box, input some text, click off box). – Alex Yurkowski Oct 06 '15 at 13:22
  • Hi Alex: When I click on the label the alert should be "click..." its "change..." from the input-change. But thanks a lot for your help. Actually Juan Mendes helped me to figure it out and pointed me to the right answer. – websupporter Oct 07 '15 at 06:32