Phaelax
Phaelax

Reputation: 2017

Issue sorting select options

The linked snippet is just a rough example of what I'm doing but will demonstrate the issue I'm having. I have two select boxes that I can transfer items between. I need to track when an item has moved from its original box for when I submit changes through an ajax call later because I don't want to include the entire list in the transmission, only whats necessary. (my real data is much larger lists)

To keep track, I'm using a data attribute, "data-changed". And to help reflect what's been flagged for updating, I toggle a class on that particular option. This all works fine and dandy. My problem is when I try to implement sorting on these lists. The options get sorted as expected but it seems like it's losing its data attribute in the process, which breaks the other stuff. I can't figure out how to fix this.

https://jsfiddle.net/bnLfhpq4/

$('#btnSubmit').click(function(e) {
  e.preventDefault();

  var arr = new Array();

  // get options from active list
  var options = $('#activeList option');
  options.each(function(i, o) {
    if ($(o).data('changed') == "yes") {
      var skill = {
        id: $(o).val(),
        active: 0
      }
      arr.push(skill);
    }
  });


  // get options from inactive list
  var options = $('#inactiveList option');
  options.each(function(i, o) {
    if ($(o).data('changed') == "yes") {
      var skill = {
        id: $(o).val(),
        active: 1
      }
      arr.push(skill);
    }
  });

  console.log(arr);
});


$('#btn_in').click(function() {
  var options = $('#activeList option:selected');

  options.each(function(i, o) {
    var c = $(o).data('changed');
    if (c == "yes") {
      $(o).data('changed', 'no');
      $(o).removeClass('flagged');
    } else {
      $(o).data('changed', 'yes');
      $(o).addClass('flagged');
    }
  });

  $('#inactiveList').append(options);

  //sortOptions('#inactiveList');

});




$('#btn_out').click(function() {
  var options = $('#inactiveList option:selected');

  options.each(function(i, o) {
    var c = $(o).data('changed');
    if (c == "yes") {
      $(o).data('changed', 'no');
      $(o).removeClass('flagged');
    } else {
      $(o).data('changed', 'yes');
      $(o).addClass('flagged');
    }
  });

  $('#activeList').append(options);
  sortOptions('#activeList');

});





function sortOptions(selector) {
  var options = $(selector + ' option');

  options.sort(function(a, b) {
    if (a.text > b.text) return 1;
    if (a.text < b.text) return -1;
    return 0;
  });

  $(selector).empty().append(options);


}
.superSelect {
  min-height: 200px;
}

.buttons {
  display: inline-block
}

.flagged {
  color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select multiple id="activeList" class="superSelect">
  <option value="t1" data-changed="no">Option 1</option>
  <option value="t2" data-changed="no">Option 2</option>
  <option value="t3" data-changed="no">Option 3</option>
  <option value="t4" data-changed="no">Option 4</option>
</select>

<div class="buttons">
  <button id="btn_in">>>></button><br/><br/>
  <button id="btn_out"><<<</button>
</div>


<select multiple id="inactiveList" class="superSelect">
  <option value="x1" data-changed="no">Thingy 1</option>
  <option value="x2" data-changed="no">Thingy 2</option>
  <option value="x3" data-changed="no">Thingy 3</option>
  <option value="x4" data-changed="no">Thingy 4</option>
</select>

<br/>

<button id="btnSubmit">Submit</button>

Upvotes: 0

Views: 121

Answers (1)

Sarah Gro&#223;
Sarah Gro&#223;

Reputation: 10879

  1. A very good explanation of why not to mix data- attributes and data() has already been given in this answer. Therefore, change everything to use .attr('data-changed') and it will work.

    The reason it worked without the ordering functionality is because the association between the data that jQuery stores internally and the DOM node is apparently lost when the node is removed from the DOM via empty() and re-added via append().

  2. You do not need to use an extra class for visualisation of the changed options. Just use the attribute selector [data-changed="yes"] in your CSS.
  3. The two callbacks of each button click event were almost identical, only differing in the list IDs being swapped. I refactored the code to use one single function and dynamically switch between the lists.
  4. Although this might only be the case in the code you created for this demonstration, and not in your actual app: Avoid using the < and > characters (in your button labels) without encoding them as HTML entities &lt;/&gt;. Although all major browsers should render the buttons just fine, there is a chance that a client's parser might get confused and lead to some display issues.

$('#btnSubmit').click(function(e) {
  e.preventDefault();

  var arr = new Array();

  // get options from active list
  var options = $('#activeList option');
  options.each(function(i, o) {
    if ($(o).data('changed') == "yes") {
      var skill = {
        id: $(o).val(),
        active: 0
      }
      arr.push(skill);
    }
  });


  // get options from inactive list
  var options = $('#inactiveList option');
  options.each(function(i, o) {
    if ($(o).data('changed') == "yes") {
      var skill = {
        id: $(o).val(),
        active: 1
      }
      arr.push(skill);
    }
  });

  console.log(arr);
});


$activeList = $('#activeList');
$inactiveList = $('#inactiveList');

function moveOptions() {
  var $otherList = this === $activeList ? $inactiveList : $activeList;
  var options = $('option:selected', this);

  options.each(function(i, o) {
    var c = $(o).data('changed');
    if (c == "yes") {
      $(o).attr('data-changed', 'no');
    } else {
      $(o).attr('data-changed', 'yes');
    }
  });

  $otherList.append(options);
  sortOptions($otherList);
}

$('#btn_in').click(moveOptions.bind($activeList));
$('#btn_out').click(moveOptions.bind($inactiveList));

function sortOptions(list) {
  var options = $('option', list);

  options.sort(function(a, b) {
    if (a.text > b.text) return 1;
    if (a.text < b.text) return -1;
    return 0;
  });

  list.empty().append(options);


}
.superSelect {
  min-height: 200px;
}

.buttons {
  display: inline-block
}

[data-changed="yes"] {
  color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select multiple id="activeList" class="superSelect">
  <option value="t1" data-changed="no">Option 1</option>
  <option value="t2" data-changed="no">Option 2</option>
  <option value="t3" data-changed="no">Option 3</option>
  <option value="t4" data-changed="no">Option 4</option>
</select>

<div class="buttons">
  <button id="btn_in">&gt;&gt;&gt;</button><br/><br/>
  <button id="btn_out">&lt;&lt;&lt;</button>
</div>


<select multiple id="inactiveList" class="superSelect">
  <option value="x1" data-changed="no">Thingy 1</option>
  <option value="x2" data-changed="no">Thingy 2</option>
  <option value="x3" data-changed="no">Thingy 3</option>
  <option value="x4" data-changed="no">Thingy 4</option>
</select>

<br/>

<button id="btnSubmit">Submit</button>

Upvotes: 2

Related Questions