views:

531

answers:

1

I am writing a Facebook app in PHP/FBJS. I have some code where I attach an addEventListener() to two buttons. When I run the app, the first button I click on fires the addEventListener() and the event handler is invoked as expected. But if I click on the second button or click on the same button again, the event handler is not invoked. Here is my code:

//PHP
      public function loadCargoDialogFbjsAction() {
        $this->_helper->layout()->disableLayout();
        $this->_helper->viewRenderer->setNoRender();

        $loadableCargo = $this->getRequest()->getPost('loadableCargo');
        $fbjs =
          '<div id="load_cargo_select">
            <form id="load_cargo_select_form" action="" method="POST">
              <p>Your train has stopped in the city of ' . $loadableCargo['city'] . '</p>
              <p>' . $loadableCargo['city'] . ' produces the following goods:</p>
              <ul>';
        if(count($loadableCargo['city_goods']) <= 0) {
          $fbjs .= '<li>None</li>';
        } else {
          foreach($loadableCargo['city_goods'] as $goods) {
            $fbjs .= '<li>' . $goods['name'] . '</li>';
          }
        }
        $fbjs .=
              '</ul>
              <p>Your train is hauling the following goods:</p>
              <ul>';
              if(count($loadableCargo['train_goods']) <= 0) {
                $fbjs .= '<li>None</li>';
              } else {
                foreach($loadableCargo['train_goods'] as $goods) {
                  $fbjs .= '<li>' . $goods['name'] . '</li>';
                }
              }
        $fbjs .=
              '</ul>
              <p>What would you like to do?</p>
              <input type="button" id="load-new-submit" name="load-cargo-new" value="Load new goods" />
              &nbsp;&nbsp;
              <input type="button" id="discard-existing-submit" name="load-cargo-discard" value="Discard existing goods" />
            </form>
          </div>';

        echo $fbjs;
      }


