Loading ...
Sorry, an error occurred while loading the content.

Re: Add listener to objects added through AJAX

Expand Messages
  • alexshusta
    Hi Robert, If I m understanding you correctly, you re having trouble getting listeners to work on dynamically created HTML tags (not just pictures) right? This
    Message 1 of 6 , Jul 1, 2007
    • 0 Attachment
      Hi Robert,

      If I'm understanding you correctly, you're having trouble getting listeners to work on dynamically created HTML tags (not just pictures) right? This is "a known issue" or at least I think I've run into it before so I'd assume the JS gurus know about it. The work around is to attach your event listener explicitly and avoid using onAvailable. Here's an example page that dynamically loads an image and attaches a click handler at image insertion:

      <html>
          <head>
              <title>AJAX Listeners</title>
              <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.2.2/build/reset-fonts-grids/reset-fonts-grids.css">
              <style>
                  h1,h2,h3 {font-family:"Trebuchet MS"; font-weight:bold; letter-spacing:1px;}
                  h1 {font-size:133%;}
                  #hd {padding:1em 0; border-bottom:1px solid #000;}
                  #bd {padding:1em; border-bottom:1px solid #000;}
                  #ft {padding:1em 0;}
                  #target {display:block;}
              </style>
              <script type="text/javascript" src="http://yui.yahooapis.com/2.2.2/build/yahoo-dom-event/yahoo-dom-event.js"></script>
          </head>
          <body>
              <div id="doc" type="yui-t7">
                  <div id="hd">
                      <h1>AJAX Listeners</h1>
                  </div>
                  <div id="bd">
                      <input type="button" id="load" value="Load Image">
                      <div id="target"></div>
                  </div>
                  <div id="ft">
                      fooooooter
                  </div>
              </div>
          </body>
          <script type="text/javascript">
          function init(){
              //alert('in init');
              // setup the click listeners
              YAHOO.util.Event.addListener("load", "click", loadPic, "load", true);
          }
          function loadPic(){
              //alert('loading pic');
              var sUrl = 'http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_devnet_1.gif
      ';
              var newPic = new Image();
              newPic.src = sUrl;
              newPic.id = "newPic";
              setupPicListener(newPic);
              document.getElementById("target").appendChild(newPic);
          }
          function setupPicListener(listenTo){
              //alert('setup listener for: ' + listenTo);
              YAHOO.util.Event.addListener(listenTo, "click", function(){alert("clicked");});
          }

          YAHOO.util.Event.onContentReady("doc", init);
          </script>
      </html>


      The "loadPic" function is the important bit. You can also include the "addListener" call inline instead of using a separate function, I just did that so I could test different ways of attaching the listener without having to retype/paste the alert code. :)

      Finally, if you absolutely must use a detached handler, you can always attach your listener to a parent element of the inserted tag and include a source check, e.g.
      if(event.source.id = myTargetId){
      //do stuff
      }

      when it detects an appropriate event. Due to the bubbling nature of events in js you should be able to catch anything that happens using this approach, but you'll potentially spend a lot of cycles checking and ignoring events that aren't the one you're looking for. This is akin to the approach that Java uses.

      Hope that helps, but if not please feel free to clarify your question with some example code and I bet someone on the list will be able to give you a definitive answer.
      ~Alexander

      --- In ydn-javascript@yahoogroups.com, "roblj689" <robert.ljungwald@...> wrote:
      >
      > I have tried addListener and onAvailable, but when I load images with
      > AJAX calls, but they don't "listen". How do I fetch the image-id with
      > YUI this way?
      >
    • roblj689
      Hi, thank you for your reply! This is the thing I want to add listerners to, the images loaded. The strange abbreviations are arrays containing information
      Message 2 of 6 , Jul 1, 2007
      • 0 Attachment
        Hi,
        thank you for your reply!

        This is the thing I want to add listerners to, the images loaded. The
        strange abbreviations are arrays containing information about a
        product. 'im' is the one containing the image adresses.

        * ------------------- showimages------------------
        Description: Shows the pictures with innerHTML in the layer "pictures".
        This is done with innerHTML. Gets loaded to the user.
        @returns: Void .
        @param: Javascript variables (arrays) mtrl, buck, rat, qoh,
        leng, im, col, desc, com, pr, im, nm, pd, i.e all attributes of the
        image, material, buckle, rating, QOH, length, image(adresses),
        color, description, comment, price, name, productid. The imageadress
        shows the image.
        The other variables are used as description when the picture is shown.
        ---------------------------------------------------------*/
        function showimages(mtrl, buck, rat, qoh, leng, im, col, desc, com,
        pr, im, nm, pd) {
        var pictures = document.getElementById("pictures");
        pictures.innerHTML ="</br></br>"; //to erase the old before inserting new.
        for (var i =0; i < 5; i++) {
        pictures.innerHTML += "<img border='0' src='" + im[i] + "' width='50'
        height='50' onMouseover='get_match(" +pd[i] + ")';></div><div></br>"
        if (i==2 || i==5 || i==8 || i == 11 || i == 14 || i == 17 || i ==
        20) //to arrange the pictures nicely, in a pattern.
        pictures.innerHTML += "<br>"
        }
        }


        YAHOO.util.Event.onAvailable("test", "click",
        YAHOO.layer.container.overlay1.show, YAHOO.lager.container.overlay1,
        true);

        Suppouse I was to name the image inserted "test" (id="test"). Then I
        want the eventlistener to grab it when loaded. Thats the main problem.

        Also, I presumed problem will be a name conflict, every image cannot
        have an id "test" (there are 5).
      • alexshusta
        Hi Robert, Aha! The entire problem is obviously that there s a missing forward slash from the beginning of your function description comment. ;^) If only life
        Message 3 of 6 , Jul 1, 2007
        • 0 Attachment
          Hi Robert,

          Aha! The entire problem is obviously that there's a missing forward slash from the beginning of your function description comment.  ;^)

          If only life was actually so simple. But, there are actually a few formatting issues that may be having an effect on your code. The misspelling of "layer" as "lager" in your onAvailable call (unless you've got a lager layer in which case I demand you share). The argument "im" is being passed into the function twice. And there were some semi-colons missing, which is okay until the news group inserts line-breaks and javascript then acts as if each line break is a semi-colon. Some of the OR statements will never be true, but I assume you changed the for loop to run from 0 through 4 for testing.

          Your use of "var" to instantiate the for loop index 'i' is correct and will create a locally scoped variable that is discarded when the function exits thereby preventing a memory leak.

          Looking at your code and your question, I think I get what you're trying to do and generally you're going about it the right way. You can remove the inline onmouseover statement that's calling get_match once you've got the YUI enabled listener working. Or, you can leave it if you'd like to fire one function on the mouseover event and a different function on the click event. Or (yay! more options) you could move it into another listener that is attached to your elements dynamically.

          If you want to attach listeners to all of your dynamic images, there's an easy way to find them using
          var anArray = YAHOO.util.Dom.getElementsByClassName('myClass');
          then you just have to be sure to insert the proper class when you're creating the elements.

          After running through your example code moving chunks of text around, inserting some semi-colons, removing unused arguments, and generally being way too obsessive about formatting this is what I got:

          <script>
          function showimages(im, pd){
              var newInner = ""; // not really necessary as it should start out empty by default.
              for (var i =0; i < 5; i++){
                  newInner += "<img border='0' src='";
                  newInner += im[i] + "'";
                  newInner += " width='50' height='50'";
                  //newInner += " onmouseover='get_match(\"" +pd[i] + "\");'";
                  newInner += " id='test' class='clickable'><br>";
                  if (i==2 || i==5){ //to arrange the pictures nicely, in a pattern.
                      newInner += "<br>";
                  }
              }
              document.getElementById("pictures").innerHTML = newInner;
          }

          function get_match(toGet){
              alert('getting match for: ' + toGet);
          }

          function addListener(addTo){
              alert('adding listener to: ' + addTo);
              YAHOO.util.Event.addListener(addTo, "click", clickHandler, addTo.toString(), true);
              //YAHOO.util.Event.addListener(addTo, "click", YAHOO.layer.container.overlay1.show, YAHOO.lager.container.overlay1, true);
          }

          function clickHandler(sourceEvent, clickedOn){
              alert('you clicked on: ' + clickedOn);
          }


          function init(){
              //YAHOO.util.Event.onAvailable("test", addListener);
             
              showimages([1,2,3,4,5,6], ['a','b','c','d','e']); // dummy vars so you get nice boxes with red X's to click
             
              var myImages = YAHOO.util.Dom.getElementsByClassName('clickable', 'img'); // see http://developer.yahoo.com/yui/dom/#class
              for(var i=0; i<myImages.length; i++){
                  addListener(myImages[i]);   
              }
          }

          YAHOO.util.Event.onDOMReady(init);
          </script>

          The onAvailable call is only going to happen once (well, would happen once if I hadn't commented it out), when the first image with id='test' is inserted. After that it stops paying attention.

          The last lines of the new code get all images on the page with class='clickable' and attaches a click listener to each one.

          Generally, you had the right approach to setting up the listeners. Create the elements, use a library method to find them and then attach the listener. This is much better than attempting to traverse the entire page searching for a few items.

          Good luck with the implementation!
          ~Alexander

          --- In ydn-javascript@yahoogroups.com, "roblj689" <robert.ljungwald@...> wrote:
          >
          > Hi,
          > thank you for your reply!
          >
          > This is the thing I want to add listerners to, the images loaded. The
          > strange abbreviations are arrays containing information about a
          > product. 'im' is the one containing the image adresses.
          >
          > * ------------------- showimages------------------
          > Description: Shows the pictures with innerHTML in the layer "pictures".
          > This is done with innerHTML. Gets loaded to the user.
          > @returns: Void .
          > @param: Javascript variables (arrays) mtrl, buck, rat, qoh,
          > leng, im, col, desc, com, pr, im, nm, pd, i.e all attributes of the
          > image, material, buckle, rating, QOH, length, image(adresses),
          > color, description, comment, price, name, productid. The imageadress
          > shows the image.
          > The other variables are used as description when the picture is shown.
          > ---------------------------------------------------------*/
          > function showimages(mtrl, buck, rat, qoh, leng, im, col, desc, com,
          > pr, im, nm, pd) {
          > var pictures = document.getElementById("pictures");
          > pictures.innerHTML ="</br></br>"; //to erase the old before inserting new.
          > for (var i =0; i < 5; i++) {
          > pictures.innerHTML += "<img border='0' src='" + im[i] + "' width='50'
          > height='50' onMouseover='get_match(" +pd[i] + ")';></div><div></br>"
          > if (i==2 || i==5 || i==8 || i == 11 || i == 14 || i == 17 || i ==
          > 20) //to arrange the pictures nicely, in a pattern.
          > pictures.innerHTML += "<br>"
          > }
          > }
          >
          >
          > YAHOO.util.Event.onAvailable("test", "click",
          > YAHOO.layer.container.overlay1.show, YAHOO.lager.container.overlay1,
          > true);
          >
          > Suppouse I was to name the image inserted "test" (id="test"). Then I
          > want the eventlistener to grab it when loaded. Thats the main problem.
          >
          > Also, I presumed problem will be a name conflict, every image cannot
          > have an id "test" (there are 5).
          >
        • roblj689
          Thanks for the reply! Great! I will look into this on my vacation in a week! Robert ... argument ... assume ... and ... trying ... remove ... you ve ... like
          Message 4 of 6 , Jul 6, 2007
          • 0 Attachment
            Thanks for the reply! Great! I will look into this on my vacation in
            a week!

            Robert
            --- In ydn-javascript@yahoogroups.com, "alexshusta" <alexshusta@...>
            wrote:
            >
            > Hi Robert,
            >
            > Aha! The entire problem is obviously that there's a missing forward
            > slash from the beginning of your function description comment. ;^)
            >
            > If only life was actually so simple. But, there are actually a few
            > formatting issues that may be having an effect on your code. The
            > misspelling of "layer" as "lager" in your onAvailable call (unless
            > you've got a lager layer in which case I demand you share). The
            argument
            > "im" is being passed into the function twice. And there were some
            > semi-colons missing, which is okay until the news group inserts
            > line-breaks and javascript then acts as if each line break is a
            > semi-colon. Some of the OR statements will never be true, but I
            assume
            > you changed the for loop to run from 0 through 4 for testing.
            >
            > Your use of "var" to instantiate the for loop index 'i' is correct
            and
            > will create a locally scoped variable that is discarded when the
            > function exits thereby preventing a memory leak.
            >
            > Looking at your code and your question, I think I get what you're
            trying
            > to do and generally you're going about it the right way. You can
            remove
            > the inline onmouseover statement that's calling get_match once
            you've
            > got the YUI enabled listener working. Or, you can leave it if you'd
            like
            > to fire one function on the mouseover event and a different
            function on
            > the click event. Or (yay! more options) you could move it into
            another
            > listener that is attached to your elements dynamically.
            >
            > If you want to attach listeners to all of your dynamic images,
            there's
            > an easy way to find them using
            > var anArray = YAHOO.util.Dom.getElementsByClassName('myClass');
            > then you just have to be sure to insert the proper class when you're
            > creating the elements.
            >
            > After running through your example code moving chunks of text
            around,
            > inserting some semi-colons, removing unused arguments, and generally
            > being way too obsessive about formatting this is what I got:
            >
            > <script>
            > function showimages(im, pd){
            > var newInner = ""; // not really necessary as it should start
            out
            > empty by default.
            > for (var i =0; i < 5; i++){
            > newInner += "<img border='0' src='";
            > newInner += im[i] + "'";
            > newInner += " width='50' height='50'";
            > //newInner += " onmouseover='get_match(\"" +pd[i]
            + "\");'";
            > newInner += " id='test' class='clickable'><br>";
            > if (i==2 || i==5){ //to arrange the pictures nicely, in a
            > pattern.
            > newInner += "<br>";
            > }
            > }
            > document.getElementById("pictures").innerHTML = newInner;
            > }
            >
            > function get_match(toGet){
            > alert('getting match for: ' + toGet);
            > }
            >
            > function addListener(addTo){
            > alert('adding listener to: ' + addTo);
            > YAHOO.util.Event.addListener(addTo, "click", clickHandler,
            > addTo.toString(), true);
            > //YAHOO.util.Event.addListener(addTo, "click",
            > YAHOO.layer.container.overlay1.show, YAHOO.lager.container.overlay1,
            > true);
            > }
            >
            > function clickHandler(sourceEvent, clickedOn){
            > alert('you clicked on: ' + clickedOn);
            > }
            >
            >
            > function init(){
            > //YAHOO.util.Event.onAvailable("test", addListener);
            >
            > showimages([1,2,3,4,5,6], ['a','b','c','d','e']); // dummy
            vars so
            > you get nice boxes with red X's to click
            >
            > var myImages = YAHOO.util.Dom.getElementsByClassName
            ('clickable',
            > 'img'); // see http://developer.yahoo.com/yui/dom/#class
            > for(var i=0; i<myImages.length; i++){
            > addListener(myImages[i]);
            > }
            > }
            >
            > YAHOO.util.Event.onDOMReady(init);
            > </script>
            >
            > The onAvailable call is only going to happen once (well, would
            happen
            > once if I hadn't commented it out), when the first image with
            id='test'
            > is inserted. After that it stops paying attention.
            >
            > The last lines of the new code get all images on the page with
            > class='clickable' and attaches a click listener to each one.
            >
            > Generally, you had the right approach to setting up the listeners.
            > Create the elements, use a library method to find them and then
            attach
            > the listener. This is much better than attempting to traverse the
            entire
            > page searching for a few items.
            >
            > Good luck with the implementation!
            > ~Alexander
            >
            > --- In ydn-javascript@yahoogroups.com, "roblj689"
            <robert.ljungwald@>
            > wrote:
            > >
            > > Hi,
            > > thank you for your reply!
            > >
            > > This is the thing I want to add listerners to, the images loaded.
            The
            > > strange abbreviations are arrays containing information about a
            > > product. 'im' is the one containing the image adresses.
            > >
            > > * ------------------- showimages------------------
            > > Description: Shows the pictures with innerHTML in the layer
            > "pictures".
            > > This is done with innerHTML. Gets loaded to the user.
            > > @returns: Void .
            > > @param: Javascript variables (arrays) mtrl, buck, rat, qoh,
            > > leng, im, col, desc, com, pr, im, nm, pd, i.e all attributes of
            the
            > > image, material, buckle, rating, QOH, length, image(adresses),
            > > color, description, comment, price, name, productid. The
            imageadress
            > > shows the image.
            > > The other variables are used as description when the picture is
            shown.
            > > ---------------------------------------------------------*/
            > > function showimages(mtrl, buck, rat, qoh, leng, im, col, desc,
            com,
            > > pr, im, nm, pd) {
            > > var pictures = document.getElementById("pictures");
            > > pictures.innerHTML ="</br></br>"; //to erase the old before
            inserting
            > new.
            > > for (var i =0; i < 5; i++) {
            > > pictures.innerHTML += "<img border='0' src='" + im[i] + "'
            width='50'
            > > height='50' onMouseover='get_match(" +pd[i]
            + ")';></div><div></br>"
            > > if (i==2 || i==5 || i==8 || i == 11 || i == 14 || i == 17 || i
            ==
            > > 20) //to arrange the pictures nicely, in a pattern.
            > > pictures.innerHTML += "<br>"
            > > }
            > > }
            > >
            > >
            > > YAHOO.util.Event.onAvailable("test", "click",
            > > YAHOO.layer.container.overlay1.show,
            YAHOO.lager.container.overlay1,
            > > true);
            > >
            > > Suppouse I was to name the image inserted "test" (id="test").
            Then I
            > > want the eventlistener to grab it when loaded. Thats the main
            problem.
            > >
            > > Also, I presumed problem will be a name conflict, every image
            cannot
            > > have an id "test" (there are 5).
            > >
            >
          • roblj689
            So, now I have worked some on this problem and came up with a start (with your help of course :) ) function showimages(im, pd){ var newInner = ; // not
            Message 5 of 6 , Jul 9, 2007
            • 0 Attachment
              So, now I have worked some on this problem and came up with a start
              (with your help of course :) )


              function showimages(im, pd){
              var newInner = ""; // not really necessary as it should start out
              empty by default.
              for (var i =0; i < 5; i++){
              newInner += "<img border='0' src='";
              newInner += im[i] + "'";
              newInner += " width='50' height='50'";
              newInner += " onmouseover='get_match(\"" +pd[i] + "\");'";
              newInner += " id='test' class='clickable'><br>";
              if (i==2 || i==5){ //to arrange the pictures nicely, in a
              pattern.
              newInner += "<br>";
              }
              }
              document.getElementById("pictures").innerHTML = newInner;
              init3();
              }

              function addListener(addTo){

              YAHOO.util.Event.addListener(addTo, "click",
              YAHOO.lager.container.overlay1.show, YAHOO.lager.container.overlay1,
              true);
              newInner2 = "<img src='" + addTo.src + "'/>";
              document.getElementById("overlay1").innerHTML = newInner2;

              }

              function clickHandler(sourceEvent, clickedOn){

              }


              function init3(){

              var myImages =
              YAHOO.util.Dom.getElementsByClassName('clickable', 'img');
              for(var i=0; i<myImages.length; i++){

              addListener(myImages[i]);
              }
              }

              This generates a "div" that shows the LAST picture in my array of
              selected picures loaded. What I want is to display the one beeing
              clicked. I have tried to pass an Id of the one beeing clicked, but
              that ruins most of it instead. Any ideas?

              Once again, thanks for the excellent help!
            Your message has been successfully submitted and would be delivered to recipients shortly.