Home > Back-end >  Select one data-attribute per table row and cell
Select one data-attribute per table row and cell

Time:06-24

I have the following html table:

<table border="1" cellpadding="5" >
  <tr>
    <td ><div  data-value="0">aaa</div></td>
    <td ><div  data-value="1">bbb</div></td>
     <td ><div  data-value="2">ccc</div></td>
  </tr>
  <tr>
    <td ><div  data-value="0">ddd</div></td>
    <td ><div  data-value="1">eee</div></td>
    <td ><div  data-value="2">fff</div></td>
  </tr>
  <tr>
    <td ><div  data-value="0">ggg</div></td>
    <td ><div  data-value="1">hhh</div></td>
    <td ><div  data-value="2">iii</div></td>
  </tr>
</table>

If you click on a "div attribute" inside a table cell I need to get the "data-value" of the clicked div attribute. After that I build a query string to use it with "URLSearchParams". This works so far.

Now I need a certain condition. It should be only allowed to select one div-attribute per table row and column. But I don't know how to implement this condition in my code.

This is the Fiddle and the code:

var btn7;
var btn8;
var btn9;

$('.btn7').click(function () {
    if ($(this).attr('data-selected') === 'true') {
        $(this).attr('data-selected', 'false');
        $(this).removeClass('selected');

    } else {
        $(this).closest('tr').find('.btn7').not(this)
        .removeClass('selected').attr('data-selected', 'false');
        $(this).attr('data-selected', 'true');
        $(this).addClass('selected');
        params.set('var7', $(this).data("value"));
        window.history.replaceState({}, '', `?${params}`);
    }
});

$('.btn8').click(function () {
    if ($(this).attr('data-selected') === 'true') {
        $(this).attr('data-selected', 'false');
        $(this).removeClass('selected');

    } else {
        $(this).closest('tr').find('.btn8').not(this)
        .removeClass('selected').attr('data-selected', 'false');
        $(this).attr('data-selected', 'true');
        $(this).addClass('selected');
        params.set('var8', $(this).data("value"));
        window.history.replaceState({}, '', `?${params}`);
    }
});

$('.btn9').click(function () {
    if ($(this).attr('data-selected') === 'true') {
        $(this).attr('data-selected', 'false');
        $(this).removeClass('selected');

    } else {
        $(this).closest('tr').find('.btn9').not(this)
        .removeClass('selected').attr('data-selected', 'false');
        $(this).attr('data-selected', 'true');
        $(this).addClass('selected');
        params.set('var9', $(this).data("value"));
        window.history.replaceState({}, '', `?${params}`);
    }
});


const params = new URLSearchParams({
    var7: btn7,
    var8: btn8,
    var9: btn9,
});

CodePudding user response:

Idea

Mark each table cell with a data- attribute indicating its respective row and column, and maintain 2 arrays that hold the currently selected element (if any) for each of the columns and row.

Implementation

The following code implements the selection logic. Based on the arrays holding the currently active selections you can visit all relevant elements and assemble the parameters when you send a request to the server.

The specs of single cell/row selection implies that there will usually be rows and columns that do not carry a selection.

Note that the case of expressly deselecting a cell is not handled.

The code does not resort to jquery.

<!DOCTYPE html>
<html>
    <head>
        <title>SO _: 1-in-a-row, 1-in-a-col selection</title>
        <style type="text/css">
            .selected {
              background: #333;
              color: #fff;
            }
        </style>
        <script type="text/javascript">
            let a_colSelection = new Array(3)
              , a_rowSelection = new Array(3)
              ;
            
            document.addEventListener ( 'DOMContentLoaded', () => { 
                Array.from(document.querySelectorAll('div[data-row][data-col]')).forEach ( el => {
                    el.addEventListener ( 'click', eve => {
                        let c = parseInt(eve.target.getAttribute('data-col'))
                          , r = parseInt(eve.target.getAttribute('data-row'))
                          ;
                        
                        if (a_colSelection[c] !== undefined) {
                            document.querySelector(`div[data-col="${a_colSelection[c][1]}"][data-row="${a_colSelection[c][0]}"]`).classList.remove("selected");
                        }
                        if (a_rowSelection[r] !== undefined) {
                            document.querySelector(`div[data-col="${a_rowSelection[r][1]}"][data-row="${a_rowSelection[r][0]}"]`).classList.remove("selected");
                        }
                        a_colSelection[c] = [r, c];
                        a_rowSelection[r] = [r, c];
                        document.querySelector(`div[data-col="${a_colSelection[c][1]}"][data-row="${a_rowSelection[r][0]}"]`).classList.add("selected");
                    }); 
                });
            });
        </script>
    </head>
    <body>
        <table border="1" cellpadding="5" >
          <tr>
            <td ><div data-value="0" data-col="0" data-row="0">aaa</div></td>
            <td ><div data-value="1" data-col="1" data-row="0">bbb</div></td>
             <td ><div data-value="2" data-col="2" data-row="0">ccc</div></td>
          </tr>
          <tr>
            <td ><div data-value="0" data-col="0" data-row="1">ddd</div></td>
            <td ><div data-value="1" data-col="1" data-row="1">eee</div></td>
            <td ><div data-value="2" data-col="2" data-row="1">fff</div></td>
          </tr>
          <tr>
            <td ><div data-value="0" data-col="0" data-row="2">ggg</div></td>
            <td ><div data-value="1" data-col="1" data-row="2">hhh</div></td>
            <td ><div data-value="2" data-col="2" data-row="2">iii</div></td>
          </tr>
        </table>
    </body>