// JavaScript/FBJS
    function loadCargo() {
      var actionPrompt = document.getElementById('action-prompt');
      actionPrompt.setTextValue('Loading cargo...');

      var ajax = new Ajax();
      ajax.responseType = Ajax.JSON;
      ajax.ondone = function(data) {
    //debugger;
        ajax.responseType = Ajax.FBML;
        ajax.ondone = function(fbjsData) {
    //debugger;
          if(data.loadableCargo.length == 0) {
            moveTrainManual();
          } else {
            var dialog = new Dialog().showChoice('Load Cargo', fbjsData, 'Minimize', 'Pass');

            var dlgBtnNew = document.getElementById('load-new-submit');
            dlgBtnNew.cityId = data.loadableCargo.city_id;
            dlgBtnNew.trainId = data.loadableCargo.train_id;
            dlgBtnNew.addEventListener('click', cargoEventHandler); //loadNewCargo);

            var dlgBtnDiscard = document.getElementById('discard-existing-submit');
            dlgBtnDiscard.cityId = data.loadableCargo.city_id;
            dlgBtnDiscard.trainId = data.loadableCargo.train_id;
            dlgBtnDiscard.addEventListener('click', cargoEventHandler); //discardExistingCargo);

            dialog.onconfirm = function() {
              // Submit the form if it exists, then hide the dialog.
              dialog.hide();
              actionPrompt = document.getElementById('action-prompt');
              actionPrompt.setInnerXHTML('<span><div id="action-text">'+
                'The "Load cargo" dialog has been minimized'+
                '</div>'+
                '<div id="action-end">'+
                '<form action="" method="POST">'+
                '<input type="button" value="Maximize" id="next-phase" onclick="loadCargo();" />'+
                '</form>'+
                '</div></span>');
              actionButton = document.getElementById('next-phase');
              actionButton.setValue('Maximize');
              actionButton.addEventListener('click', loadCargoEventHandler);
            };
            dialog.oncancel = function() {
              moveTrainManual();
            }
          }
        }
        ajax.post(baseURL + '/turn/load-cargo-dialog-fbjs', data);
      }
      ajax.post(baseURL + '/turn/load-cargo');
    }

    function cargoEventHandler(evt) {
      //new Dialog().showMessage('loadNewCargo', 'city id='+cityId+', train id='+trainId);
    //debugger;
      cityId = evt.target.cityId;
      trainId = evt.target.trainId;

      switch(evt.target.getId()) {
        case 'load-new-submit':
          ajax = new Ajax();
          ajax.responseType = Ajax.JSON;
          param = { 'load-cargo-submit': "Load new goods", 'city-id': cityId, 'train-id': trainId };
          ajax.ondone = function(data) {
            openCargoHolds = data.openCargoHolds;
            cargoHoldsUsed = 0;
            ajax.responseType = Ajax.FBML;
            param = { 'openCargoHolds': data.openCargoHolds, 'cityGoods': data.cityGoods, 'trainId': data.trainId };
            ajax.ondone = function(fbjsData) {
        //debugger;
              var dialog = new Dialog().showChoice('Load Cargo', fbjsData, 'Load cargo', 'Cancel');
              dialog.onconfirm = function() {
                var goods = [];
                var goodsIds = [];
                numGoods = document.getElementById('goods-count').getValue();

                for(var i = 0; i < numGoods; i++) {
                  j = i + 1;
                  goods[i] = document.getElementById('goods-' + j).getValue();
                  goodsIds[i] = document.getElementById('goods-id-' + j).getValue();
                }
                var trainId = document.getElementById('train-id').getValue();
                param = { "goods": goods, "goods-id": goodsIds, "train-id": trainId };
                ajax.responseType = Ajax.JSON;
                ajax.ondone = function(data) {
                  loadCargo();
                }
                ajax.post(baseURL + '/turn/do-load-cargo-new', param);
                //dialog.hide();
              };
              dialog.oncancel = function() {
                loadCargo();
              }
            }
            ajax.post(baseURL + '/turn/load-cargo-new-dialog-fbjs', param);
          }
          ajax.post(baseURL + '/turn/load-cargo-select', param);
          break;
        case 'discard-existing-submit':
          ajax = new Ajax();
          ajax.responseType = Ajax.JSON;
          param = { 'load-cargo-submit': "Discard existing goods", 'city-id': cityId, 'train-id': trainId };
          ajax.ondone = function(data) {
            ajax.responseType = Ajax.FBML;
            param = { 'openCargoHolds': data.openCargoHolds, 'trainGoods': data.trainGoods, 'trainId': data.trainId };
            ajax.ondone = function(fbjsData) {
              var dialog = new Dialog().showChoice('Discard Cargo', fbjsData, 'Discard cargo', 'Cancel');
              dialog.onconfirm = function() {
                var goods = [];
                var goodsIds = [];
                numGoods = document.getElementById('goods-count').getValue();
                for(var i = 0; i < numGoods; i++) {
                  j = i + 1;
                  goods[i] = document.getElementById('goods-' + j).getValue();
                  goodsIds[i] = document.getElementById('goods-id-' + j).getValue();
                }
                var trainId = document.getElementById('train-id').getValue();
                param = { "goods": goods, "goods-id": goodsIds, "train-id": trainId };
                ajax.responseType = Ajax.JSON;
                ajax.ondone = function(data) {
                  loadCargo();
                }
                ajax.post(baseURL + '/turn/do-load-cargo-discard', param);
                //dialog.hide();
              };
              dialog.oncancel = function() {
                loadCargo();
              }
            }
            ajax.post(baseURL + '/turn/load-cargo-discard-dialog-fbjs', param);
          }
          ajax.post(baseURL + '/turn/load-cargo-select', param);
          break;
      }
      return false;
    }

Any help would be greatly appreciated. Thanks!

@Tim

I changed my loadCargo() function as follows to prevent duplication of those elements, but I am still running into the same problem as before.

