Using Jquery, replace specific row with specific row with using up and down button in table

30 views Asked by At

https://i.stack.imgur.com/4PpsO.png

This is very unique requirement that I want to make up/down (sort order row) functionality. I can able to do up/down using jQuery but I have required complicated requirement which I tried but can't able to do it. As I attached snapshot. If I click down button #trMain-1989 then that row with next 4 row move after #trMain-1991

Same thing if I click up button of #trMain-1995 then it will move all 5 rows(#trMain-1995, #trLang-1995, #trQuestion-1995, #trAnswer-1995 and #trAct-1995) move to up before #trMain-1993

Here are my codes which I am trying last 3-4 days but not success.

Code: 1

$(document).on('click','.fas', function(event){
    var $element = this;
    var row = $($element).parents("tr:first");
    let index = oTable.row(row).index();
    console.log(index);
    if($(this).is('.up')){
        row.eq(index-4).insertBefore(row.prev());
    }else{
        row.eq(index+4).insertAfter(row.next());
    }
    return false;
    });

Code: 2

$(document).on('click','.fas', function(event){
    
    if(oTable){
        let tr = $(this).closest('tr');
        let cls = $(tr).attr("class");
        //console.log(cls);
            cls = cls.split("-");
        //console.log(cls[1]);
        let index = oTable.row(tr).index();
        let indexes = oTable.rows()[0];
        
        let order = -1;
        let buttonName = event.currentTarget.className;
        if(buttonName.indexOf('down') > 0){
            order = 1;
        }
        
        let data1 = oTable.row(index).data();
        console.log(data1);
        let base_index = indexes.indexOf(index) + order;
        console.log(base_index);
        let index2 = indexes[base_index];
        console.log(index2);
        var data2 = oTable.row(index2).data();
        //console.log(data1);
        oTable.row(index).data(data2);
        oTable.row(index2).data(data1);
});

I am expecting that if anyone can help me to correct my code or give any smart solution if it then it would be great for me. Thanks

2

There are 2 answers

0
IT goldman On

You want to move them as if they were groups.

Each group start with a tr that has class that starts with trMain- and ends with a tr that has class that starts with trAct-.

In order to move up from tr_current we find the previous trMain- and call it tr_target. We start moving our tr_current and all its next siblings to before the tr_target until tr_current points to the next trMain- which we won't move.

In order to move down, it's the same logic only a little more advanced since we move like 10 elements below rather than 5 above. Look at code.

document.querySelectorAll(".up").forEach(function(button) {
  button.addEventListener('click', moveUp)
})

document.querySelectorAll(".down").forEach(function(button) {
  button.addEventListener('click', moveDown)
})

function moveUp(ev) {
  var tr_current = ev.target.closest('tr')

  // find previous tr that has class that starts with 'main-'
  var tr_target = tr_current.previousElementSibling
  while (tr_target && ![...tr_target.classList].some((cls) => cls.startsWith('trMain-'))) {
    tr_target = tr_target.previousElementSibling
  }
  if (!tr_target) {
    return
  }

  // move each tr_current before tr_target until has class that starts with 'main-'
  do {
    var next = tr_current.nextElementSibling
    tr_current.parentNode.insertBefore(tr_current, tr_target)
    tr_current = next
  } while (tr_current && ![...tr_current.classList].some((cls) => cls.startsWith('trMain-')))

}

// same logic but a step further 
function moveDown(ev) {
  var tr_current = ev.target.closest('tr')

  var tr_target = tr_current.nextElementSibling
  while (tr_target && ![...tr_target.classList].some((cls) => cls.startsWith('trMain-'))) {
    tr_target = tr_target.nextElementSibling
  }
  if (!tr_target) {
    return
  }
  var tr_target = tr_target.nextElementSibling
  while (tr_target && ![...tr_target.classList].some((cls) => cls.startsWith('trAct-'))) {
    tr_target = tr_target.nextElementSibling
  }

  do {
    var next = tr_current.nextElementSibling
    tr_current.parentNode.insertBefore(tr_current, tr_target.nextElementSibling)
    tr_current = next
    tr_target = tr_target.nextElementSibling
  } while (tr_current && ![...tr_current.classList].some((cls) => cls.startsWith('trMain-')))

}
table {
  width: 100%;
  border-collapse: collapse;
}

[class*="trMain-"] {
  background: yellow;
}
<table border="1">
  <tbody>
    <tr class="trMain-1989">
      <td>trMain 1989 <button class="up">up</button> <button class="down">down</button></td>
    </tr>
    <tr class="trLang-1989">
      <td>trLang 1989</td>
    </tr>
    <tr class="trQuestion-1989">
      <td>trQuestion 1989</td>
    </tr>
    <tr class="trAnswer-1989">
      <td>trAnswer 1989</td>
    </tr>
    <tr class="trAct-1989">
      <td>trAct 1989</td>
    </tr>
    <tr class="trMain-1991">
      <td>trMain 1991 <button class="up">up</button> <button class="down">down</button></td>
    </tr>
    <tr class="trLang-1991">
      <td>trLang 1991</td>
    </tr>
    <tr class="trQuestion-1991">
      <td>trQuestion 1991</td>
    </tr>
    <tr class="trAnswer-1991">
      <td>trAnswer 1991</td>
    </tr>
    <tr class="trAct-1991">
      <td>trAct 1991</td>
    </tr>
    <tr class="trMain-1993">
      <td>trMain 1993 <button class="up">up</button> <button class="down">down</button></td>
    </tr>
    <tr class="trLang-1993">
      <td>trLang 1993</td>
    </tr>
    <tr class="trQuestion-1993">
      <td>trQuestion 1993</td>
    </tr>
    <tr class="trAnswer-1993">
      <td>trAnswer 1993</td>
    </tr>
    <tr class="trAct-1993">
      <td>trAct 1993</td>
    </tr>
    <tr class="trMain-1995">
      <td>trMain 1995 <button class="up">up</button> <button class="down">down</button></td>
    </tr>
    <tr class="trLang-1995">
      <td>trLang 1995</td>
    </tr>
    <tr class="trQuestion-1995">
      <td>trQuestion 1995</td>
    </tr>
    <tr class="trAnswer-1995">
      <td>trAnswer 1995</td>
    </tr>
    <tr class="trAct-1995">
      <td>trAct 1995</td>
    </tr>
  </tbody>
</table>

0
poorly-written-code On

As of jQuery 3.4, the :first pseudo-class is deprecated. Remove it from your selectors and filter the results later using .first().

Since there is already an answer for your current schema, I'll suggest some improvements.

  • Pre-fixing your class names with tr is redundant when the element name is also tr. This expands to tr.trMain-1999 which could be reduced to tr.main-1999 without loosing any information.

  • Your class names appear to post-fixed with a year, and since we want to group by this data, you could store this as a data- property instead of a class.

let GetGroup = (group) => $(`table tr[data-group="${group}"]`);

$('table') //this is just boiler plate to generate a table
  .html([...Array(10)].map((u, i) =>  ['main','language','question','answer','act'].map((item) => `<tr class="${item}" data-group="${i + 1985}" style="background-color:#${(i * 369e3).toString(16).padStart(6, '0')}22"><td><button data-move="up">↑</button><button data-move="down">↓</button><span>${i + 1985}</span><span>${item}</span></td></tr>`)).join(''))
  .on('click', 'button[data-move]', function() {
    let $group = GetGroup($(this).parents('tr').attr('data-group'));
    
    ({
      up: () => {
        if ($group.first().prev()) { //can we move up?
          $group.insertBefore(
            GetGroup($group.first().prev().attr('data-group')).first());
        }
      },
      down: () => {
        if ($group.last().next()) { //can we move down?
          $group.insertAfter(
            GetGroup($group.last().next().attr('data-group')).last());
        }
      }
    }[this.dataset.move])(); //which button was clicked
  });
<table></table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>