Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 15 of 15
  1. #1
    Regular Coder
    Join Date
    Sep 2010
    Posts
    331
    Thanks
    9
    Thanked 6 Times in 6 Posts

    How to addEventListener with argument?

    When I was learning about the addEventListener method, I read that you're not supposed to include the parenthesis on the function you want to run because it will cause the event listener to 'fire off' immediately. If this is true, then how would you add an argument to a function you want to run?
    Coding is a challenge, get used to it
    Always remember to debug
    Try the guess & check method
    Break it down into simple steps

  • #2
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,335
    Thanks
    11
    Thanked 588 Times in 569 Posts
    event listeners don't pass arguments: the event object is generated by the browser and passed as the first and only argument.

    if you want to reach other data, give the data a name and point your handler to that name.
    if you are adding many handlers to many elms in a loop, it can be slightly complicated.

    in that case, i would use Array.map instead of the for, and your variables will magically line up when the handler fires later because map() uses a function, and the function has its own scope, unlike a for-loop routine where all handlers inherit the same scope.


    you can use jQuery's map() to accomplish the same thing in old-school browsers.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • #3
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,083
    Thanks
    38
    Thanked 498 Times in 492 Posts

    Question

    Quote Originally Posted by rnd me View Post
    ...
    if you want to reach other data, give the data a name and point your handler to that name.
    if you are adding many handlers to many elms in a loop, it can be slightly complicated.

    in that case, i would use Array.map instead of the for, and your variables will magically line up when the handler fires later because map() uses a function, and the function has its own scope, unlike a for-loop routine where all handlers inherit the same scope.
    ...
    @rnd me:
    Would you happen to have a brief example of the code that would accomplish this? Something simple with the extra parameter(s).
    Preferably without using JQuery so I could understand the basics.

  • #4
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,447
    Thanks
    76
    Thanked 4,372 Times in 4,337 Posts
    I *think* what Elitis is talking about is something like this:
    Code:
    <form id="theForm">
    <input type="button" name="Button1" value="click here" />
    <input type="button" name="Button2" value="or here" />
    <input type="button" name="Button3" value="or finally here" />
    </form>
    
    <script type="text/javascript">
    (
      function( )
      {
          var form = document.getElementById("theForm");
          var btn1 = form.Button1;
          var btn2 = form.Button2;
          var btn3 = form.Button3;
    
          btn1.onclick = doSomething;
          
          btn2.onclick = function(evt) { doSomethingElse( evt, "with another argument" ); }
    
          btn3.onclick = fundtion( ) { doEvenMore( this, 3, "zamboni", new Date() ); }
    
          function doSomething( evt ) 
          {
              if ( evt == null ) { evt = window.event; }
              ... now code to do something using the event ...
          }
    
          function doSomethingElse( evt, arg2 ) 
          {
              if ( evt == null ) { evt = window.event; }
              ... now code to do something using the event *AND* the other argument...
          }
    
          function doEvenMore( withButton, num, name, when )
          {
               ... do something with the four arguments ...
               ... this time no event used ...
          }
      }
    )();
    </script>
    Elitis: Assuming I understood you, then hopefully you now see the "trick": You create an anonymous function and use *IT* to pass the arguments that you need. You can see there how you can pass along the event or this reference, if you want them.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • Users who have thanked Old Pedant for this post:

    jmrker (02-12-2013)

  • #5
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,335
    Thanks
    11
    Thanked 588 Times in 569 Posts
    Quote Originally Posted by jmrker View Post
    @rnd me:
    Would you happen to have a brief example of the code that would accomplish this? Something simple with the extra parameter(s).
    Preferably without using JQuery so I could understand the basics.

    Code:
    function bindHandlers(){
      var links=$("a").toArray();
      link.map(function mapper(elm, index){
        elm.addEventListener(
            "click", 
             function clicker(e){ 
    
                alert(  "You clicked the link pointing to " + 
                              elm.href +"\nLink # "+ 
                             ++index + " of " +
                             links.length     ); 
             },
             false 
         );//end addevent
      });//end map()
    }//end bindHandlers()
    here i closed links , grabbing the length property each event invocation (named clicker), and used [].map with an anon function named mapper as a loop construct.

    this is good for "looped" setTimeout calls as well because the mapped function grabs "index" as a whole separate entity, unlike the typical for-loop "i" var which is always the loop length by the time the event actually fires...

    also, you can avoid the .toArray() if you switch the arguments around (index, elm) on the map: jQuery's map() is backwards from ecma5's map(). it goes without saying that this fact makes me sad and angry.
    but i needed a third party obje1ct for the demo, so i made a normal array to point to from the event handlers.

    EDIT: changed to addEventListenter format instead of direct binding (doh!)...


    EDIT2: oops, you said WITHOUT jQuery.
    change
    Code:
      var links=$("a").toArray();
    to
    Code:
      var links=[].slice.call(document.links);
    to kill jq
    Last edited by rnd me; 02-12-2013 at 12:51 AM.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • #6
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,335
    Thanks
    11
    Thanked 588 Times in 569 Posts
    Quote Originally Posted by Old Pedant View Post
    I *think* what Elitis is talking about is something like this:

    that doesn't use addEventListener, and this in that case is the same as evt.target...
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • #7
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,447
    Thanks
    76
    Thanked 4,372 Times in 4,337 Posts
    Quote Originally Posted by rnd me View Post
    that doesn't use addEventListener
    I know. I was sloppy.

    But principal is same.

    If you want to add an extra argument or two, use an anonymous function.
    Code:
        elm.addEventListener(
            "click", 
            function(evt) { doSomethingElse( evt, "with another   argument" ); },
             false );
    Same thing you did, except yours fancier.

    , and this in that case is the same as evt.target...
    Well, unless of course you are in MSIE 8 or below where there's no evt.target. I tend to use this and avoid event unless another reason. I still have way way way too many customers using MSIE 7. And, as I discovered a couple of weeks ago, some still using MSIE 5 !!!

    ****

    EDIT: Some actual numbers:
    294 MSIE 5.5
    3845 MSIE 6
    4632 MSIE 7
    9991 MSIE 8
    15693 MSIE 9
    1219 MSIE 10
    20557 Chrome (all versions)
    16911 Firefox
    51728 WebKit (all versions)
    12645 Macintosh (all browsers)
    804 Opera
    and so on...

    I sure can't abandon MSIE pre version 9, however much I'd like to.
    Last edited by Old Pedant; 02-12-2013 at 01:46 AM.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #8
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,083
    Thanks
    38
    Thanked 498 Times in 492 Posts
    Old Pedant:
    Thank you for the interesting example.
    Very useful to my understanding, expecially after I figured out that:
    Code:
          btn3.onclick = fundtion( ) { doEvenMore( this, 3, "zamboni", new Date() ); }
    probably should have been:
    Code:
          btn3.onclick = function( ) { doEvenMore( this, 3, "zamboni", new Date() ); }

  • #9
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,447
    Thanks
    76
    Thanked 4,372 Times in 4,337 Posts
    Now way! We have to stick with funcamentals, so fundtion it is!

    Hey, I touch type. Mostly I type what I touch.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #10
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,083
    Thanks
    38
    Thanked 498 Times in 492 Posts
    @"rnd me"

    Thanks.
    Your script of post #5 looks pretty slick, but you code above my pay grade
    as I have been unable to get it to do anything yet...including the alert message

    (I'll keep trying!)

  • #11
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,335
    Thanks
    11
    Thanked 588 Times in 569 Posts
    Quote Originally Posted by jmrker View Post
    @"rnd me"

    Thanks.
    Your script of post #5 looks pretty slick, but you code above my pay grade
    as I have been unable to get it to do anything yet...including the alert message

    (I'll keep trying!)
    stupid typos. that's what you get for renaming the array in the message reply box instead of firebug.

    complete, jQuery less code below, tested on FF + Chrome

    Code:
    function bindHandlers(){
      var links=[].slice.call(document.links);;
      links.map(function mapper(elm, index){
        elm.addEventListener(
            "click", 
             function clicker(e){ 
    
                alert(  "You clicked the link pointing to " + 
                              elm.href +"\nLink # "+ 
                             ++index + " of " +
                             links.length     ); 
             },
             false 
         );//end addevent
      });//end map()
    }//end bindHandlers()
    
    bindHandlers()
    the above code calls "elm" what the other calls "this" or "evt.target" or "evt.srcElement".
    to me, that's less to type and is easier to read than "this", which can get confusing and means different things in attachEvent() as it does in addEventListener()...

    PS: if your site's visitors are browser-challenged, don't forget the array methods inline compat pack.
    i like http://danml.com/js/f.js, which gives you JSON, the Array methods and more for under 4kb.
    pro tip: use an HTML conditional comment to add such external script only in IE8 and below...
    Last edited by rnd me; 02-12-2013 at 04:59 AM.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • Users who have thanked rnd me for this post:

    jmrker (02-12-2013)

  • #12
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,335
    Thanks
    11
    Thanked 588 Times in 569 Posts
    Quote Originally Posted by Old Pedant View Post

    Well, unless of course you are in MSIE 8 or below where there's no evt.target. I tend to use this and avoid event unless another reason. I still have way way way too many customers using MSIE 7. And, as I discovered a couple of weeks ago, some still using MSIE 5 !!!
    fair enough, for that you use a unified approach:
    Code:
     var targ=evt.target||evt.srcElement;
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • #13
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,083
    Thanks
    38
    Thanked 498 Times in 492 Posts
    I think I figured out how to use 'rnd me's code with the following example.

    The only part I'm not sure of is how the variable 'index' works in this context.
    It does seem to increment on each click of the link (essentially acts as a counter)
    but I don't see where it is initialized nor where it is stored between clicks.

    Can that be explained a bit more in my test code below?

    Code:
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <title> Untitled </title>
    <script type="text/javascript">
    
    </script>
    
    <style type="text/css">
    
    </style>
    </head>
    <body>
    <div id="links">
    <a href="http://www.webdeveloper.com" onclick="return false"> Webdeveloper </a><br>
    <a href="http://www.codingforums.com" onclick="return false"> Coding Forums </a><br>
    <a href="http://www.dreamincode.net" onclick="return false"> Dream In Code </a><br>
    </div>
    
    <script type="text/javascript">
    function bindHandlers(){
      var links=[].slice.call(document.links);;
      links.map(function mapper(elm, index){
        elm.addEventListener(
            "click", 
             function clicker(e){ 
                alert( "You clicked the link pointing to " + 
                        elm.href + "\nLink # " +
                        ++index + " times of " + links.length + ' buttons' ); 
             },
             false 
         );//end addevent
      });//end map()
    }//end bindHandlers()
    
    window.onload = function() { bindHandlers(); }
    
    </script>
    
    </body>
    </html>

  • #14
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Indeed, it is some tricky code. First, you get all links of the document and put it in an array. The code then calls the map function on it, which iterates over each element (= link) and applies a map function to it. This map function is passed the element and the index of the element within the array (this is where "index" comes from: it's initially 0 for the first link, 1 for the second link, …).
    But instead of doing what you'd usually use the map function for, it just adds an event listener to each element. The index variable is within the closure of the mapper function and thus becomes a "counter" for each element (= link), with the little addition that the initial value is the index of the link within the document.

    Is this sufficient to understand it?

    Edit: To answer your two questions more specifically: the variable 'index' comes from the map function (see the documentation for Array.prototype.map) and it is 'stored' by keeping it alive in a closure. That's similar to this standard example:

    Code:
    var counter = (function () { var value = 0; return function () { alert(++value); }; })();
    
    counter(); // 1
    counter(); // 2
    counter(); // 3
    alert(value); // undefined
    Last edited by Airblader; 02-13-2013 at 10:31 AM.

  • #15
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,083
    Thanks
    38
    Thanked 498 Times in 492 Posts
    Yes, that helps.
    Appreciate your time.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •