Search code examples
javascriptjquerybackbone.jscallback

Losing context in callback when invoked the second time backbone


The following code works, but I think there's room for improvement. The index check is there because after the first element is removed the next element looks like it has an index of -1, but is actually the previously removed element. Then it iterates again and finds the clicked element and removes it. BUT since the index is -1 on the first go around the wrong group gets deleted.

How do I keep the zombie elements from being iterated on more efficiently? This is in a backbone view with an in page confirmation.Thanks.

EDIT: To add HTML

Group section always has a default group that shouldn't be deleted.

<div class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta  cta--std-teal  js-expand-trigger"><a href="#"><span class="icon  icon-plus--teal  expandable__cta-icon"></span>All Doctors</a></h4>
<div class="expandable__content   js-favorite-doctor-row-container" aria-expanded="true">
  <div class="location-section dr-profile">
    <div class="section__content js-doctor-row">
      <div class="favorite-doctor-manage__row">
       DR info
      </div>
    </div><!--/section__content-->
  </div><!--/location-section-->
</div><!--/expandable__content-->

Tag section to remove groups

<div class="js-favorite-doctor-manage-add-remove">
<div class="grid-construct">
  <div class="expandable" data-controller="expandable">
    <ul class="tag-list js-group-list" tabindex="-1">
      <li class="tag  tag--teal" >
        Lauren's Doctors
        <ul class="tag-sub">
          <li><button class="tag-icon  tag-icon--close-white  js-group-remove">Remove group: Lauren's Doctors</button></li>
        </ul>
      </li>
      <li class="tag  tag--teal" >
        Timmy's Doctors
        <ul class="tag-sub">
          <li><button class="tag-icon  tag-icon--close-white  js-group-remove">Remove group: Timmy's Doctors</button></li>
        </ul>
      </li>
    </ul>
</div>

removeGroup: function( evt ) {
  var deleteGroup = function() {
    if ( $(evt.currentTarget).closest('.tag').hasClass('is-active')){
      var clickedTag = $(evt.currentTarget).closest('.tag');
      var groupList = this.$el.find('.js-group-list');
      var groupTags = groupList.find('.tag');
      var index = groupTags.index(clickedTag);
      var groupSections = $('.js-favorite-group');
      // add one to account for "All" section which is never removed
      var groupToRemove = groupSections.eq(index + 1);
      console.log(groupToRemove);
      var removedGroupName = this.getGroupNameForSection(groupToRemove);
      var allDoctors = groupSections.eq(0);
      var allDoctorsContainer = allDoctors.find('.js-favorite-doctor-row-container');
      if ( index > -1 ){
        groupToRemove.find('.js-favorite-doctor-row').appendTo(allDoctorsContainer);
        clickedTag.remove();
        groupToRemove.remove();
        this.updateSectionDropdowns();
        this.ariaAlert('Group ' + removedGroupName + ' removed');
        this.hideConfirm(evt);
      }
    }  
  };

  this.showAlert(evt, deleteGroup);

},

showAlert: function (evt, callback) {
  that = this;
  var clickedTag = '';
  clickedTag = $(evt.currentTarget).closest('.tag');
  clickedTag.addClass('is-active').attr('data-delete','true');
  $('.delete-acct-message').show().focus();

  $('.js-remove-yes').on('click', function(evt){
    evt.preventDefault();
    callback.apply(that); 
  });
  $('.js-remove-no').on('click', function(evt){
    evt.preventDefault();
    this.hideConfirm(evt);
  });
},

Solution

  • I would suggest that you should use custom attributes in your html, this will simplify your javascript logic and make it more effective and efficient. I have modified your html and javascript to add the support for custom attribute data-doc-group. Have a look at your group sections div here

    <div data-doc-group="lauren" class="section-border-top--grey js-favorite-group">
    <h4 class="expandable__cta  cta--std-teal  js-expand-trigger"><a href="#"><span class="icon  icon-plus--teal  expandable__cta-icon"></span>Lauren's Doctors</a></h4>
    <div class="expandable__content   js-favorite-doctor-row-container" aria-expanded="true">
      <div class="location-section dr-profile">
        <div class="section__content js-doctor-row">
          <div class="favorite-doctor-manage__row">
           DR info
          </div>
        </div><!--/section__content-->
      </div><!--/location-section-->
    </div>
    

    Here are the tags with custom attributes

    <li data-doc-group="lauren" class="tag  tag--teal">
      Lauren's Doctors
      <ul class="tag-sub">
        <li><button class="tag-icon  tag-icon--close-white  js-group-remove">Remove group: Lauren's Doctors</button></li>
      </ul>
    </li>
    <li data-doc-group="timmy" class="tag  tag--teal">
      Timmy's Doctors
      <ul class="tag-sub">
        <li><button class="tag-icon  tag-icon--close-white  js-group-remove">Remove group: Timmy's Doctors</button></li>
      </ul>
    </li>
    

    Here is the javascript to handle this, (this may be a bit buggy, but will give you a general idea)

    removeGroup: function(evt) {
      this.showAlert(evt, function() {
        var $clickedTag = $(evt.currentTarget).closest('.tag'),
          dataGroupName,
          $groupToRemove,
          removedGroupName,
          $allDoctors = $('.js-favorite-group').eq(0),
          $allDoctorsContainer = $allDoctors.find('.js-favorite-doctor-row-container');
    
        if ($clickedTag.hasClass('is-active')){
          dataGroupName = $clickedTag.data('doc-group');
          $groupToRemove = $allDoctors.siblings('[data-doc-group="' + docGroupName + '"]');
    
          if ($groupToRemove.length > 0){
            $groupToRemove.find('.js-favorite-doctor-row').appendTo($allDoctorsContainer);
            $clickedTag.remove();
            $groupToRemove.remove();
    
            removedGroupName = this.getGroupNameForSection($groupToRemove);
    
            this.updateSectionDropdowns();
            this.ariaAlert('Group ' + removedGroupName + ' removed');
            this.hideConfirm(evt);
          }
        }  
      });
    }