radiantshaw
radiantshaw

Reputation: 545

Reorder li items according to a variable/condition

I have a list as follows:

<ul>
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
  <li>Fifth</li>
</ul>

What I want is to order the list based on different conditions. For example if the variable state == "foo", I want the Fourth li to appear first and the Second li to appear last like so:

<ul>
  <li>Fourth</li>
  <li>First</li>
  <li>Third</li>
  <li>Fifth</li>
  <li>Second</li>
</ul>

And if the state == "bar" I want the list to appear in some other way.

Also consider that there may be future conditions where state may be equal to "baz" or "foobar" each having their own order. So writing custom conditionals for each state is not an option.

My idea was to maintain an ordered array for each state and then render the list according to the array using something like Mustache or Handlebars. Is there any better solution to this which is also future proof?

Upvotes: 3

Views: 1771

Answers (5)

Takit Isy
Takit Isy

Reputation: 10081

Here is how I'll do it:

  • Using an object to store the different orders,
  • Using a .forEach() to follow the selected order to re-order the lis.

New snippets

  • Using .map(), the on 'change' function can be reduced to a single line!

Without jQuery (lighter!)

var original_list = document.querySelectorAll('#listToOrder li');
const orders = {
  def: [0, 1, 2, 3, 4],
  foo: [3, 0, 2, 4, 1],
  bar: [3, 2, 4, 1, 0],
  baz: [2, 4, 0, 3, 1]
}

document.querySelector('#selector').addEventListener('change', function() {
  document.querySelector('#listToOrder').innerHTML = orders[this.value].map(index => original_list[index].outerHTML).join('');
});
<ul id="listToOrder">
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
  <li>Fifth</li>
</ul>
<label>Select order: </label>
<select id="selector">
  <option value="def">Default</option>
  <option value="foo">Foo</option>
  <option value="bar">Bar</option>
  <option value="baz">Baz</option>
</select>

With jQuery

var original_list = $('#listToOrder li');
const orders = {
  def: [0, 1, 2, 3, 4],
  foo: [3, 0, 2, 4, 1],
  bar: [3, 2, 4, 1, 0],
  baz: [2, 4, 0, 3, 1]
}

$('#selector').on('change', function() {
  $('#listToOrder').html(orders[this.value].map(index => original_list[index].outerHTML));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="listToOrder">
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
  <li>Fifth</li>
</ul>
<label>Select order: </label>
<select id="selector">
  <option value="def">Default</option>
  <option value="foo">Foo</option>
  <option value="bar">Bar</option>
  <option value="baz">Baz</option>
</select>


Old snippet

I couldn't think about an easier way… but .map() was a good point!

var original_list = $('#listToOrder li');
const orders = {
  def: [0, 1, 2, 3, 4],
  foo: [3, 0, 2, 4, 1],
  bar: [3, 2, 4, 1, 0],
  baz: [2, 4, 0, 3, 1]
}

$('#selector').on('change', function() {
  var lis = '';
  orders[this.value].forEach(function(i){
    lis += original_list[i].outerHTML;
  })
  $('#listToOrder').html(lis);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="listToOrder">
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
  <li>Fifth</li>
</ul>
<label>Select order: </label>
<select id="selector">
  <option value="def">Default</option>
  <option value="foo">Foo</option>
  <option value="bar">Bar</option>
  <option value="baz">Baz</option>
</select>

Upvotes: 4

Yosvel Quintero
Yosvel Quintero

Reputation: 19070

I suggest to work with a constant for data and an array of states objects to give the appropriate order.

dataOrder key in the states will hold the order for the indexed data array:

const data = ['First', 'Second', 'Third', 'Fourth', 'Fifth'];
const states = [
  {
    state: 'foo',
    dataOrder: [0, 1, 2, 3]
  },
  {
    state: 'bar',
    dataOrder: [3, 2, 1, 0]
  }
];

Note: In the template view you can use Mustache or Handlebars as you wrote in the question

Upvotes: 3

aug
aug

Reputation: 11714

Probably the best option is to just maintain a map of the states which map to the order.

const statesOrder = {
  'foo':  [0, 1, 2, 3],
  'baz': [2, 1, 3, 0],
   ...
}

And then access the order by passing in the state.

const listOrder = statesOrder[state];

Also don't forget that if you only need to order the list in a visual way, CSS has an order property you might be able to take advantage of.

Upvotes: 2

Harun Or Rashid
Harun Or Rashid

Reputation: 5937

var values= ["First", "Second", "Third", "Fourth", "Fifth"];
//Re-arrange values on condition(state == "bar"/"foo" etc)

var element = '<ul>'

for(var i = 0; i < values.length; i++){
    var value = values[i];
    element += '<li>'+ value+ '</li>';
} 

element += '</ul>';
document.getElementById("ulContainer").innerHTML = element;
<div id="ulContainer"></div>

Upvotes: 1

vikscool
vikscool

Reputation: 1313

You need to add a unique selector to the elements of the ul's li so you can select them, as for example, I am considering a select will contain the condition selector and then on the onChange of the select I am calling a function which inside is calling a function to change the order of the list.

Here is the snippet:

var list = $('#listToOrder');
var original_order = $('#listToOrder').html();
$('#ddlchangeOrder').on('change', function() {
  var selected_value = this.value;
  orderSwitcher(selected_value);
});

function orderSwitcher(order) {
  switch (order) {
    case "foo":
      var elementToPush = list.find('li[data-select="4"]').clone(); //creating a clone of the element
      list.find('li[data-select="4"]').remove(); //removing the existing element
      list.prepend(elementToPush);
      break;
    case "bar":
      var elementToPush = list.find('li[data-select="2"]').clone(); //creating a clone of the element
      list.find('li[data-select="2"]').remove(); //removing the existing element
      list.prepend(elementToPush);
      break;
      //add other cases as per your need.
    default:
      list.html(original_order);
      break;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="listToOrder">
  <li data-select="1">First</li>
  <li data-select="2">Second</li>
  <li data-select="3">Third</li>
  <li data-select="4">Fourth</li>
  <li data-select="5">Fifth</li>
</ul>
<div>
  <label for="ddlchangeOrder">Define Order</label>
  <select name="" id="ddlchangeOrder">
    <option value="0">Select Order</option>
    <option value="foo">Foo</option>
    <option value="bar">Bar</option>
    <option value="no">no Case</option>
  </select>
</div>

Upvotes: 1

Related Questions