var loadCargoDialog;

function loadCargo() {
  var actionPrompt = document.getElementById('action-prompt');
  actionPrompt.setTextValue('Loading cargo...');

  var ajax = new Ajax();
  ajax.responseType = Ajax.JSON;
  ajax.ondone = function(data) {
//debugger;
    ajax.responseType = Ajax.FBML;
    ajax.ondone = function(fbjsData) {
//debugger;
      if(data.loadableCargo.length == 0) {
        moveTrainManual();
      } else {
        if(loadCargoDialog == null) {
          loadCargoDialog = new Dialog().showChoice('Load Cargo', fbjsData, 'Minimize', 'Pass');

          var dlgBtnNew = document.getElementById('load-new-submit');
          dlgBtnNew.cityId = data.loadableCargo.city_id;
          dlgBtnNew.trainId = data.loadableCargo.train_id;
          dlgBtnNew.addEventListener('click', cargoEventHandler, true); //loadNewCargo);

          var dlgBtnDiscard = document.getElementById('discard-existing-submit');
          dlgBtnDiscard.cityId = data.loadableCargo.city_id;
          dlgBtnDiscard.trainId = data.loadableCargo.train_id;
          dlgBtnDiscard.addEventListener('click', discardExistingCargo, true);

        } else {
          loadCargoDialog.showChoice('Load Cargo', fbjsData, 'Minimize', 'Pass');
        }

        loadCargoDialog.onconfirm = function() {
          // Submit the form if it exists, then hide the dialog.
          loadCargoDialog.hide();
          actionPrompt = document.getElementById('action-prompt');
          actionPrompt.setInnerXHTML('<span><div id="action-text">'+
            'The "Load cargo" dialog has been minimized'+
            '</div>'+
            '<div id="action-end">'+
            '<form action="" method="POST">'+
            '<input type="button" value="Maximize" id="next-phase" onclick="loadCargo();" />'+
            '</form>'+
            '</div></span>');
          actionButton = document.getElementById('next-phase');
          actionButton.setValue('Maximize');
          actionButton.addEventListener('click', loadCargoEventHandler);
        };
        loadCargoDialog.oncancel = function() {
          moveTrainManual();
        }
      }
    }
    ajax.post(baseURL + '/turn/load-cargo-dialog-fbjs', data);
  }
  ajax.post(baseURL + '/turn/load-cargo');
}
A: 

It looks like you might be creating elements with the same id. That would cause it to break.

Tim
Are you referring to elements in my code sample, or elsewhere in my app? If they are in my code sample, would you please identify them? Thanks.
Chris Barnhill
You may be right. It may have something to do with creating a second instance of the "Load Cargo" dialog. Maybe the confusion is due to there being two instances of the same dialog containing the same elements with the same IDs? Can you think of a solution for this?
Chris Barnhill
load-new-submit, discard-existing-submitI didn't look at the code that thoroughly, but it looks like those Id's get returned every time you make the server call as part of the response.
Tim
You can add some sort of namespacing or ids to the end of the id. You would have to maintain a unique identifier for each instance.
Tim
Thanks Tim. I tried to fix this, but it didn't work. Would you mind looking at my edits in the original post and help me figure out what I'm still doing wrong?
Chris Barnhill
The problem isn't with your javascript, its with your php. The php script is creating markup that has the same html everytime its called - which means you are getting the same ids. If you change it to id="'.$my_uniq_identifier.'"_load-new-submit' then you would get a new div everytime with its own unique Id. But you would have to manage the ids so that you wind up with the div you want.
Tim
OK, I think I understand it now. I added a unique namespace to each ID as I declared it and now the event handler fires correctly. Thank you for your help!
Chris Barnhill
Tim, I just wanted to add a note to tell you how grateful I am for your help. I never would have found this one on my own. You saved me days of debugging and frustration. I owe you one.
Chris Barnhill