</html>

CodePudding user response:

Consider the following.

Fiddle: https://jsfiddle.net/Twisty/dzng31f5/39/

HTML

<table border="1" cellpadding="5" >
  <tr >
    <td >
      <div  data-value="0">aaa</div>
    </td>
    <td >
      <div  data-value="1">bbb</div>
    </td>
    <td >
      <div  data-value="2">ccc</div>
    </td>
  </tr>
  <tr >
    <td >
      <div  data-value="0">ddd</div>
    </td>
    <td >
      <div  data-value="1">eee</div>
    </td>
    <td >
      <div  data-value="2">fff</div>
    </td>
  </tr>
  <tr >
    <td >
      <div  data-value="0">ggg</div>
    </td>
    <td >
      <div  data-value="1">hhh</div>
    </td>
    <td >
      <div  data-value="2">iii</div>
    </td>
  </tr>
</table>

I adjusted the HTML Structure, such that each Row has a Class that represents the Index name that will be used in the Object.

jQuery

$(function() {
  function checkCol(colIndex, table) {
    var result = true;
    console.log("Col Index:"   colIndex)
    $("tbody tr", table).each(function(i, el) {
      result = result && !$("td:eq("   colIndex   ") div.btn", el).hasClass("selected");
    });
    return !result;
  }

  function checkRow(target, row) {
    var isInCol = checkCol($(target).parent().index(), $(row).closest("table"));
    if (!isInCol) {
      if ($(".selected", row).length) {
        $(".selected", row).removeClass("selected");
        $(target).addClass("selected");
      } else {
        $(target).addClass("selected");
      }
    }
  }
  
  var selected = {};
  
  $('.btn').click(function(event) {
    var row = $(this).closest("tr");
    checkRow(this, row);
    $(".test tbody tr").each(function(i, el) {
      selected[$(el).attr("class")] = $(".selected", el).length ? $(".selected", el).data("value") : "";
    });
    console.log(selected);
    var params = new URLSearchParams(selected);
    console.log(params.toString());
  });
});

You can now use selected as your Data in a POST or GET call.

Updated

I had missed that each Row and Column needed to be unique. Code is updated to use Functions to check both conditions.

CodePudding user response:

"Now I need a certain condition. It should be only allowed to select one div-attribute per table row and column."

The versatility of jQuery is leveraged by the use of this because it narrows down from many objects (all <td> in <table>) to a single object (<td> the user clicked). The behavior needed is common with radio button groups called "mutual exclusive selection", using .not(this) makes it simple.

In HTML,

  • assign a common class to each <div> (ex. '.col', see Figure I)

  • assign a class to each <div> that corresponds to the value of it's [data-value] (ex. '.c0', see Figure I)

    Figure I

    <div class='col c0' data-value='0'>
    

I did not include the params part in OP since it's beyond the scope of the question (see beginning of this answer). The values are stored in object C and is easily accessible (ex. C.c0).

BTW, I hope that the logic is different with your real code. For example, there is no difference between .c0 2nd row and .c0 1st row.

Details are commented in example below

// Declare object to store [data-value]
let C = {};

// Any click on a .col calls the event handler
$('.col').on('click', function() {
  // Flip .selected on this .col
  $(this).toggleClass('selected');
  // If this .col is flipped to be .selected...
  if ($(this).is('.selected')) {
    //... get this .col [data-value] (0, 1, or 2)...
    let idx = $(this).data('value');
    /*
    ... find all .c0, .c1, or .c2 BUT NOT this .col and
    remove .selected from them...
    */
    $('.c'   idx).not(this).removeClass('selected');
    /*
    ... then find the closest <tr>, then find all .col of
    <tr> BUT NOT this .col and remove .selected from them
    */
    $(this).closest('tr').find('.col')
      .not(this).removeClass('selected');
    // set key 'c0', 'c1', or 'c2' of C to this .col [data-value]
    C['c' idx] = $(this).data('value');
  }
  console.log(C);
});
<table border="1" cellpadding="5" >
  <tr>
    <td><div  data-value="0">aaa</div></td>
    <td><div  data-value="1">bbb</div></td>
    <td><div  data-value="2">ccc</div></td>
  </tr>
  <tr>
    <td><div  data-value="0">aaa</div></td>
    <td><div  data-value="1">bbb</div></td>
    <td><div  data-value="2">ccc</div></td>
  </tr>
  <tr>
    <td><div  data-value="0">aaa</div></td>
    <td><div  data-value="1">bbb</div></td>
    <td><div  data-value="2">ccc</div></td>
  </tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

  • Related